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学习教程

公众号 交流 后台项目 前端项目 SpringCloud版本

## 简介 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项目专属学习路线! 加微信群交流,公众号后台回复「**加群**」即可。 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/.nojekyll ================================================ ================================================ FILE: docs/README.md ================================================ # mall学习教程

公众号 后台项目 前端项目 SpringCloud版本

## 友情提示 > 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项目专属学习路线! 加微信群交流,公众号后台回复「**加群**」即可。 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/_coverpage.md ================================================ ![logo](images/mall.svg) # 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项目 ![](../images/arch_screen_01.png) ### 添加项目依赖 > 在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 ``` ### 项目结构说明 ![](../images/arch_screen_02.png) ### 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) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ 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注解 ![](../images/arch_screen_03.png) ### 运行项目,查看结果 #### 访问Swagger-UI接口文档地址 接口地址:http://localhost:8080/swagger-ui.html ![](../images/arch_screen_04.png) #### 对请求参数已经添加说明 ![](../images/arch_screen_05.png) #### 对返回结果已经添加说明 ![](../images/arch_screen_06.png) ### 直接在在线文档上面进行接口测试 ![](../images/arch_screen_07.png) ![](../images/arch_screen_08.png) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-02](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-02) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/architect/mall_arch_03.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall整合Redis实现缓存功能 > 本文主要讲解mall整合Redis的过程,以短信验证码的存储验证为例。 ## Redis的安装和启动 > Redis是用C语言开发的一个高性能键值对数据库,可用于数据缓存,主要用于处理大量数据的高访问负载。 - 下载Redis,下载地址:https://github.com/MicrosoftArchive/redis/releases ![](../images/arch_screen_09.png) - 下载完后解压到指定目录 ![](../images/arch_screen_10.png) - 在当前地址栏输入cmd后,执行redis的启动命令:redis-server.exe redis.windows.conf ![](../images/arch_screen_11.png) ## 整合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 ,对接口进行测试。 ![](../images/arch_screen_12.png) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-03](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-03) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ 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/ ![](../images/arch_screen_13.png) #### 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 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) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ 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 ![](../images/arch_screen_14.png) ### 未登录前访问接口 ![](../images/arch_screen_15.png) ![](../images/arch_screen_16.png) ### 登录后访问接口 - 进行登录操作:登录帐号test 123456 ![](../images/arch_screen_17.png) ![](../images/arch_screen_18.png) - 点击Authorize按钮,在弹框中输入登录接口中获取到的token信息 ![](../images/arch_screen_19.png) ![](../images/arch_screen_20.png) - 登录后访问获取权限列表接口,发现已经可以正常访问 ![](../images/arch_screen_15.png) ![](../images/arch_screen_21.png) ### 访问需要权限的接口 > 由于test帐号并没有设置任何权限,所以他无法访问具有`pms:brand:read`权限的获取品牌列表接口。 ![](../images/arch_screen_22.png) ![](../images/arch_screen_23.png) ### 改用其他有权限的帐号登录 > 改用admin 123456登录后访问,点击Authorize按钮打开弹框,点击logout登出后再重新输入新token。 `注意`:如果admin帐号密码不对的话,公众号后台回复`体验`来获取。 ![](../images/arch_screen_22.png) ![](../images/arch_screen_24.png) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-04](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-04) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ 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) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ 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) ![](../images/arch_screen_25.png) 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 ![](../images/arch_screen_26.png) 3. 运行bin目录下的elasticsearch.bat启动Elasticsearch ![](../images/arch_screen_27.png) 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) ![](../images/arch_screen_28.png) 5. 运行bin目录下的kibana.bat,启动Kibana的用户界面 ![](../images/arch_screen_29.png) 6. 访问[http://localhost:5601](http://localhost:5601) 即可打开Kibana的用户界面 ![](../images/arch_screen_30.png) ### 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接口可以获得常用的数据操作方法 ![](../images/arch_screen_31.png) ##### 可以使用衍生查询 >在接口中直接指定查询方法名称便可查询,无需进行实现,如商品表中有商品名称、标题和关键字,直接定义以下查询,就可以对这三个字段进行全文搜索。 ```java /** * 搜索查询 * * @param name 商品名称 * @param subTitle 商品标题 * @param keywords 商品关键字 * @param page 分页信息 * @return */ Page findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page); ``` > 在idea中直接会提示对应字段 ![](../images/arch_screen_32.png) ##### 使用@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 delete(@PathVariable Long id) { esProductService.delete(id); return CommonResult.success(null); } @ApiOperation(value = "根据id批量删除商品") @RequestMapping(value = "/delete/batch", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("ids") List ids) { esProductService.delete(ids); return CommonResult.success(null); } @ApiOperation(value = "根据id创建商品") @RequestMapping(value = "/create/{id}", method = RequestMethod.POST) @ResponseBody public CommonResult create(@PathVariable Long id) { EsProduct esProduct = esProductService.create(id); if (esProduct != null) { return CommonResult.success(esProduct); } else { return CommonResult.failed(); } } @ApiOperation(value = "简单搜索") @RequestMapping(value = "/search/simple", method = RequestMethod.GET) @ResponseBody public CommonResult> search(@RequestParam(required = false) String keyword, @RequestParam(required = false, defaultValue = "0") Integer pageNum, @RequestParam(required = false, defaultValue = "5") Integer pageSize) { Page esProductPage = esProductService.search(keyword, pageNum, pageSize); return CommonResult.success(CommonPage.restPage(esProductPage)); } } ``` ## 进行接口测试 ### 将数据库中数据导入到Elasticsearch ![](../images/arch_screen_33.png) ![](../images/arch_screen_34.png) ### 进行商品搜索 ![](../images/arch_screen_35.png) ![](../images/arch_screen_36.png) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-06](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-06) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/architect/mall_arch_08.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall整合Mongodb实现文档操作 > 本文主要讲解mall整合Mongodb的过程,以实现商品浏览记录在Mongodb中的添加、删除、查询为例。 ## 项目使用框架介绍 ### Mongodb > Mongodb是为快速开发互联网Web应用而构建的数据库系统,其数据模型和持久化策略就是为了构建高读/写吞吐量和高自动灾备伸缩性的系统。 #### Mongodb的安装和使用 1. 下载Mongodb安装包,下载地址:[https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-3.2.21-signed.msi](https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-3.2.21-signed.msi) 2. 选择安装路径进行安装 ![](../images/arch_screen_37.png) ![](../images/arch_screen_38.png) 3. 在安装路径下创建data\\db和data\\log两个文件夹 ![](../images/arch_screen_39.png) 4. 在安装路径下创建mongod.cfg配置文件 ``` systemLog: destination: file path: D:\developer\env\MongoDB\data\log\mongod.log storage: dbPath: D:\developer\env\MongoDB\data\db ``` 5. 安装为服务(运行命令需要用管理员权限) ``` D:\developer\env\MongoDB\bin\mongod.exe --config "D:\developer\env\MongoDB\mongod.cfg" --install ``` ![](../images/arch_screen_40.png) 6. 服务相关命令 ``` 启动服务:net start MongoDB 关闭服务:net stop MongoDB 移除服务:D:\developer\env\MongoDB\bin\mongod.exe --remove ``` 7. 下载客户端程序:[https://download.robomongo.org/1.2.1/windows/robo3t-1.2.1-windows-x86_64-3e50a65.zip](https://download.robomongo.org/1.2.1/windows/robo3t-1.2.1-windows-x86_64-3e50a65.zip) 7. 解压到指定目录,打开robo3t.exe并连接到localhost:27017 ![](../images/arch_screen_41.png) ### Spring Data Mongodb > 和Spring Data Elasticsearch类似,Spring Data Mongodb是Spring提供的一种以Spring Data风格来操作数据存储的方式,它可以避免编写大量的样板代码。 #### 常用注解 - @Document:标示映射到Mongodb文档上的领域对象 - @Id:标示某个域为ID域 - @Indexed:标示某个字段为Mongodb的索引字段 #### Sping Data方式的数据操作 ##### 继承MongoRepository接口可以获得常用的数据操作方法 ![](../images/arch_screen_42.png) ##### 可以使用衍生查询 > 在接口中直接指定查询方法名称便可查询,无需进行实现,以下为根据会员id按时间倒序获取浏览记录的例子。 ```java /** * 会员商品浏览历史Repository * Created by macro on 2018/8/3. */ public interface MemberReadHistoryRepository extends MongoRepository { /** * 根据会员id按时间倒序获取浏览记录 * @param memberId 会员id */ List findByMemberIdOrderByCreateTimeDesc(Long memberId); } ``` > 在idea中直接会提示对应字段 ![](../images/arch_screen_43.png) ##### 使用@Query注解可以用Mongodb的JSON查询语句进行查询 ```java @Query("{ 'memberId' : ?0 }") List findByMemberId(Long memberId); ``` ## 整合Mongodb实现文档操作 ### 在pom.xml中添加相关依赖 ```xml org.springframework.boot spring-boot-starter-data-mongodb ``` ### 修改SpringBoot配置文件 > 修改application.yml文件,在spring:data节点下添加Mongodb相关配置。 ```yml mongodb: host: localhost # mongodb的连接地址 port: 27017 # mongodb的连接端口号 database: mall-port # mongodb的连接的数据库 ``` ### 添加会员浏览记录文档对象MemberReadHistory > 文档对象的ID域添加@Id注解,需要检索的字段添加@Indexed注解。 ```java package com.macro.mall.tiny.nosql.mongodb.document; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; /** * 用户商品浏览历史记录 * Created by macro on 2018/8/3. */ @Document public class MemberReadHistory { @Id private String id; @Indexed private Long memberId; private String memberNickname; private String memberIcon; @Indexed private Long productId; private String productName; private String productPic; private String productSubTitle; private String productPrice; private Date createTime; //省略了所有getter和setter方法 } ``` ### 添加MemberReadHistoryRepository接口用于操作Mongodb > 继承MongoRepository接口,这样就拥有了一些基本的Mongodb数据操作方法,同时定义了一个衍生查询方法。 ```java package com.macro.mall.tiny.nosql.mongodb.repository; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import org.springframework.data.mongodb.repository.MongoRepository; import java.util.List; /** * 会员商品浏览历史Repository * Created by macro on 2018/8/3. */ public interface MemberReadHistoryRepository extends MongoRepository { /** * 根据会员id按时间倒序获取浏览记录 * @param memberId 会员id */ List findByMemberIdOrderByCreateTimeDesc(Long memberId); } ``` ### 添加MemberReadHistoryService接口 ```java package com.macro.mall.tiny.service; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import java.util.List; /** * 会员浏览记录管理Service * Created by macro on 2018/8/3. */ public interface MemberReadHistoryService { /** * 生成浏览记录 */ int create(MemberReadHistory memberReadHistory); /** * 批量删除浏览记录 */ int delete(List ids); /** * 获取用户浏览历史记录 */ List list(Long memberId); } ``` ### 添加MemberReadHistoryService接口实现类MemberReadHistoryServiceImpl ```java package com.macro.mall.tiny.service.impl; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import com.macro.mall.tiny.nosql.mongodb.repository.MemberReadHistoryRepository; import com.macro.mall.tiny.service.MemberReadHistoryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * 会员浏览记录管理Service实现类 * Created by macro on 2018/8/3. */ @Service public class MemberReadHistoryServiceImpl implements MemberReadHistoryService { @Autowired private MemberReadHistoryRepository memberReadHistoryRepository; @Override public int create(MemberReadHistory memberReadHistory) { memberReadHistory.setId(null); memberReadHistory.setCreateTime(new Date()); memberReadHistoryRepository.save(memberReadHistory); return 1; } @Override public int delete(List ids) { List deleteList = new ArrayList<>(); for(String id:ids){ MemberReadHistory memberReadHistory = new MemberReadHistory(); memberReadHistory.setId(id); deleteList.add(memberReadHistory); } memberReadHistoryRepository.deleteAll(deleteList); return ids.size(); } @Override public List list(Long memberId) { return memberReadHistoryRepository.findByMemberIdOrderByCreateTimeDesc(memberId); } } ``` ### 添加MemberReadHistoryController定义接口 ```java package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import com.macro.mall.tiny.service.MemberReadHistoryService; 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.*; import java.util.List; /** * 会员商品浏览记录管理Controller * Created by macro on 2018/8/3. */ @Controller @Api(tags = "MemberReadHistoryController", description = "会员商品浏览记录管理") @RequestMapping("/member/readHistory") public class MemberReadHistoryController { @Autowired private MemberReadHistoryService memberReadHistoryService; @ApiOperation("创建浏览记录") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody public CommonResult create(@RequestBody MemberReadHistory memberReadHistory) { int count = memberReadHistoryService.create(memberReadHistory); if (count > 0) { return CommonResult.success(count); } else { return CommonResult.failed(); } } @ApiOperation("删除浏览记录") @RequestMapping(value = "/delete", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("ids") List ids) { int count = memberReadHistoryService.delete(ids); if (count > 0) { return CommonResult.success(count); } else { return CommonResult.failed(); } } @ApiOperation("展示浏览记录") @RequestMapping(value = "/list", method = RequestMethod.GET) @ResponseBody public CommonResult> list(Long memberId) { List memberReadHistoryList = memberReadHistoryService.list(memberId); return CommonResult.success(memberReadHistoryList); } } ``` ## 进行接口测试 ### 添加商品浏览记录到Mongodb ![](../images/arch_screen_44.png) ![](../images/arch_screen_45.png) ### 查询Mongodb中的商品浏览记录 ![](../images/arch_screen_46.png) ![](../images/arch_screen_47.png) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-07](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-07) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/architect/mall_arch_09.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall整合RabbitMQ实现延迟消息 > 本文主要讲解mall整合RabbitMQ实现延迟消息的过程,以发送延迟消息取消超时订单为例。 ## 项目使用框架介绍 ### RabbitMQ > RabbitMQ是一个被广泛使用的开源消息队列。它是轻量级且易于部署的,它能支持多种消息协议。RabbitMQ可以部署在分布式和联合配置中,以满足高规模、高可用性的需求。 #### RabbitMQ的安装和使用 1. 安装Erlang,下载地址:[http://erlang.org/download/otp_win64_21.3.exe](http://erlang.org/download/otp_win64_21.3.exe) ![](../images/arch_screen_53.png) 2. 安装RabbitMQ,下载地址:[https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe](https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe) ![](../images/arch_screen_54.png) 3. 安装完成后,进入RabbitMQ安装目录下的sbin目录 ![](../images/arch_screen_55.png) 4. 在地址栏输入cmd并回车启动命令行,然后输入以下命令启动管理功能: ``` rabbitmq-plugins enable rabbitmq_management ``` ![](../images/arch_screen_56.png) 5. 访问地址查看是否安装成功:[http://localhost:15672/](http://localhost:15672/) ![](../images/arch_screen_57.png) 6. 输入账号密码并登录:guest guest 7. 创建帐号并设置其角色为管理员:mall mall ![](../images/arch_screen_58.png) 8. 创建一个新的虚拟host为:/mall ![](../images/arch_screen_59.png) 9. 点击mall用户进入用户配置页面 ![](../images/arch_screen_60.png) 10. 给mall用户配置该虚拟host的权限 ![](../images/arch_screen_61.png) 11. 至此,RabbitMQ的安装和配置完成。 #### RabbitMQ的消息模型 ![](../images/arch_screen_52.png) 标志 | 中文名 | 英文名| 描述 ----|----|----|---- P |生产者 |Producer |消息的发送者,可以将消息发送到交换机 C |消费者 |Consumer |消息的接收者,从队列中获取消息进行消费 X |交换机 |Exchange |接收生产者发送的消息,并根据路由键发送给指定队列 Q |队列 |Queue |存储从交换机发来的消息 type | 交换机类型 |type |direct表示直接根据路由键(orange/black)发送消息 ### Lombok > Lombok为Java语言添加了非常有趣的附加功能,你可以不用再为实体类手写getter,setter等方法,通过一个注解即可拥有。 注意:需要安装idea的Lombok插件,并在项目中的pom文件中添加依赖。 ![](../images/arch_screen_48.png) ## 业务场景说明 > 用于解决用户下单以后,订单超时如何取消订单的问题。 - 用户进行下单操作(会有锁定商品库存、使用优惠券、积分一系列的操作); - 生成订单,获取订单的id; - 获取到设置的订单超时时间(假设设置的为60分钟不支付取消订单); - 按订单超时时间发送一个延迟消息给RabbitMQ,让它在订单超时后触发取消订单的操作; - 如果用户没有支付,进行取消订单操作(释放锁定商品库存、返还优惠券、返回积分一系列操作)。 ## 整合RabbitMQ实现延迟消息 ### 在pom.xml中添加相关依赖 ```xml org.springframework.boot spring-boot-starter-amqp org.projectlombok lombok true ``` ### 修改SpringBoot配置文件 > 修改application.yml文件,在spring节点下添加RabbitMQ相关配置。 ```yml rabbitmq: host: localhost # rabbitmq的连接地址 port: 5672 # rabbitmq的连接端口号 virtual-host: /mall # rabbitmq的虚拟host username: mall # rabbitmq的用户名 password: mall # rabbitmq的密码 publisher-confirms: true #如果对异步消息需要回调必须设置为true ``` ### 添加消息队列的枚举配置类QueueEnum > 用于延迟消息队列及处理取消订单消息队列的常量定义,包括交换机名称、队列名称、路由键名称。 ```java package com.macro.mall.tiny.dto; import lombok.Getter; /** * 消息队列枚举配置 * Created by macro on 2018/9/14. */ @Getter public enum QueueEnum { /** * 消息通知队列 */ QUEUE_ORDER_CANCEL("mall.order.direct", "mall.order.cancel", "mall.order.cancel"), /** * 消息通知ttl队列 */ QUEUE_TTL_ORDER_CANCEL("mall.order.direct.ttl", "mall.order.cancel.ttl", "mall.order.cancel.ttl"); /** * 交换名称 */ private String exchange; /** * 队列名称 */ private String name; /** * 路由键 */ private String routeKey; QueueEnum(String exchange, String name, String routeKey) { this.exchange = exchange; this.name = name; this.routeKey = routeKey; } } ``` ### 添加RabbitMQ的配置 > 用于配置交换机、队列及队列与交换机的绑定关系。 ```java package com.macro.mall.tiny.config; import com.macro.mall.tiny.dto.QueueEnum; import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 消息队列配置 * Created by macro on 2018/9/14. */ @Configuration public class RabbitMqConfig { /** * 订单消息实际消费队列所绑定的交换机 */ @Bean DirectExchange orderDirect() { return (DirectExchange) ExchangeBuilder .directExchange(QueueEnum.QUEUE_ORDER_CANCEL.getExchange()) .durable(true) .build(); } /** * 订单延迟队列队列所绑定的交换机 */ @Bean DirectExchange orderTtlDirect() { return (DirectExchange) ExchangeBuilder .directExchange(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange()) .durable(true) .build(); } /** * 订单实际消费队列 */ @Bean public Queue orderQueue() { return new Queue(QueueEnum.QUEUE_ORDER_CANCEL.getName()); } /** * 订单延迟队列(死信队列) */ @Bean public Queue orderTtlQueue() { return QueueBuilder .durable(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getName()) .withArgument("x-dead-letter-exchange", QueueEnum.QUEUE_ORDER_CANCEL.getExchange())//到期后转发的交换机 .withArgument("x-dead-letter-routing-key", QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey())//到期后转发的路由键 .build(); } /** * 将订单队列绑定到交换机 */ @Bean Binding orderBinding(DirectExchange orderDirect,Queue orderQueue){ return BindingBuilder .bind(orderQueue) .to(orderDirect) .with(QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey()); } /** * 将订单延迟队列绑定到交换机 */ @Bean Binding orderTtlBinding(DirectExchange orderTtlDirect,Queue orderTtlQueue){ return BindingBuilder .bind(orderTtlQueue) .to(orderTtlDirect) .with(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey()); } } ``` #### 在RabbitMQ管理页面可以看到以下交换机和队列 ![](../images/arch_screen_62.png) ![](../images/arch_screen_63.png) ![](../images/arch_screen_64.png) ![](../images/arch_screen_65.png) #### 交换机及队列说明 - mall.order.direct(取消订单消息队列所绑定的交换机):绑定的队列为mall.order.cancel,一旦有消息以mall.order.cancel为路由键发过来,会发送到此队列。 - mall.order.direct.ttl(订单延迟消息队列所绑定的交换机):绑定的队列为mall.order.cancel.ttl,一旦有消息以mall.order.cancel.ttl为路由键发送过来,会转发到此队列,并在此队列保存一定时间,等到超时后会自动将消息发送到mall.order.cancel(取消订单消息消费队列)。 ### 添加延迟消息的发送者CancelOrderSender > 用于向订单延迟消息队列(mall.order.cancel.ttl)里发送消息。 ```java package com.macro.mall.tiny.component; import com.macro.mall.tiny.dto.QueueEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.AmqpException; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessagePostProcessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 取消订单消息的发出者 * Created by macro on 2018/9/14. */ @Component public class CancelOrderSender { private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderSender.class); @Autowired private AmqpTemplate amqpTemplate; public void sendMessage(Long orderId,final long delayTimes){ //给延迟队列发送消息 amqpTemplate.convertAndSend(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange(), QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey(), orderId, new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { //给消息设置延迟毫秒值 message.getMessageProperties().setExpiration(String.valueOf(delayTimes)); return message; } }); LOGGER.info("send delay message orderId:{}",orderId); } } ``` ### 添加取消订单消息的接收者CancelOrderReceiver > 用于从取消订单的消息队列(mall.order.cancel)里接收消息。 ```java package com.macro.mall.tiny.component; import com.macro.mall.tiny.service.OmsPortalOrderService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 取消订单消息的处理者 * Created by macro on 2018/9/14. */ @Component @RabbitListener(queues = "mall.order.cancel") public class CancelOrderReceiver { private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderReceiver.class); @Autowired private OmsPortalOrderService portalOrderService; @RabbitHandler public void handle(Long orderId){ LOGGER.info("receive delay message orderId:{}",orderId); portalOrderService.cancelOrder(orderId); } } ``` ### 添加OmsPortalOrderService接口 ```java package com.macro.mall.tiny.service; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.dto.OrderParam; import org.springframework.transaction.annotation.Transactional; /** * 前台订单管理Service * Created by macro on 2018/8/30. */ public interface OmsPortalOrderService { /** * 根据提交信息生成订单 */ @Transactional CommonResult generateOrder(OrderParam orderParam); /** * 取消单个超时订单 */ @Transactional void cancelOrder(Long orderId); } ``` ### 添加OmsPortalOrderService的实现类OmsPortalOrderServiceImpl ```java package com.macro.mall.tiny.service.impl; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.component.CancelOrderSender; import com.macro.mall.tiny.dto.OrderParam; import com.macro.mall.tiny.service.OmsPortalOrderService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * 前台订单管理Service * Created by macro on 2018/8/30. */ @Service public class OmsPortalOrderServiceImpl implements OmsPortalOrderService { private static Logger LOGGER = LoggerFactory.getLogger(OmsPortalOrderServiceImpl.class); @Autowired private CancelOrderSender cancelOrderSender; @Override public CommonResult generateOrder(OrderParam orderParam) { //todo 执行一系类下单操作,具体参考mall项目 LOGGER.info("process generateOrder"); //下单完成后开启一个延迟消息,用于当用户没有付款时取消订单(orderId应该在下单后生成) sendDelayMessageCancelOrder(11L); return CommonResult.success(null, "下单成功"); } @Override public void cancelOrder(Long orderId) { //todo 执行一系类取消订单操作,具体参考mall项目 LOGGER.info("process cancelOrder orderId:{}",orderId); } private void sendDelayMessageCancelOrder(Long orderId) { //获取订单超时时间,假设为60分钟 long delayTimes = 30 * 1000; //发送延迟消息 cancelOrderSender.sendMessage(orderId, delayTimes); } } ``` ### 添加OmsPortalOrderController定义接口 ```java package com.macro.mall.tiny.controller; import com.macro.mall.tiny.dto.OrderParam; import com.macro.mall.tiny.service.OmsPortalOrderService; 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.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; /** * 订单管理Controller * Created by macro on 2018/8/30. */ @Controller @Api(tags = "OmsPortalOrderController", description = "订单管理") @RequestMapping("/order") public class OmsPortalOrderController { @Autowired private OmsPortalOrderService portalOrderService; @ApiOperation("根据购物车信息生成订单") @RequestMapping(value = "/generateOrder", method = RequestMethod.POST) @ResponseBody public Object generateOrder(@RequestBody OrderParam orderParam) { return portalOrderService.generateOrder(orderParam); } } ``` ## 进行接口测试 ### 调用下单接口 注意:已经将延迟消息时间设置为30秒 ![](../images/arch_screen_49.png) ![](../images/arch_screen_50.png) ![](../images/arch_screen_51.png) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-08](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-08) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/architect/mall_arch_10.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall整合OSS实现文件上传 > 本文主要讲解mall整合OSS实现文件上传的过程,采用的是服务端签名后前端直传的方式。 ## OSS > 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。OSS可用于图片、音视频、日志等海量文件的存储。各种终端设备、Web网站程序、移动应用可以直接向OSS写入或读取数据。 ### OSS中的相关概念 - Endpoint:访问域名,通过该域名可以访问OSS服务的API,进行文件上传、下载等操作。 - Bucket:存储空间,是存储对象的容器,所有存储对象都必须隶属于某个存储空间。 - Object:对象,对象是 OSS 存储数据的基本单元,也被称为 OSS 的文件。 - AccessKey:访问密钥,指的是访问身份验证中用到的 AccessKeyId 和 AccessKeySecret。 ### OSS的相关设置 #### 开通OSS服务 - 登录阿里云官网; - 将鼠标移至产品标签页,单击对象存储 OSS,打开OSS 产品详情页面; - 在OSS产品详情页,单击立即开通。 #### 创建存储空间 - 点击网页右上角控制台按钮进入控制台 ![](../images/arch_screen_77.png) - 选择我的云产品中的对象存储OSS ![](../images/arch_screen_78.png) - 点击左侧存储空间的加号新建存储空间 ![](../images/arch_screen_79.png) - 新建存储空间并设置读写权限为公共读 ![](../images/arch_screen_80.png) #### 跨域资源共享(CORS)的设置 > 由于浏览器处于安全考虑,不允许跨域资源访问,所以我们要设置OSS的跨域资源共享。 - 选择一个存储空间,打开其基础设置 ![](../images/arch_screen_81.png) - 点击跨越设置的设置按钮 ![](../images/arch_screen_82.png) - 点击创建规则 ![](../images/arch_screen_83.png) - 进行跨域规则设置 ![](../images/arch_screen_84.png) ### 服务端签名后前端直传的相关说明 #### 流程示例图 ![](../images/arch_screen_85.png) #### 流程介绍 1. Web前端请求应用服务器,获取上传所需参数(如OSS的accessKeyId、policy、callback等参数) 2. 应用服务器返回相关参数 3. Web前端直接向OSS服务发起上传文件请求 4. 等上传完成后OSS服务会回调应用服务器的回调接口 5. 应用服务器返回响应给OSS服务 6. OSS服务将应用服务器回调接口的内容返回给Web前端 ## 整合OSS实现文件上传 ### 在pom.xml中添加相关依赖 ```xml com.aliyun.oss aliyun-sdk-oss 2.5.0 ``` ### 修改SpringBoot配置文件 > 修改application.yml文件,添加OSS相关配置。 注意:endpoint、accessKeyId、accessKeySecret、bucketName、callback、prefix都要改为你自己帐号OSS相关的,callback需要是公网可以访问的地址。 ```yml # OSS相关配置信息 aliyun: oss: endpoint: oss-cn-shenzhen.aliyuncs.com # oss对外服务的访问域名 accessKeyId: test # 访问身份验证中用到用户标识 accessKeySecret: test # 用户用于加密签名字符串和oss用来验证签名字符串的密钥 bucketName: macro-oss # oss的存储空间 policy: expire: 300 # 签名有效期(S) maxSize: 10 # 上传文件大小(M) callback: http://localhost:8080/aliyun/oss/callback # 文件上传成功后的回调地址 dir: prefix: mall/images/ # 上传文件夹路径前缀 ``` ### 添加OSS的相关Java配置 > 用于配置OSS的连接客户端OSSClient。 ```java package com.macro.mall.tiny.config; import com.aliyun.oss.OSSClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Created by macro on 2018/5/17. */ @Configuration public class OssConfig { @Value("${aliyun.oss.endpoint}") private String ALIYUN_OSS_ENDPOINT; @Value("${aliyun.oss.accessKeyId}") private String ALIYUN_OSS_ACCESSKEYID; @Value("${aliyun.oss.accessKeySecret}") private String ALIYUN_OSS_ACCESSKEYSECRET; @Bean public OSSClient ossClient(){ return new OSSClient(ALIYUN_OSS_ENDPOINT,ALIYUN_OSS_ACCESSKEYID,ALIYUN_OSS_ACCESSKEYSECRET); } } ``` ### 添加OSS上传策略封装对象OssPolicyResult > 前端直接上传文件时所需参数,从后端返回过来。 ```java package com.macro.mall.tiny.dto; import io.swagger.annotations.ApiModelProperty; /** * 获取OSS上传文件授权返回结果 * Created by macro on 2018/5/17. */ public class OssPolicyResult { @ApiModelProperty("访问身份验证中用到用户标识") private String accessKeyId; @ApiModelProperty("用户表单上传的策略,经过base64编码过的字符串") private String policy; @ApiModelProperty("对policy签名后的字符串") private String signature; @ApiModelProperty("上传文件夹路径前缀") private String dir; @ApiModelProperty("oss对外服务的访问域名") private String host; @ApiModelProperty("上传成功后的回调设置") private String callback; //省略了所有getter,setter方法 } ``` ### 添加OSS上传成功后的回调参数对象OssCallbackParam > 当OSS上传成功后,会根据该配置参数来回调对应接口。 ```java package com.macro.mall.tiny.dto; import io.swagger.annotations.ApiModelProperty; /** * oss上传成功后的回调参数 * Created by macro on 2018/5/17. */ public class OssCallbackParam { @ApiModelProperty("请求的回调地址") private String callbackUrl; @ApiModelProperty("回调是传入request中的参数") private String callbackBody; @ApiModelProperty("回调时传入参数的格式,比如表单提交形式") private String callbackBodyType; //省略了所有getter,setter方法 } ``` ### OSS上传成功后的回调结果对象OssCallbackResult > 回调接口中返回的数据对象,封装了上传文件的信息。 ```java package com.macro.mall.tiny.dto; import io.swagger.annotations.ApiModelProperty; /** * oss上传文件的回调结果 * Created by macro on 2018/5/17. */ public class OssCallbackResult { @ApiModelProperty("文件名称") private String filename; @ApiModelProperty("文件大小") private String size; @ApiModelProperty("文件的mimeType") private String mimeType; @ApiModelProperty("图片文件的宽") private String width; @ApiModelProperty("图片文件的高") private String height; //省略了所有getter,setter方法 } ``` ### 添加OSS业务接口OssService ```java package com.macro.mall.tiny.service; import com.macro.mall.tiny.dto.OssCallbackResult; import com.macro.mall.tiny.dto.OssPolicyResult; import javax.servlet.http.HttpServletRequest; /** * oss上传管理Service * Created by macro on 2018/5/17. */ public interface OssService { /** * oss上传策略生成 */ OssPolicyResult policy(); /** * oss上传成功回调 */ OssCallbackResult callback(HttpServletRequest request); } ``` ### 添加OSS业务接口OssService的实现类OssServiceImpl ```java package com.macro.mall.tiny.service.impl; import cn.hutool.json.JSONUtil; import com.aliyun.oss.OSSClient; import com.aliyun.oss.common.utils.BinaryUtil; import com.aliyun.oss.model.MatchMode; import com.aliyun.oss.model.PolicyConditions; import com.macro.mall.tiny.dto.OssCallbackParam; import com.macro.mall.tiny.dto.OssCallbackResult; import com.macro.mall.tiny.dto.OssPolicyResult; import com.macro.mall.tiny.service.OssService; 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.stereotype.Service; import javax.servlet.http.HttpServletRequest; import java.text.SimpleDateFormat; import java.util.Date; /** * oss上传管理Service实现类 * Created by macro on 2018/5/17. */ @Service public class OssServiceImpl implements OssService { private static final Logger LOGGER = LoggerFactory.getLogger(OssServiceImpl.class); @Value("${aliyun.oss.policy.expire}") private int ALIYUN_OSS_EXPIRE; @Value("${aliyun.oss.maxSize}") private int ALIYUN_OSS_MAX_SIZE; @Value("${aliyun.oss.callback}") private String ALIYUN_OSS_CALLBACK; @Value("${aliyun.oss.bucketName}") private String ALIYUN_OSS_BUCKET_NAME; @Value("${aliyun.oss.endpoint}") private String ALIYUN_OSS_ENDPOINT; @Value("${aliyun.oss.dir.prefix}") private String ALIYUN_OSS_DIR_PREFIX; @Autowired private OSSClient ossClient; /** * 签名生成 */ @Override public OssPolicyResult policy() { OssPolicyResult result = new OssPolicyResult(); // 存储目录 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); String dir = ALIYUN_OSS_DIR_PREFIX+sdf.format(new Date()); // 签名有效期 long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000; Date expiration = new Date(expireEndTime); // 文件大小 long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 * 1024; // 回调 OssCallbackParam callback = new OssCallbackParam(); callback.setCallbackUrl(ALIYUN_OSS_CALLBACK); callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}"); callback.setCallbackBodyType("application/x-www-form-urlencoded"); // 提交节点 String action = "http://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT; try { PolicyConditions policyConds = new PolicyConditions(); policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize); policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir); String postPolicy = ossClient.generatePostPolicy(expiration, policyConds); byte[] binaryData = postPolicy.getBytes("utf-8"); String policy = BinaryUtil.toBase64String(binaryData); String signature = ossClient.calculatePostSignature(postPolicy); String callbackData = BinaryUtil.toBase64String(JSONUtil.parse(callback).toString().getBytes("utf-8")); // 返回结果 result.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId()); result.setPolicy(policy); result.setSignature(signature); result.setDir(dir); result.setCallback(callbackData); result.setHost(action); } catch (Exception e) { LOGGER.error("签名生成失败", e); } return result; } @Override public OssCallbackResult callback(HttpServletRequest request) { OssCallbackResult result= new OssCallbackResult(); String filename = request.getParameter("filename"); filename = "http://".concat(ALIYUN_OSS_BUCKET_NAME).concat(".").concat(ALIYUN_OSS_ENDPOINT).concat("/").concat(filename); result.setFilename(filename); result.setSize(request.getParameter("size")); result.setMimeType(request.getParameter("mimeType")); result.setWidth(request.getParameter("width")); result.setHeight(request.getParameter("height")); return result; } } ``` ### 添加OssController定义接口 ```java package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.dto.OssCallbackResult; import com.macro.mall.tiny.dto.OssPolicyResult; import com.macro.mall.tiny.service.impl.OssServiceImpl; 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.ResponseBody; import javax.servlet.http.HttpServletRequest; /** * Oss相关操作接口 * Created by macro on 2018/4/26. */ @Controller @Api(tags = "OssController", description = "Oss管理") @RequestMapping("/aliyun/oss") public class OssController { @Autowired private OssServiceImpl ossService; @ApiOperation(value = "oss上传签名生成") @RequestMapping(value = "/policy", method = RequestMethod.GET) @ResponseBody public CommonResult policy() { OssPolicyResult result = ossService.policy(); return CommonResult.success(result); } @ApiOperation(value = "oss上传成功回调") @RequestMapping(value = "callback", method = RequestMethod.POST) @ResponseBody public CommonResult callback(HttpServletRequest request) { OssCallbackResult ossCallbackResult = ossService.callback(request); return CommonResult.success(ossCallbackResult); } } ``` ## 进行接口测试 ### 测试获取上传策略的接口 ![](../images/arch_screen_66.png) ![](../images/arch_screen_67.png) ![](../images/arch_screen_68.png) ### 启动mall-admin-web前端项目来测试上传接口 - 如何启动前端项目,具体参考该项目的readme文档:[https://github.com/macrozheng/mall-admin-web](https://github.com/macrozheng/mall-admin-web) - 点击添加商品品牌的上传按钮进行测试 ![](../images/arch_screen_69.png) - 会调用两次请求,第一次访问本地接口获取上传的策略 ![](../images/arch_screen_70.png) ![](../images/arch_screen_71.png) - 第二次调用oss服务 的接口进行文件上传 ![](../images/arch_screen_72.png) ![](../images/arch_screen_73.png) - 可以看到上面接口调用并没有传入回调参数callback,所以接口返回了204 no content,这次我们传入回调参数callback试试,可以发现oss服务回调了我们自己定义的回调接口,并返回了相应结果。 ![](../images/arch_screen_74.png) ![](../images/arch_screen_75.png) ![](../images/arch_screen_76.png) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-09](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-09) ## 参考资料 - 开通OSS服务:[https://help.aliyun.com/document_detail/31884.html?spm=a2c4g.11186623.6.566.74b87eaebrfQno](https://help.aliyun.com/document_detail/31884.html?spm=a2c4g.11186623.6.566.74b87eaebrfQno) - 创建存储空间:[https://help.aliyun.com/document_detail/31885.html?spm=a2c4g.11186623.6.567.496228bcVZUZqB](https://help.aliyun.com/document_detail/31885.html?spm=a2c4g.11186623.6.567.496228bcVZUZqB) - 跨域资源共享(CORS):[https://help.aliyun.com/document_detail/31928.html?spm=5176.11065259.1996646101.searchclickresult.4d1a5607Pf3e9i](https://help.aliyun.com/document_detail/31928.html?spm=5176.11065259.1996646101.searchclickresult.4d1a5607Pf3e9i) - 服务端签名直传并设置上传回调:[https://help.aliyun.com/document_detail/31927.html?spm=a2c4g.11186623.6.1268.2c256506mNqV1t](https://help.aliyun.com/document_detail/31927.html?spm=a2c4g.11186623.6.1268.2c256506mNqV1t) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/admin.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Boot Admin:微服务应用监控 > Spring Boot Admin 可以对SpringBoot应用的各项指标进行监控,可以作为微服务架构中的监控中心来使用,本文将对其用法进行详细介绍。 ## Spring Boot Admin 简介 SpringBoot应用可以通过Actuator来暴露应用运行过程中的各项指标,Spring Boot Admin通过这些指标来监控SpringBoot应用,然后通过图形化界面呈现出来。Spring Boot Admin不仅可以监控单体应用,还可以和Spring Cloud的注册中心相结合来监控微服务应用。 Spring Boot Admin 可以提供应用的以下监控信息: - 监控应用运行过程中的概览信息; - 度量指标信息,比如JVM、Tomcat及进程信息; - 环境变量信息,比如系统属性、系统环境变量以及应用配置信息; - 查看所有创建的Bean信息; - 查看应用中的所有配置信息; - 查看应用运行日志信息; - 查看JVM信息; - 查看可以访问的Web端点; - 查看HTTP跟踪信息。 ## 创建admin-server模块 > 这里我们创建一个admin-server模块来作为监控中心演示其功能。 - 在pom.xml中添加相关依赖: ```xml org.springframework.boot spring-boot-starter-web de.codecentric spring-boot-admin-starter-server ``` - 在application.yml中进行配置: ```yaml spring: application: name: admin-server server: port: 9301 ``` - 在启动类上添加@EnableAdminServer来启用admin-server功能: ```java @EnableAdminServer @SpringBootApplication public class AdminServerApplication { public static void main(String[] args) { SpringApplication.run(AdminServerApplication.class, args); } } ``` ## 创建admin-client模块 > 这里我们创建一个admin-client模块作为客户端注册到admin-server。 - 在pom.xml中添加相关依赖: ```xml org.springframework.boot spring-boot-starter-web de.codecentric spring-boot-admin-starter-client ``` - 在application.yml中进行配置: ```yaml spring: application: name: admin-client boot: admin: client: url: http://localhost:9301 #配置admin-server地址 server: port: 9305 management: endpoints: web: exposure: include: '*' endpoint: health: show-details: always logging: file: admin-client.log #添加开启admin的日志监控 ``` - 启动admin-server和admin-client服务。 ## 监控信息演示 - 访问如下地址打开Spring Boot Admin的主页:http://localhost:9301 ![](../images/springcloud_admin_01.png) - 点击wallboard按钮,选择admin-client查看监控信息; - 监控信息概览; ![](../images/springcloud_admin_02.png) - 度量指标信息,比如JVM、Tomcat及进程信息; ![](../images/springcloud_admin_03.png) - 环境变量信息,比如系统属性、系统环境变量以及应用配置信息; ![](../images/springcloud_admin_04.png) - 查看所有创建的Bean信息; ![](../images/springcloud_admin_05.png) - 查看应用中的所有配置信息; ![](../images/springcloud_admin_06.png) - 查看日志信息,需要添加以下配置才能开启; ```yaml logging: file: admin-client.log #添加开启admin的日志监控 ``` ![](../images/springcloud_admin_07.png) - 查看JVM信息; ![](../images/springcloud_admin_08.png) - 查看可以访问的Web端点; ![](../images/springcloud_admin_09.png) - 查看HTTP跟踪信息; ![](../images/springcloud_admin_10.png) ## 结合注册中心使用 > Spring Boot Admin结合Spring Cloud 注册中心使用,只需将admin-server和注册中心整合即可,admin-server 会自动从注册中心获取服务列表,然后挨个获取监控信息。这里以Eureka注册中心为例来介绍下该功能。 ### 修改admin-server - 在pom.xml中添加相关依赖: ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client ``` - 在application-eureka.yml中进行配置,只需添加注册中心配置即可: ```yaml spring: application: name: admin-server server: port: 9301 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8001/eureka/ ``` - 在启动类上添加@EnableDiscoveryClient来启用服务注册功能: ```java @EnableDiscoveryClient @EnableAdminServer @SpringBootApplication public class AdminServerApplication { public static void main(String[] args) { SpringApplication.run(AdminServerApplication.class, args); } } ``` ### 修改admin-client - 在pom.xml中添加相关依赖: ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client ``` - 在application-eureka.yml中进行配置,删除原来的admin-server地址配置,添加注册中心配置即可: ```yaml spring: application: name: admin-client server: port: 9305 management: endpoints: web: exposure: include: '*' endpoint: health: show-details: always logging: file: admin-client.log #添加开启admin的日志监控 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8001/eureka/ ``` - 在启动类上添加@EnableDiscoveryClient来启用服务注册功能: ```java @EnableDiscoveryClient @SpringBootApplication public class AdminClientApplication { public static void main(String[] args) { SpringApplication.run(AdminClientApplication.class, args); } } ``` ### 功能演示 - 启动eureka-server,使用application-eureka.yml配置启动admin-server,admin-client; - 查看注册中心发现服务均已注册:http://localhost:8001/ ![](../images/springcloud_admin_11.png) - 查看Spring Boot Admin 主页发现可以看到服务信息:http://localhost:9301 ![](../images/springcloud_admin_12.png) ## 添加登录认证 > 我们可以通过给admin-server添加Spring Security支持来获得登录认证功能。 ### 创建admin-security-server模块 - 在pom.xml中添加相关依赖: ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client de.codecentric spring-boot-admin-starter-server 2.1.5 org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-web ``` - 在application.yml中进行配置,配置登录用户名和密码,忽略admin-security-server的监控信息: ```yaml spring: application: name: admin-security-server security: # 配置登录用户名和密码 user: name: macro password: 123456 boot: # 不显示admin-security-server的监控信息 admin: discovery: ignored-services: ${spring.application.name} server: port: 9301 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8001/eureka/ ``` - 对SpringSecurity进行配置,以便admin-client可以注册: ```java /** * Created by macro on 2019/9/30. */ @Configuration public class SecuritySecureConfig extends WebSecurityConfigurerAdapter { private final String adminContextPath; public SecuritySecureConfig(AdminServerProperties adminServerProperties) { this.adminContextPath = adminServerProperties.getContextPath(); } @Override protected void configure(HttpSecurity http) throws Exception { SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler(); successHandler.setTargetUrlParameter("redirectTo"); successHandler.setDefaultTargetUrl(adminContextPath + "/"); http.authorizeRequests() //1.配置所有静态资源和登录页可以公开访问 .antMatchers(adminContextPath + "/assets/**").permitAll() .antMatchers(adminContextPath + "/login").permitAll() .anyRequest().authenticated() .and() //2.配置登录和登出路径 .formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and() .logout().logoutUrl(adminContextPath + "/logout").and() //3.开启http basic支持,admin-client注册时需要使用 .httpBasic().and() .csrf() //4.开启基于cookie的csrf保护 .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) //5.忽略这些路径的csrf保护以便admin-client注册 .ignoringAntMatchers( adminContextPath + "/instances", adminContextPath + "/actuator/**" ); } } ``` - 修改启动类,开启AdminServer及注册发现功能: ```java @EnableDiscoveryClient @EnableAdminServer @SpringBootApplication public class AdminSecurityServerApplication { public static void main(String[] args) { SpringApplication.run(AdminSecurityServerApplication.class, args); } } ``` - 启动eureka-server,admin-security-server,访问Spring Boot Admin 主页发现需要登录才能访问:http://localhost:9301 ![](../images/springcloud_admin_13.png) ## 使用到的模块 ```lua springcloud-learning ├── eureka-server -- eureka注册中心 ├── admin-server -- admin监控中心服务 ├── admin-client -- admin监控中心监控的应用服务 └── admin-security-server -- 带登录认证的admin监控中心服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/bus.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Bus:消息总线 > Spring Cloud Bus 使用轻量级的消息代理来连接微服务架构中的各个服务,可以将其用于广播状态更改(例如配置中心配置更改)或其他管理指令,本文将对其用法进行详细介绍。 ## Spring Cloud Bus 简介 我们通常会使用消息代理来构建一个主题,然后把微服务架构中的所有服务都连接到这个主题上去,当我们向该主题发送消息时,所有订阅该主题的服务都会收到消息并进行消费。使用 Spring Cloud Bus 可以方便地构建起这套机制,所以 Spring Cloud Bus 又被称为消息总线。Spring Cloud Bus 配合 Spring Cloud Config 使用可以实现配置的动态刷新。目前 Spring Cloud Bus 支持两种消息代理:RabbitMQ 和 Kafka,下面以 RabbitMQ 为例来演示下使用Spring Cloud Bus 动态刷新配置的功能。 ## RabbitMQ的安装 - 安装Erlang,下载地址:[http://erlang.org/download/otp_win64_21.3.exe](http://erlang.org/download/otp_win64_21.3.exe) ![](../images/arch_screen_53.png) - 安装RabbitMQ,下载地址:[https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe](https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe) ![](../images/arch_screen_54.png) - 安装完成后,进入RabbitMQ安装目录下的sbin目录: ![](../images/arch_screen_55.png) - 在地址栏输入cmd并回车启动命令行,然后输入以下命令启动管理功能: ``` rabbitmq-plugins enable rabbitmq_management ``` ![](../images/arch_screen_56.png) - 访问地址查看是否安装成功:[http://localhost:15672/](http://localhost:15672/) ![](../images/arch_screen_57.png) - 输入账号密码并登录:guest guest ## 动态刷新配置 > 使用 Spring Cloud Bus 动态刷新配置需要配合 Spring Cloud Config 一起使用,我们使用[上一节](https://mp.weixin.qq.com/s/xVsKrGeRInn3fwNWrDF-CQ)中的config-server、config-client模块来演示下该功能。 ### 给config-server添加消息总线支持 - 在pom.xml中添加相关依赖: ```xml org.springframework.cloud spring-cloud-starter-bus-amqp org.springframework.boot spring-boot-starter-actuator ``` - 添加配置文件application-amqp.yml,主要是添加了RabbitMQ的配置及暴露了刷新配置的Actuator端点; ```yaml server: port: 8904 spring: application: name: config-server cloud: config: server: git: uri: https://gitee.com/macrozheng/springcloud-config.git username: macro password: 123456 clone-on-start: true # 开启启动时直接从git获取配置 rabbitmq: #rabbitmq相关配置 host: localhost port: 5672 username: guest password: guest eureka: client: service-url: defaultZone: http://localhost:8001/eureka/ management: endpoints: #暴露bus刷新配置的端点 web: exposure: include: 'bus-refresh' ``` ### 给config-client添加消息总线支持 - 在pom.xml中添加相关依赖: ```xml org.springframework.cloud spring-cloud-starter-bus-amqp ``` - 添加配置文件bootstrap-amqp1.yml及bootstrap-amqp2.yml用于启动两个不同的config-client,两个配置文件只有端口号不同; ```yaml server: port: 9004 spring: application: name: config-client cloud: config: profile: dev #启用环境名称 label: dev #分支名称 name: config #配置文件名称 discovery: enabled: true service-id: config-server rabbitmq: #rabbitmq相关配置 host: localhost port: 5672 username: guest password: guest eureka: client: service-url: defaultZone: http://localhost:8001/eureka/ management: endpoints: web: exposure: include: 'refresh' ``` ### 动态刷新配置演示 - 我们先启动相关服务,启动eureka-server,以application-amqp.yml为配置启动config-server,以bootstrap-amqp1.yml为配置启动config-client,以bootstrap-amqp2.yml为配置再启动一个config-client,启动后注册中心显示如下: ![](../images/springcloud_config_08.png) - 启动所有服务后,我们登录RabbitMQ的控制台可以发现Spring Cloud Bus 创建了一个叫springCloudBus的交换机及三个以 springCloudBus.anonymous开头的队列: ![](../images/springcloud_config_10.png) ![](../images/springcloud_config_11.png) - 我们先修改Git仓库中dev分支下的config-dev.yml配置文件: ```yaml # 修改前信息 config: info: "config info for dev(dev)" # 修改后信息 config: info: "update config info for dev(dev)" ``` - 调用注册中心的接口刷新所有配置:[http://localhost:8904/actuator/bus-refresh](http://localhost:8904/actuator/bus-refresh) ![](../images/springcloud_config_09.png) - 刷新后再分别调用[http://localhost:9004/configInfo](http://localhost:9004/configInfo) 和 [http://localhost:9005/configInfo](http://localhost:9005/configInfo) 获取配置信息,发现都已经刷新了; ``` update config info for dev(dev) ``` - 如果只需要刷新指定实例的配置可以使用以下格式进行刷新:http://localhost:8904/actuator/bus-refresh/{destination} ,我们这里以刷新运行在9004端口上的config-client为例[http://localhost:8904/actuator/bus-refresh/config-client:9004](http://localhost:8904/actuator/bus-refresh/config-client:9004)。 ## 配合WebHooks使用 WebHooks相当于是一个钩子函数,我们可以配置当向Git仓库push代码时触发这个钩子函数,这里以Gitee为例来介绍下其使用方式,这里当我们向配置仓库push代码时就会自动刷新服务配置了。 ![](../images/springcloud_config_12.png) ## 使用到的模块 ```lua springcloud-learning ├── eureka-server -- eureka注册中心 ├── config-server -- 配置中心服务 └── config-client -- 获取配置的客户端服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/config.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Config:外部集中化配置管理 > Spring Cloud Config 可以为微服务架构中的应用提供集中化的外部配置支持,它分为服务端和客户端两个部分,本文将对其用法进行详细介绍。 ## Spring Cloud Config 简介 Spring Cloud Config 分为服务端和客户端两个部分。服务端被称为分布式配置中心,它是个独立的应用,可以从配置仓库获取配置信息并提供给客户端使用。客户端可以通过配置中心来获取配置信息,在启动时加载配置。Spring Cloud Config 的配置中心默认采用Git来存储配置信息,所以天然就支持配置信息的版本管理,并且可以使用Git客户端来方便地管理和访问配置信息。 ## 在Git仓库中准备配置信息 > 由于Spring Cloud Config 需要一个存储配置信息的Git仓库,这里我们先在Git仓库中添加好配置文件再演示其功能,Git仓库地址为:[https://gitee.com/macrozheng/springcloud-config](https://gitee.com/macrozheng/springcloud-config)。 ### 配置仓库目录结构 ![](../images/springcloud_config_01.png) ### master分支下的配置信息 - config-dev.yml: ```yaml config: info: "config info for dev(master)" ``` - config-test.yml: ```yaml config: info: "config info for test(master)" ``` - config-prod.yml: ```yaml config: info: "config info for prod(master)" ``` ### dev分支下的配置信息 - config-dev.yml: ```yaml config: info: "config info for dev(dev)" ``` - config-test.yml: ```yaml config: info: "config info for test(dev)" ``` - config-prod.yml: ```yaml config: info: "config info for prod(dev)" ``` ## 创建config-server模块 > 这里我们创建一个config-server模块来演示Spring Cloud Config 作为配置中心的功能。 ### 在pom.xml中添加相关依赖 ```xml org.springframework.cloud spring-cloud-config-server org.springframework.cloud spring-cloud-starter-netflix-eureka-client ``` ### 在application.yml中进行配置 ```yaml server: port: 8901 spring: application: name: config-server cloud: config: server: git: #配置存储配置信息的Git仓库 uri: https://gitee.com/macrozheng/springcloud-config.git username: macro password: 123456 clone-on-start: true #开启启动时直接从git获取配置 eureka: client: service-url: defaultZone: http://localhost:8001/eureka/ ``` ### 在启动类上添加@EnableConfigServer注解来启用配置中心功能 ```java @EnableConfigServer @EnableDiscoveryClient @SpringBootApplication public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } ``` ### 通过config-server获取配置信息 > 这里我们通过config-server来演示下如何获取配置信息。 #### 获取配置文件信息的访问格式 ```bash # 获取配置信息 /{label}/{application}-{profile} # 获取配置文件信息 /{label}/{application}-{profile}.yml ``` #### 占位符相关解释 - application:代表应用名称,默认为配置文件中的spring.application.name,如果配置了spring.cloud.config.name,则为该名称; - label:代表分支名称,对应配置文件中的spring.cloud.config.label; - profile:代表环境名称,对应配置文件中的spring.cloud.config.profile。 #### 获取配置信息演示 - 启动eureka-server、config-server服务; - 访问[http://localhost:8901/master/config-dev](http://localhost:8901/master/config-dev)来获取master分支上dev环境的配置信息; ![](../images/springcloud_config_02.png) - 访问[http://localhost:8901/master/config-dev.yml](http://localhost:8901/master/config-dev.yml)来获取master分支上dev环境的配置文件信息,对比上面信息,可以看出配置信息和配置文件信息并不是同一个概念; ![](../images/springcloud_config_03.png) - 访问[http://localhost:8901/master/config-test.yml](http://localhost:8901/dev/config-dev.yml)来获取master分支上test环境的配置文件信息: ![](../images/springcloud_config_04.png) - 访问[http://localhost:8901/dev/config-dev.yml](http://localhost:8901/dev/config-dev.yml)来获取dev分支上dev环境的配置文件信息: ![](../images/springcloud_config_05.png) ## 创建config-client模块 > 我们创建一个config-client模块来从config-server获取配置。 ### 在pom.xml中添加相关依赖 ```xml org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-web ``` ### 在bootstrap.yml中进行配置 ```yaml server: port: 9001 spring: application: name: config-client cloud: config: #Config客户端配置 profile: dev #启用配置后缀名称 label: dev #分支名称 uri: http://localhost:8901 #配置中心地址 name: config #配置文件名称 eureka: client: service-url: defaultZone: http://localhost:8001/eureka/ ``` ### 添加ConfigClientController类用于获取配置 ```java /** * Created by macro on 2019/9/11. */ @RestController public class ConfigClientController { @Value("${config.info}") private String configInfo; @GetMapping("/configInfo") public String getConfigInfo() { return configInfo; } } ``` ### 演示从配置中心获取配置 - 启动config-client服务; - 访问[http://localhost:9001/configInfo](http://localhost:9001/configInfo),可以获取到dev分支下dev环境的配置; ```bash config info for dev(dev) ``` ### 获取子目录下的配置 > 我们不仅可以把每个项目的配置放在不同的Git仓库存储,也可以在一个Git仓库中存储多个项目的配置,此时就会用到在子目录中搜索配置信息的配置。 - 首先我们需要在config-server中添加相关配置,用于搜索子目录中的配置,这里我们用到了application占位符,表示对于不同的应用,我们从对应应用名称的子目录中搜索配置,比如config子目录中的配置对应config应用; ```yaml spring: cloud: config: server: git: search-paths: '{application}' ``` - 访问[http://localhost:9001/configInfo](http://localhost:9001/configInfo)进行测试,可以发现获取的是config子目录下的配置信息。 ```bash config info for config dir dev(dev) ``` ### 刷新配置 > 当Git仓库中的配置信息更改后,我们可以通过SpringBoot Actuator的refresh端点来刷新客户端配置信息,以下更改都需要在config-client中进行。 - 在pom.xml中添加Actuator的依赖: ```xml org.springframework.boot spring-boot-starter-actuator ``` - 在bootstrap.yml中开启refresh端点: ```yaml management: endpoints: web: exposure: include: 'refresh' ``` - 在ConfigClientController类添加@RefreshScope注解用于刷新配置: ```java /** * Created by macro on 2019/9/11. */ @RestController @RefreshScope public class ConfigClientController { @Value("${config.info}") private String configInfo; @GetMapping("/configInfo") public String getConfigInfo() { return configInfo; } } ``` - 重新启动config-client后,调用refresh端点进行配置刷新: ![](../images/springcloud_config_06.png) - 访问[http://localhost:9001/configInfo](http://localhost:9001/configInfo)进行测试,可以发现配置信息已经刷新。 ```bash update config info for config dir dev(dev) ``` ## 配置中心添加安全认证 > 我们可以通过整合SpringSecurity来为配置中心添加安全认证。 ### 创建config-security-server模块 - 在pom.xml中添加相关依赖: ```xml org.springframework.cloud spring-cloud-config-server org.springframework.boot spring-boot-starter-security ``` - 在application.yml中进行配置: ```yaml server: port: 8905 spring: application: name: config-security-server cloud: config: server: git: uri: https://gitee.com/macrozheng/springcloud-config.git username: macro password: 123456 clone-on-start: true #开启启动时直接从git获取配置 security: #配置用户名和密码 user: name: macro password: 123456 ``` - 启动config-security-server服务。 ### 修改config-client的配置 - 添加bootstrap-security.yml配置文件,主要是配置了配置中心的用户名和密码: ```yaml server: port: 9002 spring: application: name: config-client cloud: config: profile: dev #启用配置后缀名称 label: dev #分支名称 uri: http://localhost:8905 #配置中心地址 name: config #配置文件名称 username: macro password: 123456 ``` - 使用bootstrap-security.yml启动config-client服务; - 访问[http://localhost:9002/configInfo](http://localhost:9002/configInfo)进行测试,发现可以获取到配置信息。 ```bash config info for dev(dev) ``` ## config-sever集群搭建 > 在微服务架构中,所有服务都从配置中心获取配置,配置中心一旦宕机,会发生很严重的问题,下面我们搭建一个双节点的配置中心集群来解决该问题。 - 启动两个config-server分别运行在8902和8903端口上; - 添加config-client的配置文件bootstrap-cluster.yml,主要是添加了从注册中心获取配置中心地址的配置并去除了配置中心uri的配置: ```yaml spring: cloud: config: profile: dev #启用环境名称 label: dev #分支名称 name: config #配置文件名称 discovery: enabled: true service-id: config-server eureka: client: service-url: defaultZone: http://localhost:8001/eureka/ ``` - 以bootstrap-cluster.yml启动config-client服务,注册中心显示信息如下: ![](../images/springcloud_config_07.png) - 访问[http://localhost:9003/configInfo](http://localhost:9003/configInfo),发现config-client可以获取到配置信息。 ```bash config info for config dir dev(dev) ``` ## 使用到的模块 ```lua springcloud-learning ├── eureka-server -- eureka注册中心 ├── config-server -- 配置中心服务 ├── config-security-server -- 带安全认证的配置中心服务 └── config-client -- 获取配置的客户端服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/consul.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Consul:服务治理与配置中心 > Spring Cloud Consul 为 SpringBoot 应用提供了 Consul的支持,Consul既可以作为注册中心使用,也可以作为配置中心使用,本文将对其用法进行详细介绍。 ## Consul 简介 Consul是HashiCorp公司推出的开源软件,提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。 Spring Cloud Consul 具有如下特性: - 支持服务治理:Consul作为注册中心时,微服务中的应用可以向Consul注册自己,并且可以从Consul获取其他应用信息; - 支持客户端负责均衡:包括Ribbon和Spring Cloud LoadBalancer; - 支持Zuul:当Zuul作为网关时,可以从Consul中注册和发现应用; - 支持分布式配置管理:Consul作为配置中心时,使用键值对来存储配置信息; - 支持控制总线:可以在整个微服务系统中通过 Control Bus 分发事件消息。 ## 使用Consul作为注册中心 ### 安装并运行Consul - 首先我们从官网下载Consul,地址:https://www.consul.io/downloads.html ![](../images/springcloud_consul_01.png) - 下载完成后只有一个exe文件,双击运行; - 在命令行中输入以下命令可以查看版本号: ```shell consul --version ``` - 查看版本号信息如下: ```bash Consul v1.6.1 Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents) ``` - 使用开发模式启动: ```shell consul agent -dev ``` - 通过以下地址可以访问Consul的首页:http://localhost:8500 ![](../images/springcloud_consul_02.png) ### 创建应用注册到Consul > 我们通过改造user-service和ribbon-service来演示下服务注册与发现的功能,主要是将应用原来的Eureka注册中心支持改为Consul注册中心支持。 - 创建consul-user-service模块和consul-ribbon-service模块; - 修改相关依赖,把原来的Eureka注册发现的依赖改为Consul的,并添加SpringBoot Actuator的依赖: ```xml org.springframework.cloud spring-cloud-starter-consul-discovery org.springframework.boot spring-boot-starter-actuator ``` - 修改配置文件application.yml,将Eureka的注册发现配置改为Consul的: ```yaml server: port: 8206 spring: application: name: consul-user-service cloud: consul: #Consul服务注册发现配置 host: localhost port: 8500 discovery: service-name: ${spring.application.name} ``` - 运行两个consul-user-service和一个consul-ribbon-service,在Consul页面上可以看到如下信息: ![](../images/springcloud_consul_03.png) ### 负载均衡功能 > 由于我们运行了两个consul-user-service,而consul-ribbon-service默认会去调用它的接口,我们调用consul-ribbon-service的接口来演示下负载均衡功能。 多次调用接口:http://localhost:8308/user/1 ,可以发现两个consul-user-service的控制台交替打印如下信息。 ```bash 2019-10-20 10:39:32.580 INFO 12428 --- [io-8206-exec-10] c.macro.cloud.controller.UserController : 根据id获取用户信息,用户名称为:macro ``` ## 使用Consul作为配置中心 > 我们通过创建consul-config-client模块,并在Consul中添加配置信息来演示下配置管理的功能。 ### 创建consul-config-client模块 - 在pom.xml中添加相关依赖: ```xml org.springframework.cloud spring-cloud-starter-consul-config org.springframework.cloud spring-cloud-starter-consul-discovery ``` - 添加配置文件application.yml,启用的是dev环境的配置: ```yaml spring: profiles: active: dev ``` - 添加配置文件bootstrap.yml,主要是对Consul的配置功能进行配置: ```yaml server: port: 9101 spring: application: name: consul-config-client cloud: consul: host: localhost port: 8500 discovery: serviceName: consul-config-client config: enabled: true #是否启用配置中心功能 format: yaml #设置配置值的格式 prefix: config #设置配置所在目录 profile-separator: ':' #设置配置的分隔符 data-key: data #配置key的名字,由于Consul是K/V存储,配置存储在对应K的V中 ``` - 创建ConfigClientController,从Consul配置中心中获取配置信息: ```java /** * Created by macro on 2019/9/11. */ @RestController @RefreshScope public class ConfigClientController { @Value("${config.info}") private String configInfo; @GetMapping("/configInfo") public String getConfigInfo() { return configInfo; } } ``` ### 在Consul中添加配置 - 在consul中添加配置存储的key为: ```bash config/consul-config-client:dev/data ``` - 在consul中添加配置存储的value为: ```yaml config: info: "config info for dev" ``` - 存储信息截图如下: ![](../images/springcloud_consul_04.png) - 启动consul-config-client,调用接口查看配置信息:http://localhost:9101/configInfo ```bash config info for dev ``` ### Consul的动态刷新配置 我们只要修改下Consul中的配置信息,再次调用查看配置的接口,就会发现配置已经刷新。回想下在使用Spring Cloud Config的时候,我们需要调用接口,通过Spring Cloud Bus才能刷新配置。Consul使用其自带的Control Bus 实现了一种事件传递机制,从而实现了动态刷新功能。 ## 使用到的模块 ``` lua springcloud-learning ├── consul-config-client -- 用于演示consul作为配置中心的consul客户端 ├── consul-user-service -- 注册到consul的提供User对象CRUD接口的服务 └── consul-service -- 注册到consul的ribbon服务调用测试服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/eureka.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Eureka:服务注册与发现 > Spring Cloud Eureka是Spring Cloud Netflix 子项目的核心组件之一,主要用于微服务架构中的服务治理。 本文将对搭建Eureka注册中心,搭建Eureka客户端,搭建Eureka集群及给Eureka注册中心添加登录认证进行介绍。 ## Eureka简介 在微服务架构中往往会有一个注册中心,每个微服务都会向注册中心去注册自己的地址及端口信息,注册中心维护着服务名称与服务实例的对应关系。每个微服务都会定时从注册中心获取服务列表,同时汇报自己的运行情况,这样当有的服务需要调用其他服务时,就可以从自己获取到的服务列表中获取实例地址进行调用,Eureka实现了这套服务注册与发现机制。 ## 搭建Eureka注册中心 > 这里我们以创建并运行Eureka注册中心来看看在IDEA中创建并运行SpringCloud应用的正确姿势。 ### 使用IDEA来创建SpringCloud应用 - 创建一个eureka-server模块,并使用Spring Initializer初始化一个SpringBoot项目 ![](../images/springcloud_eureka_01.png) - 填写应用信息 ![](../images/springcloud_eureka_02.png) - 选择你需要的SpringCloud组件进行创建 ![](../images/springcloud_eureka_03.png) - 创建完成后会发现pom.xml文件中已经有了eureka-server的依赖 ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-server ``` - 在启动类上添加@EnableEurekaServer注解来启用Euerka注册中心功能 ```java @EnableEurekaServer @SpringBootApplication public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } } ``` - 在配置文件application.yml中添加Eureka注册中心的配置 ```yaml server: port: 8001 #指定运行端口 spring: application: name: eureka-server #指定服务名称 eureka: instance: hostname: localhost #指定主机地址 client: fetch-registry: false #指定是否要从注册中心获取服务(注册中心不需要开启) register-with-eureka: false #指定是否要注册到注册中心(注册中心不需要开启) server: enable-self-preservation: false #关闭保护模式 ``` ### 使用IDEA的Run Dashboard来运行SpringCloud应用 > 此时服务已经创建完成,点击启动类的main方法就可以运行了。但是在微服务项目中我们会启动很多服务,为了便于管理,我们使用IDEA的Run Dashboard来启动。 - 打开Run Dashboard,默认情况下,当IDEA检查到你的项目中有SpringBoot应用时,会提示你开启,如果你没开启,可以用以下方法开启。 ![](../images/springcloud_eureka_04.png) - 运行SpringCloud应用 ![](../images/springcloud_eureka_05.png) - 运行完成后访问地址[http://localhost:8001/](http://localhost:8001/)可以看到Eureka注册中心的界面 ![](../images/springcloud_eureka_06.png) ## 搭建Eureka客户端 - 新建一个eureka-client模块,并在pom.xml中添加如下依赖 ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-web ``` - 在启动类上添加@EnableDiscoveryClient注解表明是一个Eureka客户端 ```java @EnableDiscoveryClient @SpringBootApplication public class EurekaClientApplication { public static void main(String[] args) { SpringApplication.run(EurekaClientApplication.class, args); } } ``` - 在配置文件application.yml中添加Eureka客户端的配置 ```yaml server: port: 8101 #运行端口号 spring: application: name: eureka-client #服务名称 eureka: client: register-with-eureka: true #注册到Eureka的注册中心 fetch-registry: true #获取注册实例列表 service-url: defaultZone: http://localhost:8001/eureka/ #配置注册中心地址 ``` - 运行eureka-client ![](../images/springcloud_eureka_07.png) - 查看注册中心[http://localhost:8001/](http://localhost:8001/)发现Eureka客户端已经成功注册 ![](../images/springcloud_eureka_08.png) ## 搭建Eureka注册中心集群 ### 搭建两个注册中心 > 由于所有服务都会注册到注册中心去,服务之间的调用都是通过从注册中心获取的服务列表来调用,注册中心一旦宕机,所有服务调用都会出现问题。所以我们需要多个注册中心组成集群来提供服务,下面将搭建一个双节点的注册中心集群。 - 给eureka-sever添加配置文件application-replica1.yml配置第一个注册中心 ```yaml server: port: 8002 spring: application: name: eureka-server eureka: instance: hostname: replica1 client: serviceUrl: defaultZone: http://replica2:8003/eureka/ #注册到另一个Eureka注册中心 fetch-registry: true register-with-eureka: true ``` - 给eureka-sever添加配置文件application-replica2.yml配置第二个注册中心 ```yaml server: port: 8003 spring: application: name: eureka-server eureka: instance: hostname: replica2 client: serviceUrl: defaultZone: http://replica1:8002/eureka/ #注册到另一个Eureka注册中心 fetch-registry: true register-with-eureka: true ``` **这里我们通过两个注册中心互相注册,搭建了注册中心的双节点集群,由于defaultZone使用了域名,所以还需在本机的host文件中配置一下。** - 修改本地host文件 ``` 127.0.0.1 replica1 127.0.0.1 replica2 ``` ### 运行Eureka注册中心集群 > 在IDEA中我们可以通过使用不同的配置文件来启动同一个SpringBoot应用。 - 添加两个配置,分别以application-replica1.yml和application-replica2.yml来启动eureka-server > 从原启动配置中复制一个出来 ![](../images/springcloud_eureka_09.png) > 配置启动的配置文件 ![](../images/springcloud_eureka_10.png) - 启动两个eureka-server,访问其中一个注册中心[http://replica1:8002/](http://replica1:8002/)发现另一个已经成为其备份 ![](../images/springcloud_eureka_11.png) - 修改Eureka-client,让其连接到集群 > 添加eureka-client的配置文件application-replica.yml,让其同时注册到两个注册中心。 ```yaml server: port: 8102 spring: application: name: eureka-client eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://replica1:8002/eureka/,http://replica2:8003/eureka/ #同时注册到两个注册中心 ``` > 以该配置文件启动后访问任意一个注册中心节点都可以看到eureka-client ![](../images/springcloud_eureka_12.png) ## 给Eureka注册中心添加认证 ### 创建一个eureka-security-server模块,在pom.xml中添加以下依赖 > 需要添加SpringSecurity模块。 ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-server org.springframework.boot spring-boot-starter-security ``` ### 添加application.yml配置文件 > 主要是配置了登录注册中心的用户名和密码。 ```yaml server: port: 8004 spring: application: name: eureka-security-server security: #配置SpringSecurity登录用户名和密码 user: name: macro password: 123456 eureka: instance: hostname: localhost client: fetch-registry: false register-with-eureka: false ``` ### 添加Java配置WebSecurityConfig > 默认情况下添加SpringSecurity依赖的应用每个请求都需要添加CSRF token才能访问,Eureka客户端注册时并不会添加,所以需要配置/eureka/**路径不需要CSRF token。 ```java @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().ignoringAntMatchers("/eureka/**"); super.configure(http); } } ``` ### 运行eureka-security-server,访问[http://localhost:8004](http://localhost:8004)发现需要登录认证 ![](../images/springcloud_eureka_13.png) ### eureka-client注册到有登录认证的注册中心 - 配置文件中需要修改注册中心地址格式 ``` http://${username}:${password}@${hostname}:${port}/eureka/ ``` - 添加application-security.yml配置文件,按格式修改用户名和密码 ```yaml server: port: 8103 spring: application: name: eureka-client eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://macro:123456@localhost:8004/eureka/ ``` - 以application-security.yml配置运行eureka-client,可以在注册中心界面看到eureka-client已经成功注册 ![](../images/springcloud_eureka_14.png) ## Eureka的常用配置 ```yaml eureka: client: #eureka客户端配置 register-with-eureka: true #是否将自己注册到eureka服务端上去 fetch-registry: true #是否获取eureka服务端上注册的服务列表 service-url: defaultZone: http://localhost:8001/eureka/ # 指定注册中心地址 enabled: true # 启用eureka客户端 registry-fetch-interval-seconds: 30 #定义去eureka服务端获取服务列表的时间间隔 instance: #eureka客户端实例配置 lease-renewal-interval-in-seconds: 30 #定义服务多久去注册中心续约 lease-expiration-duration-in-seconds: 90 #定义服务多久不去续约认为服务失效 metadata-map: zone: jiangsu #所在区域 hostname: localhost #服务主机名称 prefer-ip-address: false #是否优先使用ip来作为主机名 server: #eureka服务端配置 enable-self-preservation: false #关闭eureka服务端的保护机制 ``` ## 使用到的模块 ``` lua springcloud-learning ├── eureka-server -- eureka注册中心 ├── eureka-security-server -- 带登录认证的eureka注册中心 └── eureka-client -- eureka客户端 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/feign.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud OpenFeign:基于Ribbon和Hystrix的声明式服务调用 > Spring Cloud OpenFeign 是声明式的服务调用工具,它整合了Ribbon和Hystrix,拥有负载均衡和服务容错功能,本文将对其用法进行详细介绍。 ## Feign简介 Feign是声明式的服务调用工具,我们只需创建一个接口并用注解的方式来配置它,就可以实现对某个服务接口的调用,简化了直接使用RestTemplate来调用服务接口的开发量。Feign具备可插拔的注解支持,同时支持Feign注解、JAX-RS注解及SpringMvc注解。当使用Feign时,Spring Cloud集成了Ribbon和Eureka以提供负载均衡的服务调用及基于Hystrix的服务容错保护功能。 ## 创建一个feign-service模块 > 这里我们创建一个feign-service模块来演示feign的常用功能。 ### 在pom.xml中添加相关依赖 ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-openfeign org.springframework.boot spring-boot-starter-web ``` ### 在application.yml中进行配置 ```yaml server: port: 8701 spring: application: name: feign-service eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8001/eureka/ ``` ### 在启动类上添加@EnableFeignClients注解来启用Feign的客户端功能 ```java @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class FeignServiceApplication { public static void main(String[] args) { SpringApplication.run(FeignServiceApplication.class, args); } } ``` ### 添加UserService接口完成对user-service服务的接口绑定 > 我们通过@FeignClient注解实现了一个Feign客户端,其中的value为user-service表示这是对user-service服务的接口调用客户端。我们可以回想下user-service中的UserController,只需将其改为接口,保留原来的SpringMvc注释即可。 ```java /** * Created by macro on 2019/9/5. */ @FeignClient(value = "user-service") public interface UserService { @PostMapping("/user/create") CommonResult create(@RequestBody User user); @GetMapping("/user/{id}") CommonResult getUser(@PathVariable Long id); @GetMapping("/user/getByUsername") CommonResult getByUsername(@RequestParam String username); @PostMapping("/user/update") CommonResult update(@RequestBody User user); @PostMapping("/user/delete/{id}") CommonResult delete(@PathVariable Long id); } ``` ### 添加UserFeignController调用UserService实现服务调用 ```java /** * Created by macro on 2019/8/29. */ @RestController @RequestMapping("/user") public class UserFeignController { @Autowired private UserService userService; @GetMapping("/{id}") public CommonResult getUser(@PathVariable Long id) { return userService.getUser(id); } @GetMapping("/getByUsername") public CommonResult getByUsername(@RequestParam String username) { return userService.getByUsername(username); } @PostMapping("/create") public CommonResult create(@RequestBody User user) { return userService.create(user); } @PostMapping("/update") public CommonResult update(@RequestBody User user) { return userService.update(user); } @PostMapping("/delete/{id}") public CommonResult delete(@PathVariable Long id) { return userService.delete(id); } } ``` ## 负载均衡功能演示 - 启动eureka-service,两个user-service,feign-service服务,启动后注册中心显示如下: ![](../images/springcloud_feign_01.png) - 多次调用[http://localhost:8701/user/1](http://localhost:8701/user/1)进行测试,可以发现运行在8201和8202的user-service服务交替打印如下信息: ```bash 2019-10-04 15:15:34.829 INFO 9236 --- [nio-8201-exec-5] c.macro.cloud.controller.UserController : 根据id获取用户信息,用户名称为:macro 2019-10-04 15:15:35.492 INFO 9236 --- [io-8201-exec-10] c.macro.cloud.controller.UserController : 根据id获取用户信息,用户名称为:macro 2019-10-04 15:15:35.825 INFO 9236 --- [nio-8201-exec-9] c.macro.cloud.controller.UserController : 根据id获取用户信息,用户名称为:macro ``` ## Feign中的服务降级 > Feign中的服务降级使用起来非常方便,只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可,下面我们为UserService接口添加一个服务降级实现类。 ### 添加服务降级实现类UserFallbackService > 需要注意的是它实现了UserService接口,并且对接口中的每个实现方法进行了服务降级逻辑的实现。 ```java /** * Created by macro on 2019/9/5. */ @Component public class UserFallbackService implements UserService { @Override public CommonResult create(User user) { User defaultUser = new User(-1L, "defaultUser", "123456"); return new CommonResult<>(defaultUser); } @Override public CommonResult getUser(Long id) { User defaultUser = new User(-1L, "defaultUser", "123456"); return new CommonResult<>(defaultUser); } @Override public CommonResult getByUsername(String username) { User defaultUser = new User(-1L, "defaultUser", "123456"); return new CommonResult<>(defaultUser); } @Override public CommonResult update(User user) { return new CommonResult("调用失败,服务被降级",500); } @Override public CommonResult delete(Long id) { return new CommonResult("调用失败,服务被降级",500); } } ``` ### 修改UserService接口,设置服务降级处理类为UserFallbackService > 修改@FeignClient注解中的参数,设置fallback为UserFallbackService.class即可。 ```java @FeignClient(value = "user-service",fallback = UserFallbackService.class) public interface UserService { } ``` ### 修改application.yml,开启Hystrix功能 ```yaml feign: hystrix: enabled: true #在Feign中开启Hystrix ``` ## 服务降级功能演示 - 关闭两个user-service服务,重新启动feign-service; - 调用[http://localhost:8701/user/1](http://localhost:8701/user/1)进行测试,可以发现返回了服务降级信息。 ![](../images/springcloud_feign_02.png) ## 日志打印功能 > Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。 ### 日志级别 - NONE:默认的,不显示任何日志; - BASIC:仅记录请求方法、URL、响应状态码及执行时间; - HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息; - FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。 ### 通过配置开启更为详细的日志 > 我们通过java配置来使Feign打印最详细的Http请求日志信息。 ```java /** * Created by macro on 2019/9/5. */ @Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } } ``` ### 在application.yml中配置需要开启日志的Feign客户端 > 配置UserService的日志级别为debug。 ```yaml logging: level: com.macro.cloud.service.UserService: debug ``` ### 查看日志 > 调用[http://localhost:8701/user/1](http://localhost:8701/user/1)进行测试,可以看到以下日志。 ```bash 2019-10-04 15:44:03.248 DEBUG 5204 --- [-user-service-2] com.macro.cloud.service.UserService : [UserService#getUser] ---> GET http://user-service/user/1 HTTP/1.1 2019-10-04 15:44:03.248 DEBUG 5204 --- [-user-service-2] com.macro.cloud.service.UserService : [UserService#getUser] ---> END HTTP (0-byte body) 2019-10-04 15:44:03.257 DEBUG 5204 --- [-user-service-2] com.macro.cloud.service.UserService : [UserService#getUser] <--- HTTP/1.1 200 (9ms) 2019-10-04 15:44:03.257 DEBUG 5204 --- [-user-service-2] com.macro.cloud.service.UserService : [UserService#getUser] content-type: application/json;charset=UTF-8 2019-10-04 15:44:03.258 DEBUG 5204 --- [-user-service-2] com.macro.cloud.service.UserService : [UserService#getUser] date: Fri, 04 Oct 2019 07:44:03 GMT 2019-10-04 15:44:03.258 DEBUG 5204 --- [-user-service-2] com.macro.cloud.service.UserService : [UserService#getUser] transfer-encoding: chunked 2019-10-04 15:44:03.258 DEBUG 5204 --- [-user-service-2] com.macro.cloud.service.UserService : [UserService#getUser] 2019-10-04 15:44:03.258 DEBUG 5204 --- [-user-service-2] com.macro.cloud.service.UserService : [UserService#getUser] {"data":{"id":1,"username":"macro","password":"123456"},"message":"操作成功","code":200} 2019-10-04 15:44:03.258 DEBUG 5204 --- [-user-service-2] com.macro.cloud.service.UserService : [UserService#getUser] <--- END HTTP (92-byte body) ``` ## Feign的常用配置 ### Feign自己的配置 ```yaml feign: hystrix: enabled: true #在Feign中开启Hystrix compression: request: enabled: false #是否对请求进行GZIP压缩 mime-types: text/xml,application/xml,application/json #指定压缩的请求数据类型 min-request-size: 2048 #超过该大小的请求会被压缩 response: enabled: false #是否对响应进行GZIP压缩 logging: level: #修改日志级别 com.macro.cloud.service.UserService: debug ``` ### Feign中的Ribbon配置 在Feign中配置Ribbon可以直接使用Ribbon的配置,具体可以参考[Spring Cloud Ribbon:负载均衡的服务调用](https://mp.weixin.qq.com/s/uKteoRrFjUbbl08NG522YQ)。 ### Feign中的Hystrix配置 在Feign中配置Hystrix可以直接使用Hystrix的配置,具体可以参考[Spring Cloud Hystrix:服务容错保护](https://mp.weixin.qq.com/s/lEjojtuH7XOM9emXkd0TkQ)。 ## 使用到的模块 ``` lua springcloud-learning ├── eureka-server -- eureka注册中心 ├── user-service -- 提供User对象CRUD接口的服务 └── feign-service -- feign服务调用测试服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/gateway.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Gateway:新一代API网关服务 > Spring Cloud Gateway 为 SpringBoot 应用提供了API网关支持,具有强大的智能路由与过滤器功能,本文将对其用法进行详细介绍。 ## Gateway 简介 Gateway是在Spring生态系统之上构建的API网关服务,基于Spring 5,Spring Boot 2和 Project Reactor等技术。Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能, 例如:熔断、限流、重试等。 Spring Cloud Gateway 具有如下特性: - 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建; - 动态路由:能够匹配任何请求属性; - 可以对路由指定 Predicate(断言)和 Filter(过滤器); - 集成Hystrix的断路器功能; - 集成 Spring Cloud 服务发现功能; - 易于编写的 Predicate(断言)和 Filter(过滤器); - 请求限流功能; - 支持路径重写。 ## 相关概念 - Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由; - Predicate(断言):指的是Java 8 的 Function Predicate。 输入类型是Spring框架中的ServerWebExchange。 这使开发人员可以匹配HTTP请求中的所有内容,例如请求头或请求参数。如果请求与断言相匹配,则进行路由; - Filter(过滤器):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前后对请求进行修改。 ## 创建 api-gateway模块 > 这里我们创建一个api-gateway模块来演示Gateway的常用功能。 ### 在pom.xml中添加相关依赖 ```xml org.springframework.cloud spring-cloud-starter-gateway ``` ### 两种不同的配置路由方式 > Gateway 提供了两种不同的方式用于配置路由,一种是通过yml文件来配置,另一种是通过Java Bean来配置,下面我们分别介绍下。 #### 使用yml配置 - 在application.yml中进行配置: ```yaml server: port: 9201 service-url: user-service: http://localhost:8201 spring: cloud: gateway: routes: - id: path_route #路由的ID uri: ${service-url.user-service}/user/{id} #匹配后路由地址 predicates: # 断言,路径相匹配的进行路由 - Path=/user/{id} ``` - 启动eureka-server,user-service和api-gateway服务,并调用该地址测试:http://localhost:9201/user/1 - 我们发现该请求被路由到了user-service的该路径上:http://localhost:8201/user/1 ![](../images/springcloud_gateway_01.png) #### 使用Java Bean配置 - 添加相关配置类,并配置一个RouteLocator对象: ```java /** * Created by macro on 2019/9/24. */ @Configuration public class GatewayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("path_route2", r -> r.path("/user/getByUsername") .uri("http://localhost:8201/user/getByUsername")) .build(); } } ``` - 重新启动api-gateway服务,并调用该地址测试:http://localhost:9201/user/getByUsername?username=macro - 我们发现该请求被路由到了user-service的该路径上:http://localhost:8201/user/getByUsername?username=macro ![](../images/springcloud_gateway_02.png) ## Route Predicate 的使用 Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。 Spring Cloud Gateway包括许多内置的Route Predicate工厂。 所有这些Predicate都与HTTP请求的不同属性匹配。 多个Route Predicate工厂可以进行组合,下面我们来介绍下一些常用的Route Predicate。 注意:Predicate中提到的配置都在application-predicate.yml文件中进行修改,并用该配置启动api-gateway服务。 ### After Route Predicate 在指定时间之后的请求会匹配该路由。 ```yaml spring: cloud: gateway: routes: - id: after_route uri: ${service-url.user-service} predicates: - After=2019-09-24T16:30:00+08:00[Asia/Shanghai] ``` ### Before Route Predicate 在指定时间之前的请求会匹配该路由。 ```yaml spring: cloud: gateway: routes: - id: before_route uri: ${service-url.user-service} predicates: - Before=2019-09-24T16:30:00+08:00[Asia/Shanghai] ``` ### Between Route Predicate 在指定时间区间内的请求会匹配该路由。 ```yaml spring: cloud: gateway: routes: - id: before_route uri: ${service-url.user-service} predicates: - Between=2019-09-24T16:30:00+08:00[Asia/Shanghai], 2019-09-25T16:30:00+08:00[Asia/Shanghai] ``` ### Cookie Route Predicate 带有指定Cookie的请求会匹配该路由。 ```yaml spring: cloud: gateway: routes: - id: cookie_route uri: ${service-url.user-service} predicates: - Cookie=username,macro ``` 使用curl工具发送带有cookie为`username=macro`的请求可以匹配该路由。 ```bash curl http://localhost:9201/user/1 --cookie "username=macro" ``` ### Header Route Predicate 带有指定请求头的请求会匹配该路由。 ```yaml spring: cloud: gateway: routes: - id: header_route uri: ${service-url.user-service} predicates: - Header=X-Request-Id, \d+ ``` 使用curl工具发送带有请求头为`X-Request-Id:123`的请求可以匹配该路由。 ```bash curl http://localhost:9201/user/1 -H "X-Request-Id:123" ``` ### Host Route Predicate 带有指定Host的请求会匹配该路由。 ```yaml spring: cloud: gateway: routes: - id: host_route uri: ${service-url.user-service} predicates: - Host=**.macrozheng.com ``` 使用curl工具发送带有请求头为`Host:www.macrozheng.com`的请求可以匹配该路由。 ```bash curl http://localhost:9201/user/1 -H "Host:www.macrozheng.com" ``` ### Method Route Predicate 发送指定方法的请求会匹配该路由。 ```yaml spring: cloud: gateway: routes: - id: method_route uri: ${service-url.user-service} predicates: - Method=GET ``` 使用curl工具发送GET请求可以匹配该路由。 ```bash curl http://localhost:9201/user/1 ``` 使用curl工具发送POST请求无法匹配该路由。 ```bash curl -X POST http://localhost:9201/user/1 ``` ### Path Route Predicate 发送指定路径的请求会匹配该路由。 ```yaml spring: cloud: gateway: routes: - id: path_route uri: ${service-url.user-service}/user/{id} predicates: - Path=/user/{id} ``` 使用curl工具发送`/user/1`路径请求可以匹配该路由。 ```bash curl http://localhost:9201/user/1 ``` 使用curl工具发送`/abc/1`路径请求无法匹配该路由。 ```bash curl http://localhost:9201/abc/1 ``` ### Query Route Predicate 带指定查询参数的请求可以匹配该路由。 ```yaml spring: cloud: gateway: routes: - id: query_route uri: ${service-url.user-service}/user/getByUsername predicates: - Query=username ``` 使用curl工具发送带`username=macro`查询参数的请求可以匹配该路由。 ```bash curl http://localhost:9201/user/getByUsername?username=macro ``` 使用curl工具发送带不带查询参数的请求无法匹配该路由。 ```bash curl http://localhost:9201/user/getByUsername ``` ### RemoteAddr Route Predicate 从指定远程地址发起的请求可以匹配该路由。 ```yaml spring: cloud: gateway: routes: - id: remoteaddr_route uri: ${service-url.user-service} predicates: - RemoteAddr=192.168.1.1/24 ``` 使用curl工具从192.168.1.1发起请求可以匹配该路由。 ```bash curl http://localhost:9201/user/1 ``` ### Weight Route Predicate 使用权重来路由相应请求,以下表示有80%的请求会被路由到localhost:8201,20%会被路由到localhost:8202。 ```yaml spring: cloud: gateway: routes: - id: weight_high uri: http://localhost:8201 predicates: - Weight=group1, 8 - id: weight_low uri: http://localhost:8202 predicates: - Weight=group1, 2 ``` ## Route Filter 的使用 > 路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。Spring Cloud Gateway 内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生,下面我们介绍下常用路由过滤器的用法。 ### AddRequestParameter GatewayFilter 给请求添加参数的过滤器。 ```yaml spring: cloud: gateway: routes: - id: add_request_parameter_route uri: http://localhost:8201 filters: - AddRequestParameter=username, macro predicates: - Method=GET ``` 以上配置会对GET请求添加`username=macro`的请求参数,通过curl工具使用以下命令进行测试。 ```bash curl http://localhost:9201/user/getByUsername ``` 相当于发起该请求: ```bash curl http://localhost:8201/user/getByUsername?username=macro ``` ### StripPrefix GatewayFilter 对指定数量的路径前缀进行去除的过滤器。 ```yaml spring: cloud: gateway: routes: - id: strip_prefix_route uri: http://localhost:8201 predicates: - Path=/user-service/** filters: - StripPrefix=2 ``` 以上配置会把以`/user-service/`开头的请求的路径去除两位,通过curl工具使用以下命令进行测试。 ```bash curl http://localhost:9201/user-service/a/user/1 ``` 相当于发起该请求: ```bash curl http://localhost:8201/user/1 ``` ### PrefixPath GatewayFilter 与StripPrefix过滤器恰好相反,会对原有路径进行增加操作的过滤器。 ```yaml spring: cloud: gateway: routes: - id: prefix_path_route uri: http://localhost:8201 predicates: - Method=GET filters: - PrefixPath=/user ``` 以上配置会对所有GET请求添加`/user`路径前缀,通过curl工具使用以下命令进行测试。 ```bash curl http://localhost:9201/1 ``` 相当于发起该请求: ```bash curl http://localhost:8201/user/1 ``` ### Hystrix GatewayFilter Hystrix 过滤器允许你将断路器功能添加到网关路由中,使你的服务免受级联故障的影响,并提供服务降级处理。 - 要开启断路器功能,我们需要在pom.xml中添加Hystrix的相关依赖: ```xml org.springframework.cloud spring-cloud-starter-netflix-hystrix ``` - 然后添加相关服务降级的处理类: ```java /** * Created by macro on 2019/9/25. */ @RestController public class FallbackController { @GetMapping("/fallback") public Object fallback() { Map result = new HashMap<>(); result.put("data",null); result.put("message","Get request fallback!"); result.put("code",500); return result; } } ``` - 在application-filter.yml中添加相关配置,当路由出错时会转发到服务降级处理的控制器上: ```yaml spring: cloud: gateway: routes: - id: hystrix_route uri: http://localhost:8201 predicates: - Method=GET filters: - name: Hystrix args: name: fallbackcmd fallbackUri: forward:/fallback ``` - 关闭user-service,调用该地址进行测试:http://localhost:9201/user/1 ,发现已经返回了服务降级的处理信息。 ![](../images/springcloud_gateway_03.png) ### RequestRateLimiter GatewayFilter RequestRateLimiter 过滤器可以用于限流,使用RateLimiter实现来确定是否允许当前请求继续进行,如果请求太大默认会返回HTTP 429-太多请求状态。 - 在pom.xml中添加相关依赖: ```xml org.springframework.boot spring-boot-starter-data-redis-reactive ``` - 添加限流策略的配置类,这里有两种策略一种是根据请求参数中的username进行限流,另一种是根据访问IP进行限流; ```java /** * Created by macro on 2019/9/25. */ @Configuration public class RedisRateLimiterConfig { @Bean KeyResolver userKeyResolver() { return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("username")); } @Bean public KeyResolver ipKeyResolver() { return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); } } ``` - 我们使用Redis来进行限流,所以需要添加Redis和RequestRateLimiter的配置,这里对所有的GET请求都进行了按IP来限流的操作; ```yaml server: port: 9201 spring: redis: host: localhost password: 123456 port: 6379 cloud: gateway: routes: - id: requestratelimiter_route uri: http://localhost:8201 filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 1 #每秒允许处理的请求数量 redis-rate-limiter.burstCapacity: 2 #每秒最大处理的请求数量 key-resolver: "#{@ipKeyResolver}" #限流策略,对应策略的Bean predicates: - Method=GET logging: level: org.springframework.cloud.gateway: debug ``` - 多次请求该地址:http://localhost:9201/user/1 ,会返回状态码为429的错误; ![](../images/springcloud_gateway_04.png) ### Retry GatewayFilter 对路由请求进行重试的过滤器,可以根据路由请求返回的HTTP状态码来确定是否进行重试。 - 修改配置文件: ```yaml spring: cloud: gateway: routes: - id: retry_route uri: http://localhost:8201 predicates: - Method=GET filters: - name: Retry args: retries: 1 #需要进行重试的次数 statuses: BAD_GATEWAY #返回哪个状态码需要进行重试,返回状态码为5XX进行重试 backoff: firstBackoff: 10ms maxBackoff: 50ms factor: 2 basedOnPreviousValue: false ``` - 当调用返回500时会进行重试,访问测试地址:http://localhost:9201/user/111 - 可以发现user-service控制台报错2次,说明进行了一次重试。 ```bash 2019-10-27 14:08:53.435 ERROR 2280 --- [nio-8201-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause java.lang.NullPointerException: null at com.macro.cloud.controller.UserController.getUser(UserController.java:34) ~[classes/:na] ``` ## 结合注册中心使用 > 我们上次讲到[使用Zuul作为网关](https://mp.weixin.qq.com/s/ttL5gm_ZLAQL_uJ4iNtqsQ)结合注册中心进行使用时,默认情况下Zuul会根据注册中心注册的服务列表,以服务名为路径创建动态路由,Gateway同样也实现了该功能。下面我们演示下Gateway结合注册中心如何使用默认的动态路由和过滤器。 ### 使用动态路由 - 在pom.xml中添加相关依赖: ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client ``` - 添加application-eureka.yml配置文件: ```yaml server: port: 9201 spring: application: name: api-gateway cloud: gateway: discovery: locator: enabled: true #开启从注册中心动态创建路由的功能 lower-case-service-id: true #使用小写服务名,默认是大写 eureka: client: service-url: defaultZone: http://localhost:8001/eureka/ logging: level: org.springframework.cloud.gateway: debug ``` - 使用application-eureka.yml配置文件启动api-gateway服务,访问http://localhost:9201/user-service/user/1 ,可以路由到user-service的http://localhost:8201/user/1 处。 ### 使用过滤器 > 在结合注册中心使用过滤器的时候,我们需要注意的是uri的协议为`lb`,这样才能启用Gateway的负载均衡功能。 - 修改application-eureka.yml文件,使用了PrefixPath过滤器,会为所有GET请求路径添加`/user`路径并路由; ```yaml server: port: 9201 spring: application: name: api-gateway cloud: gateway: routes: - id: prefixpath_route uri: lb://user-service #此处需要使用lb协议 predicates: - Method=GET filters: - PrefixPath=/user discovery: locator: enabled: true eureka: client: service-url: defaultZone: http://localhost:8001/eureka/ logging: level: org.springframework.cloud.gateway: debug ``` - 使用application-eureka.yml配置文件启动api-gateway服务,访问http://localhost:9201/1 ,可以路由到user-service的http://localhost:8201/user/1 处。 ## 使用到的模块 ```lua springcloud-learning ├── eureka-server -- eureka注册中心 ├── user-service -- 提供User对象CRUD接口的服务 └── api-gateway -- gateway作为网关的测试服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/gateway_oauth2.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权! > 最近发现了一个很好的微服务权限解决方案,可以通过认证服务进行统一认证,然后通过网关来统一校验认证和鉴权。此方案为目前最新方案,仅支持Spring Boot 2.2.0、Spring Cloud Hoxton 以上版本,本文将详细介绍该方案的实现,希望对大家有所帮助! ## 前置知识 > 我们将采用Nacos作为注册中心,Gateway作为网关,使用`nimbus-jose-jwt`JWT库操作JWT令牌,对这些技术不了解的朋友可以看下下面的文章。 - [Spring Cloud Gateway:新一代API网关服务](https://mp.weixin.qq.com/s/bTp4_M3m7rhlhC9wYKExwA) - [Spring Cloud Alibaba:Nacos 作为注册中心和配置中心使用](https://mp.weixin.qq.com/s/N9eAMHuDEJq7kCCJPEEJqw) - [听说你的JWT库用起来特别扭,推荐这款贼好用的!](https://mp.weixin.qq.com/s/Jo3PZoa7nL99c8UCxPiTTA) ## 应用架构 > 我们理想的解决方案应该是这样的,认证服务负责认证,网关负责校验认证和鉴权,其他API服务负责处理自己的业务逻辑。安全相关的逻辑只存在于认证服务和网关服务中,其他服务只是单纯地提供服务而没有任何安全相关逻辑。 相关服务划分: - micro-oauth2-gateway:网关服务,负责请求转发和鉴权功能,整合Spring Security+Oauth2; - micro-oauth2-auth:Oauth2认证服务,负责对登录用户进行认证,整合Spring Security+Oauth2; - micro-oauth2-api:受保护的API服务,用户鉴权通过后可以访问该服务,不整合Spring Security+Oauth2。 ## 方案实现 > 下面介绍下这套解决方案的具体实现,依次搭建认证服务、网关服务和API服务。 ### micro-oauth2-auth > 我们首先来搭建认证服务,它将作为Oauth2的认证服务使用,并且网关服务的鉴权功能也需要依赖它。 - 在`pom.xml`中添加相关依赖,主要是Spring Security、Oauth2、JWT、Redis相关依赖; ```xml org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.cloud spring-cloud-starter-oauth2 com.nimbusds nimbus-jose-jwt 8.16 org.springframework.boot spring-boot-starter-data-redis ``` - 在`application.yml`中添加相关配置,主要是Nacos和Redis相关配置; ```yaml server: port: 9401 spring: profiles: active: dev application: name: micro-oauth2-auth cloud: nacos: discovery: server-addr: localhost:8848 jackson: date-format: yyyy-MM-dd HH:mm:ss redis: database: 0 port: 6379 host: localhost password: management: endpoints: web: exposure: include: "*" ``` - 使用`keytool`生成RSA证书`jwt.jks`,复制到`resource`目录下,在JDK的`bin`目录下使用如下命令即可; ```bash keytool -genkey -alias jwt -keyalg RSA -keystore jwt.jks ``` - 创建`UserServiceImpl`类实现Spring Security的`UserDetailsService`接口,用于加载用户信息; ```java /** * 用户管理业务类 * Created by macro on 2020/6/19. */ @Service public class UserServiceImpl implements UserDetailsService { private List userList; @Autowired private PasswordEncoder passwordEncoder; @PostConstruct public void initData() { String password = passwordEncoder.encode("123456"); userList = new ArrayList<>(); userList.add(new UserDTO(1L,"macro", password,1, CollUtil.toList("ADMIN"))); userList.add(new UserDTO(2L,"andy", password,1, CollUtil.toList("TEST"))); } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { List findUserList = userList.stream().filter(item -> item.getUsername().equals(username)).collect(Collectors.toList()); if (CollUtil.isEmpty(findUserList)) { throw new UsernameNotFoundException(MessageConstant.USERNAME_PASSWORD_ERROR); } SecurityUser securityUser = new SecurityUser(findUserList.get(0)); if (!securityUser.isEnabled()) { throw new DisabledException(MessageConstant.ACCOUNT_DISABLED); } else if (!securityUser.isAccountNonLocked()) { throw new LockedException(MessageConstant.ACCOUNT_LOCKED); } else if (!securityUser.isAccountNonExpired()) { throw new AccountExpiredException(MessageConstant.ACCOUNT_EXPIRED); } else if (!securityUser.isCredentialsNonExpired()) { throw new CredentialsExpiredException(MessageConstant.CREDENTIALS_EXPIRED); } return securityUser; } } ``` - 添加认证服务相关配置`Oauth2ServerConfig`,需要配置加载用户信息的服务`UserServiceImpl`及RSA的钥匙对`KeyPair`; ```java /** * 认证服务器配置 * Created by macro on 2020/6/19. */ @AllArgsConstructor @Configuration @EnableAuthorizationServer public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter { private final PasswordEncoder passwordEncoder; private final UserServiceImpl userDetailsService; private final AuthenticationManager authenticationManager; private final JwtTokenEnhancer jwtTokenEnhancer; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("client-app") .secret(passwordEncoder.encode("123456")) .scopes("all") .authorizedGrantTypes("password", "refresh_token") .accessTokenValiditySeconds(3600) .refreshTokenValiditySeconds(86400); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { TokenEnhancerChain enhancerChain = new TokenEnhancerChain(); List delegates = new ArrayList<>(); delegates.add(jwtTokenEnhancer); delegates.add(accessTokenConverter()); enhancerChain.setTokenEnhancers(delegates); //配置JWT的内容增强器 endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService) //配置加载用户信息的服务 .accessTokenConverter(accessTokenConverter()) .tokenEnhancer(enhancerChain); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.allowFormAuthenticationForClients(); } @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); jwtAccessTokenConverter.setKeyPair(keyPair()); return jwtAccessTokenConverter; } @Bean public KeyPair keyPair() { //从classpath下的证书中获取秘钥对 KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray()); return keyStoreKeyFactory.getKeyPair("jwt", "123456".toCharArray()); } } ``` - 如果你想往JWT中添加自定义信息的话,比如说`登录用户的ID`,可以自己实现`TokenEnhancer`接口; ```java /** * JWT内容增强器 * Created by macro on 2020/6/19. */ @Component public class JwtTokenEnhancer implements TokenEnhancer { @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { SecurityUser securityUser = (SecurityUser) authentication.getPrincipal(); Map info = new HashMap<>(); //把用户ID设置到JWT中 info.put("id", securityUser.getId()); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info); return accessToken; } } ``` - 由于我们的网关服务需要RSA的公钥来验证签名是否合法,所以认证服务需要有个接口把公钥暴露出来; ```java /** * 获取RSA公钥接口 * Created by macro on 2020/6/19. */ @RestController public class KeyPairController { @Autowired private KeyPair keyPair; @GetMapping("/rsa/publicKey") public Map getKey() { RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAKey key = new RSAKey.Builder(publicKey).build(); return new JWKSet(key).toJSONObject(); } } ``` - 不要忘了还需要配置Spring Security,允许获取公钥接口的访问; ```java /** * SpringSecurity配置 * Created by macro on 2020/6/19. */ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll() .antMatchers("/rsa/publicKey").permitAll() .anyRequest().authenticated(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } ``` - 创建一个资源服务`ResourceServiceImpl`,初始化的时候把资源与角色匹配关系缓存到Redis中,方便网关服务进行鉴权的时候获取。 ```java /** * 资源与角色匹配关系管理业务类 * Created by macro on 2020/6/19. */ @Service public class ResourceServiceImpl { private Map> resourceRolesMap; @Autowired private RedisTemplate redisTemplate; @PostConstruct public void initData() { resourceRolesMap = new TreeMap<>(); resourceRolesMap.put("/api/hello", CollUtil.toList("ADMIN")); resourceRolesMap.put("/api/user/currentUser", CollUtil.toList("ADMIN", "TEST")); redisTemplate.opsForHash().putAll(RedisConstant.RESOURCE_ROLES_MAP, resourceRolesMap); } } ``` ### micro-oauth2-gateway > 接下来我们就可以搭建网关服务了,它将作为Oauth2的资源服务、客户端服务使用,对访问微服务的请求进行统一的校验认证和鉴权操作。 - 在`pom.xml`中添加相关依赖,主要是Gateway、Oauth2和JWT相关依赖; ```xml org.springframework.boot spring-boot-starter-webflux org.springframework.cloud spring-cloud-starter-gateway org.springframework.security spring-security-config org.springframework.security spring-security-oauth2-resource-server org.springframework.security spring-security-oauth2-client org.springframework.security spring-security-oauth2-jose com.nimbusds nimbus-jose-jwt 8.16 ``` - 在`application.yml`中添加相关配置,主要是路由规则的配置、Oauth2中RSA公钥的配置及路由白名单的配置; ```yaml server: port: 9201 spring: profiles: active: dev application: name: micro-oauth2-gateway cloud: nacos: discovery: server-addr: localhost:8848 gateway: routes: #配置路由规则 - id: oauth2-api-route uri: lb://micro-oauth2-api predicates: - Path=/api/** filters: - StripPrefix=1 - id: oauth2-auth-route uri: lb://micro-oauth2-auth predicates: - Path=/auth/** filters: - StripPrefix=1 discovery: locator: enabled: true #开启从注册中心动态创建路由的功能 lower-case-service-id: true #使用小写服务名,默认是大写 security: oauth2: resourceserver: jwt: jwk-set-uri: 'http://localhost:9401/rsa/publicKey' #配置RSA的公钥访问地址 redis: database: 0 port: 6379 host: localhost password: secure: ignore: urls: #配置白名单路径 - "/actuator/**" - "/auth/oauth/token" ``` - 对网关服务进行配置安全配置,由于Gateway使用的是`WebFlux`,所以需要使用`@EnableWebFluxSecurity`注解开启; ```java /** * 资源服务器配置 * Created by macro on 2020/6/19. */ @AllArgsConstructor @Configuration @EnableWebFluxSecurity public class ResourceServerConfig { private final AuthorizationManager authorizationManager; private final IgnoreUrlsConfig ignoreUrlsConfig; private final RestfulAccessDeniedHandler restfulAccessDeniedHandler; private final RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Bean public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http.oauth2ResourceServer().jwt() .jwtAuthenticationConverter(jwtAuthenticationConverter()); http.authorizeExchange() .pathMatchers(ArrayUtil.toArray(ignoreUrlsConfig.getUrls(),String.class)).permitAll()//白名单配置 .anyExchange().access(authorizationManager)//鉴权管理器配置 .and().exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler)//处理未授权 .authenticationEntryPoint(restAuthenticationEntryPoint)//处理未认证 .and().csrf().disable(); return http.build(); } @Bean public Converter> jwtAuthenticationConverter() { JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); jwtGrantedAuthoritiesConverter.setAuthorityPrefix(AuthConstant.AUTHORITY_PREFIX); jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(AuthConstant.AUTHORITY_CLAIM_NAME); JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter(); jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter); return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter); } } ``` - 在`WebFluxSecurity`中自定义鉴权操作需要实现`ReactiveAuthorizationManager`接口; ```java /** * 鉴权管理器,用于判断是否有资源的访问权限 * Created by macro on 2020/6/19. */ @Component public class AuthorizationManager implements ReactiveAuthorizationManager { @Autowired private RedisTemplate redisTemplate; @Override public Mono check(Mono mono, AuthorizationContext authorizationContext) { //从Redis中获取当前路径可访问角色列表 URI uri = authorizationContext.getExchange().getRequest().getURI(); Object obj = redisTemplate.opsForHash().get(RedisConstant.RESOURCE_ROLES_MAP, uri.getPath()); List authorities = Convert.toList(String.class,obj); authorities = authorities.stream().map(i -> i = AuthConstant.AUTHORITY_PREFIX + i).collect(Collectors.toList()); //认证通过且角色匹配的用户可访问当前路径 return mono .filter(Authentication::isAuthenticated) .flatMapIterable(Authentication::getAuthorities) .map(GrantedAuthority::getAuthority) .any(authorities::contains) .map(AuthorizationDecision::new) .defaultIfEmpty(new AuthorizationDecision(false)); } } ``` - 这里我们还需要实现一个全局过滤器`AuthGlobalFilter`,当鉴权通过后将JWT令牌中的用户信息解析出来,然后存入请求的Header中,这样后续服务就不需要解析JWT令牌了,可以直接从请求的Header中获取到用户信息。 ```java /** * 将登录用户的JWT转化成用户信息的全局过滤器 * Created by macro on 2020/6/17. */ @Component public class AuthGlobalFilter implements GlobalFilter, Ordered { private static Logger LOGGER = LoggerFactory.getLogger(AuthGlobalFilter.class); @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getHeaders().getFirst("Authorization"); if (StrUtil.isEmpty(token)) { return chain.filter(exchange); } try { //从token中解析用户信息并设置到Header中去 String realToken = token.replace("Bearer ", ""); JWSObject jwsObject = JWSObject.parse(realToken); String userStr = jwsObject.getPayload().toString(); LOGGER.info("AuthGlobalFilter.filter() user:{}",userStr); ServerHttpRequest request = exchange.getRequest().mutate().header("user", userStr).build(); exchange = exchange.mutate().request(request).build(); } catch (ParseException e) { e.printStackTrace(); } return chain.filter(exchange); } @Override public int getOrder() { return 0; } } ``` ### micro-oauth2-api > 最后我们搭建一个API服务,它不会集成和实现任何安全相关逻辑,全靠网关来保护它。 - 在`pom.xml`中添加相关依赖,就添加了一个web依赖; ```xml org.springframework.boot spring-boot-starter-web ``` - 在`application.yml`添加相关配置,很常规的配置; ```yaml server: port: 9501 spring: profiles: active: dev application: name: micro-oauth2-api cloud: nacos: discovery: server-addr: localhost:8848 management: endpoints: web: exposure: include: "*" ``` - 创建一个测试接口,网关验证通过即可访问; ```java /** * 测试接口 * Created by macro on 2020/6/19. */ @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "Hello World."; } } ``` - 创建一个`LoginUserHolder`组件,用于从请求的Header中直接获取登录用户信息; ```java /** * 获取登录用户信息 * Created by macro on 2020/6/17. */ @Component public class LoginUserHolder { public UserDTO getCurrentUser(){ //从Header中获取用户信息 ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = servletRequestAttributes.getRequest(); String userStr = request.getHeader("user"); JSONObject userJsonObject = new JSONObject(userStr); UserDTO userDTO = new UserDTO(); userDTO.setUsername(userJsonObject.getStr("user_name")); userDTO.setId(Convert.toLong(userJsonObject.get("id"))); userDTO.setRoles(Convert.toList(String.class,userJsonObject.get("authorities"))); return userDTO; } } ``` - 创建一个获取当前用户信息的接口。 ```java /** * 获取登录用户信息接口 * Created by macro on 2020/6/19. */ @RestController @RequestMapping("/user") public class UserController{ @Autowired private LoginUserHolder loginUserHolder; @GetMapping("/currentUser") public UserDTO currentUser() { return loginUserHolder.getCurrentUser(); } } ``` ## 功能演示 > 接下来我们来演示下微服务系统中的统一认证鉴权功能,所有请求均通过网关访问。 - 在此之前先启动我们的Nacos和Redis服务,然后依次启动`micro-oauth2-auth`、`micro-oauth2-gateway`及`micro-oauth2-api`服务; ![](../images/gateway_oauth2_01.png) - 使用密码模式获取JWT令牌,访问地址:http://localhost:9201/auth/oauth/token ![](../images/gateway_oauth2_02.png) - 使用获取到的JWT令牌访问需要权限的接口,访问地址:http://localhost:9201/api/hello ![](../images/gateway_oauth2_03.png) - 使用获取到的JWT令牌访问获取当前登录用户信息的接口,访问地址:http://localhost:9201/api/user/currentUser ![](../images/gateway_oauth2_04.png) - 当JWT令牌过期时,使用refresh_token获取新的JWT令牌,访问地址:http://localhost:9201/auth/oauth/token ![](../images/gateway_oauth2_05.png) - 使用没有访问权限的`andy`账号登录,访问接口时会返回如下信息,访问地址:http://localhost:9201/api/hello ![](../images/gateway_oauth2_06.png) ## 项目源码地址 https://github.com/macrozheng/springcloud-learning/tree/master/micro-oauth2 ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/hystrix.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Hystrix:服务容错保护 > Spring Cloud Hystrix 是Spring Cloud Netflix 子项目的核心组件之一,具有服务容错及线程隔离等一系列服务保护功能,本文将对其用法进行详细介绍。 ## Hystrix 简介 在微服务架构中,服务与服务之间通过远程调用的方式进行通信,一旦某个被调用的服务发生了故障,其依赖服务也会发生故障,此时就会发生故障的蔓延,最终导致系统瘫痪。Hystrix实现了断路器模式,当某个服务发生故障时,通过断路器的监控,给调用方返回一个错误响应,而不是长时间的等待,这样就不会使得调用方由于长时间得不到响应而占用线程,从而防止故障的蔓延。Hystrix具备服务降级、服务熔断、线程隔离、请求缓存、请求合并及服务监控等强大功能。 ## 创建一个hystrix-service模块 > 这里我们创建一个hystrix-service模块来演示hystrix的常用功能。 ### 在pom.xml中添加相关依赖 ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.boot spring-boot-starter-web ``` ### 在application.yml进行配置 > 主要是配置了端口、注册中心地址及user-service的调用路径。 ```yaml server: port: 8401 spring: application: name: hystrix-service eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8001/eureka/ service-url: user-service: http://user-service ``` ### 在启动类上添加@EnableCircuitBreaker来开启Hystrix的断路器功能 ```java @EnableCircuitBreaker @EnableDiscoveryClient @SpringBootApplication public class HystrixServiceApplication { public static void main(String[] args) { SpringApplication.run(HystrixServiceApplication.class, args); } ``` ### 创建UserHystrixController接口用于调用user-service服务 ## 服务降级演示 - 在UserHystrixController中添加用于测试服务降级的接口: ```java @GetMapping("/testFallback/{id}") public CommonResult testFallback(@PathVariable Long id) { return userService.getUser(id); } ``` - 在UserService中添加调用方法与服务降级方法,方法上需要添加@HystrixCommand注解: ```java @HystrixCommand(fallbackMethod = "getDefaultUser") public CommonResult getUser(Long id) { return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id); } public CommonResult getDefaultUser(@PathVariable Long id) { User defaultUser = new User(-1L, "defaultUser", "123456"); return new CommonResult<>(defaultUser); } ``` - 启动eureka-server、user-service、hystrix-service服务; ![](../images/springcloud_hystrix_01.png) - 调用接口进行测试:[http://localhost:8401/user/testFallback/1](http://localhost:8401/user/testFallback/1) ![](../images/springcloud_hystrix_02.png) - 关闭user-service服务重新测试该接口,发现已经发生了服务降级: ![](../images/springcloud_hystrix_03.png) ## @HystrixCommand详解 ### @HystrixCommand中的常用参数 - fallbackMethod:指定服务降级处理方法; - ignoreExceptions:忽略某些异常,不发生服务降级; - commandKey:命令名称,用于区分不同的命令; - groupKey:分组名称,Hystrix会根据不同的分组来统计命令的告警及仪表盘信息; - threadPoolKey:线程池名称,用于划分线程池。 ### 设置命令、分组及线程池名称 - 在UserHystrixController中添加测试接口: ```java @GetMapping("/testCommand/{id}") public CommonResult testCommand(@PathVariable Long id) { return userService.getUserCommand(id); } ``` - 在UserService中添加方式实现功能: ```java @HystrixCommand(fallbackMethod = "getDefaultUser", commandKey = "getUserCommand", groupKey = "getUserGroup", threadPoolKey = "getUserThreadPool") public CommonResult getUserCommand(@PathVariable Long id) { return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id); } ``` ### 使用ignoreExceptions忽略某些异常降级 - 在UserHystrixController中添加测试接口: ```java @GetMapping("/testException/{id}") public CommonResult testException(@PathVariable Long id) { return userService.getUserException(id); } ``` - 在UserService中添加实现方法,这里忽略了NullPointerException,当id为1时抛出IndexOutOfBoundsException,id为2时抛出NullPointerException: ```java @HystrixCommand(fallbackMethod = "getDefaultUser2", ignoreExceptions = {NullPointerException.class}) public CommonResult getUserException(Long id) { if (id == 1) { throw new IndexOutOfBoundsException(); } else if (id == 2) { throw new NullPointerException(); } return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id); } public CommonResult getDefaultUser2(@PathVariable Long id, Throwable e) { LOGGER.error("getDefaultUser2 id:{},throwable class:{}", id, e.getClass()); User defaultUser = new User(-2L, "defaultUser2", "123456"); return new CommonResult<>(defaultUser); } ``` - 调用接口进行测试:[http://localhost:8401/user/tesException/1](http://localhost:8401/user/tesException/1) ![](../images/springcloud_hystrix_04.png) - 调用接口进行测试:[http://localhost:8401/user/tesException/1](http://localhost:8401/user/tesException/2) ![](../images/springcloud_hystrix_05.png) ## Hystrix的请求缓存 > 当系统并发量越来越大时,我们需要使用缓存来优化系统,达到减轻并发请求线程数,提供响应速度的效果。 ### 相关注解 - @CacheResult:开启缓存,默认所有参数作为缓存的key,cacheKeyMethod可以通过返回String类型的方法指定key; - @CacheKey:指定缓存的key,可以指定参数或指定参数中的属性值为缓存key,cacheKeyMethod还可以通过返回String类型的方法指定; - @CacheRemove:移除缓存,需要指定commandKey。 ### 测试使用缓存 - 在UserHystrixController中添加使用缓存的测试接口,直接调用三次getUserCache方法: ```java @GetMapping("/testCache/{id}") public CommonResult testCache(@PathVariable Long id) { userService.getUserCache(id); userService.getUserCache(id); userService.getUserCache(id); return new CommonResult("操作成功", 200); } ``` - 在UserService中添加具有缓存功能的getUserCache方法: ```java @CacheResult(cacheKeyMethod = "getCacheKey") @HystrixCommand(fallbackMethod = "getDefaultUser", commandKey = "getUserCache") public CommonResult getUserCache(Long id) { LOGGER.info("getUserCache id:{}", id); return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id); } /** * 为缓存生成key的方法 */ public String getCacheKey(Long id) { return String.valueOf(id); } ``` - 调用接口测试[http://localhost:8401/user/testCache/1](http://localhost:8401/user/testCache/1),这个接口中调用了三次getUserCache方法,但是只打印了一次日志,说明有两次走的是缓存: ![](../images/springcloud_hystrix_06.png) ### 测试移除缓存 - 在UserHystrixController中添加移除缓存的测试接口,调用一次removeCache方法: ```java @GetMapping("/testRemoveCache/{id}") public CommonResult testRemoveCache(@PathVariable Long id) { userService.getUserCache(id); userService.removeCache(id); userService.getUserCache(id); return new CommonResult("操作成功", 200); } ``` - 在UserService中添加具有移除缓存功能的removeCache方法: ```java @CacheRemove(commandKey = "getUserCache", cacheKeyMethod = "getCacheKey") @HystrixCommand public CommonResult removeCache(Long id) { LOGGER.info("removeCache id:{}", id); return restTemplate.postForObject(userServiceUrl + "/user/delete/{1}", null, CommonResult.class, id); } ``` - 调用接口测试[http://localhost:8401/user/testRemoveCache/1](http://localhost:8401/user/testRemoveCache/1),可以发现有两次查询都走的是接口: ![](../images/springcloud_hystrix_07.png) ### 缓存使用过程中的问题 - 在缓存使用过程中,我们需要在每次使用缓存的请求前后对HystrixRequestContext进行初始化和关闭,否则会出现如下异常: ```java java.lang.IllegalStateException: Request caching is not available. Maybe you need to initialize the HystrixRequestContext? at com.netflix.hystrix.HystrixRequestCache.get(HystrixRequestCache.java:104) ~[hystrix-core-1.5.18.jar:1.5.18] at com.netflix.hystrix.AbstractCommand$7.call(AbstractCommand.java:478) ~[hystrix-core-1.5.18.jar:1.5.18] at com.netflix.hystrix.AbstractCommand$7.call(AbstractCommand.java:454) ~[hystrix-core-1.5.18.jar:1.5.18] ``` - 这里我们通过使用过滤器,在每个请求前后初始化和关闭HystrixRequestContext来解决该问题: ```java /** * Created by macro on 2019/9/4. */ @Component @WebFilter(urlPatterns = "/*",asyncSupported = true) public class HystrixRequestContextFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { filterChain.doFilter(servletRequest, servletResponse); } finally { context.close(); } } } ``` ## 请求合并 > 微服务系统中的服务间通信,需要通过远程调用来实现,随着调用次数越来越多,占用线程资源也会越来越多。Hystrix中提供了@HystrixCollapser用于合并请求,从而达到减少通信消耗及线程数量的效果。 ### @HystrixCollapser的常用属性 - batchMethod:用于设置请求合并的方法; - collapserProperties:请求合并属性,用于控制实例属性,有很多; - timerDelayInMilliseconds:collapserProperties中的属性,用于控制每隔多少时间合并一次请求; ### 功能演示 - 在UserHystrixController中添加testCollapser方法,这里我们先进行两次服务调用,再间隔200ms以后进行第三次服务调用: ```java @GetMapping("/testCollapser") public CommonResult testCollapser() throws ExecutionException, InterruptedException { Future future1 = userService.getUserFuture(1L); Future future2 = userService.getUserFuture(2L); future1.get(); future2.get(); ThreadUtil.safeSleep(200); Future future3 = userService.getUserFuture(3L); future3.get(); return new CommonResult("操作成功", 200); } ``` - 使用@HystrixCollapser实现请求合并,所有对getUserFuture的的多次调用都会转化为对getUserByIds的单次调用: ```java @HystrixCollapser(batchMethod = "getUserByIds",collapserProperties = { @HystrixProperty(name = "timerDelayInMilliseconds", value = "100") }) public Future getUserFuture(Long id) { return new AsyncResult(){ @Override public User invoke() { CommonResult commonResult = restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id); Map data = (Map) commonResult.getData(); User user = BeanUtil.mapToBean(data,User.class,true); LOGGER.info("getUserById username:{}", user.getUsername()); return user; } }; } @HystrixCommand public List getUserByIds(List ids) { LOGGER.info("getUserByIds:{}", ids); CommonResult commonResult = restTemplate.getForObject(userServiceUrl + "/user/getUserByIds?ids={1}", CommonResult.class, CollUtil.join(ids,",")); return (List) commonResult.getData(); } ``` - 访问接口测试[http://localhost:8401/user/testCollapser](http://localhost:8401/user/testCollapser),由于我们设置了100毫秒进行一次请求合并,前两次被合并,最后一次自己单独合并了。 ![](../images/springcloud_hystrix_08.png) ## Hystrix的常用配置 ### 全局配置 ```yaml hystrix: command: #用于控制HystrixCommand的行为 default: execution: isolation: strategy: THREAD #控制HystrixCommand的隔离策略,THREAD->线程池隔离策略(默认),SEMAPHORE->信号量隔离策略 thread: timeoutInMilliseconds: 1000 #配置HystrixCommand执行的超时时间,执行超过该时间会进行服务降级处理 interruptOnTimeout: true #配置HystrixCommand执行超时的时候是否要中断 interruptOnCancel: true #配置HystrixCommand执行被取消的时候是否要中断 timeout: enabled: true #配置HystrixCommand的执行是否启用超时时间 semaphore: maxConcurrentRequests: 10 #当使用信号量隔离策略时,用来控制并发量的大小,超过该并发量的请求会被拒绝 fallback: enabled: true #用于控制是否启用服务降级 circuitBreaker: #用于控制HystrixCircuitBreaker的行为 enabled: true #用于控制断路器是否跟踪健康状况以及熔断请求 requestVolumeThreshold: 20 #超过该请求数的请求会被拒绝 forceOpen: false #强制打开断路器,拒绝所有请求 forceClosed: false #强制关闭断路器,接收所有请求 requestCache: enabled: true #用于控制是否开启请求缓存 collapser: #用于控制HystrixCollapser的执行行为 default: maxRequestsInBatch: 100 #控制一次合并请求合并的最大请求数 timerDelayinMilliseconds: 10 #控制多少毫秒内的请求会被合并成一个 requestCache: enabled: true #控制合并请求是否开启缓存 threadpool: #用于控制HystrixCommand执行所在线程池的行为 default: coreSize: 10 #线程池的核心线程数 maximumSize: 10 #线程池的最大线程数,超过该线程数的请求会被拒绝 maxQueueSize: -1 #用于设置线程池的最大队列大小,-1采用SynchronousQueue,其他正数采用LinkedBlockingQueue queueSizeRejectionThreshold: 5 #用于设置线程池队列的拒绝阀值,由于LinkedBlockingQueue不能动态改版大小,使用时需要用该参数来控制线程数 ``` ### 实例配置 > 实例配置只需要将全局配置中的default换成与之对应的key即可。 ```yaml hystrix: command: HystrixComandKey: #将default换成HystrixComrnandKey execution: isolation: strategy: THREAD collapser: HystrixCollapserKey: #将default换成HystrixCollapserKey maxRequestsInBatch: 100 threadpool: HystrixThreadPoolKey: #将default换成HystrixThreadPoolKey coreSize: 10 ``` #### 配置文件中相关key的说明 - HystrixComandKey对应@HystrixCommand中的commandKey属性; - HystrixCollapserKey对应@HystrixCollapser注解中的collapserKey属性; - HystrixThreadPoolKey对应@HystrixCommand中的threadPoolKey属性。 ## 使用到的模块 ``` lua springcloud-learning ├── eureka-server -- eureka注册中心 ├── user-service -- 提供User对象CRUD接口的服务 └── hystrix-service -- hystrix服务调用测试服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/hystrix_dashboard.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Hystrix Dashboard:断路器执行监控 > Hystrix Dashboard 是Spring Cloud中查看Hystrix实例执行情况的一种仪表盘组件,支持查看单个实例和查看集群实例,本文将对其用法进行详细介绍。 ## 简介 Hystrix提供了Hystrix Dashboard来实时监控HystrixCommand方法的执行情况。 Hystrix Dashboard可以有效地反映出每个Hystrix实例的运行情况,帮助我们快速发现系统中的问题,从而采取对应措施。 ## Hystrix 单个实例监控 > 我们先通过使用Hystrix Dashboard监控单个Hystrix实例来了解下它的使用方法。 ### 创建一个hystrix-dashboard模块 > 用来监控hystrix实例的执行情况。 - 在pom.xml中添加相关依赖: ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-netflix-hystrix-dashboard org.springframework.boot spring-boot-starter-actuator ``` - 在application.yml进行配置: ```yaml server: port: 8501 spring: application: name: hystrix-dashboard eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8001/eureka/ ``` - 在启动类上添加@EnableHystrixDashboard来启用监控功能: ```java @EnableHystrixDashboard @EnableDiscoveryClient @SpringBootApplication public class HystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class, args); } } ``` ### 启动相关服务 > 这次我们需要启动如下服务:eureka-server、user-service、hystrix-service、hystrix-dashboard,启动后注册中心显示如下。 ![](../images/springcloud_hystrix_09.png) ### Hystrix实例监控演示 - 访问Hystrix Dashboard:[http://localhost:8501/hystrix](http://localhost:8501/hystrix) ![](../images/springcloud_hystrix_10.png) - 填写好信息后点击监控按钮,这里我们需要注意的是,由于我们本地不支持https,所以我们的地址需要填入的是http,否则会无法获取监控信息; ![](../images/springcloud_hystrix_11.png) - 还有一点值得注意的是,被监控的hystrix-service服务需要开启Actuator的hystrix.stream端点,配置信息如下: ```yaml management: endpoints: web: exposure: include: 'hystrix.stream' #暴露hystrix监控端点 ``` - 调用几次hystrix-service的接口:[http://localhost:8401/user/testCommand/1](http://localhost:8401/user/testCommand/1) ![](../images/springcloud_hystrix_12.png) - 可以发现曾经我们在@HystrixCommand中添加的commandKey和threadPoolKey属性都显示在上面了,并且有7次调用都成功了。 ### Hystrix Dashboard 图表解读 > 图表解读如下,需要注意的是,小球代表该实例健康状态及流量情况,颜色越显眼,表示实例越不健康,小球越大,表示实例流量越大。曲线表示Hystrix实例的实时流量变化。 ![](../images/springcloud_hystrix_13.png) ## Hystrix 集群实例监控 > 这里我们使用Turbine来聚合hystrix-service服务的监控信息,然后我们的hystrix-dashboard服务就可以从Turbine获取聚合好的监控信息展示给我们了。 ### 创建一个turbine-service模块 > 用来聚合hystrix-service的监控信息。 - 在pom.xml中添加相关依赖: ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-netflix-turbine org.springframework.boot spring-boot-starter-actuator ``` - 在application.yml进行配置,主要是添加了Turbine相关配置: ```yaml server: port: 8601 spring: application: name: turbine-service eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8001/eureka/ turbine: app-config: hystrix-service #指定需要收集信息的服务名称 cluster-name-expression: new String('default') #指定服务所属集群 combine-host-port: true #以主机名和端口号来区分服务 ``` - 在启动类上添加@EnableTurbine来启用Turbine相关功能: ```java @EnableTurbine @EnableDiscoveryClient @SpringBootApplication public class TurbineServiceApplication { public static void main(String[] args) { SpringApplication.run(TurbineServiceApplication.class, args); } } ``` ### 启动相关服务 > 使用application-replica1.yml配置再启动一个hystrix-service服务,启动turbine-service服务,此时注册中心显示如下。 ![](../images/springcloud_hystrix_14.png) ### Hystrix集群监控演示 - 访问Hystrix Dashboard:[http://localhost:8501/hystrix](http://localhost:8501/hystrix) - 添加集群监控地址,需要注意的是我们需要添加的是turbine-service的监控端点地址: ![](../images/springcloud_hystrix_15.png) - 调用几次hystrix-service的接口:[http://localhost:8401/user/testCommand/1](http://localhost:8401/user/testCommand/1)以及[http://localhost:8402/user/testCommand/1](http://localhost:8402/user/testCommand/1) ![](../images/springcloud_hystrix_16.png) - 可以看到我们的Hystrix实例数量变成了两个。 ## 使用到的模块 ``` lua springcloud-learning ├── eureka-server -- eureka注册中心 ├── user-service -- 提供User对象CRUD接口的服务 ├── hystrix-service -- hystrix服务调用测试服务 ├── turbine-service -- 聚合收集hystrix实例监控信息的服务 └── hystrix-dashboard -- 展示hystrix实例监控信息的仪表盘 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/knife4j_cloud.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 微服务聚合Swagger文档,这波操作是真的香! > 记得我的`mall-swarm`微服务项目中,没有做API文档聚合,访问每个服务的API文档都需要访问单独的`swagger-ui.html`页面,既然我们使用了微服务,就应该有统一的API文档入口,最近发现`knife4j`有这方面的支持,本文将详细介绍其实现,希望对大家有所帮助! ## 前置知识 > 我们将采用Nacos作为注册中心,Gateway作为网关,使用`knife4j`来生成API文档,对这些技术不了解的朋友可以看下下面的文章。 - [Spring Cloud Gateway:新一代API网关服务](https://mp.weixin.qq.com/s/bTp4_M3m7rhlhC9wYKExwA) - [Spring Cloud Alibaba:Nacos 作为注册中心和配置中心使用](https://mp.weixin.qq.com/s/N9eAMHuDEJq7kCCJPEEJqw) - [给Swagger换了个新皮肤,瞬间高大上了!](https://mp.weixin.qq.com/s/XbBD0E136F72gI6OkVIZeA) ## 应用架构 > 我们理想的解决方案应该是这样的,网关作为API文档的统一入口,网关聚合所有微服务的文档,通过在网关进行切换来实现对其他服务API文档的访问。 相关服务划分: - micro-knife4j-gateway:网关服务,作为微服务API文档的访问入口,聚合所有API文档,需要引入文档前端UI包; - micro-knife4j-user:用户服务,普通API服务,不需要引入文档前端UI包; - micro-knife4j-order:订单服务,普通API服务,不需要引入文档前端UI包。 ## 具体实现 > 下面详细介绍下Spring Cloud Gateway + knife4j 聚合API文档的具体实现,依次搭建用户服务、订单服务和网关服务。 ### micro-knife4j-user > 我们首先来搭建用户服务,一个普通的API服务,很简单,仅需三步即可集成knife4j。 - 在`pom.xml`中添加相关依赖,一个SpringBoot的web功能依赖,knife4j的微服务依赖(不包含API文档的前端UI包); ```xml org.springframework.boot spring-boot-starter-web com.github.xiaoymin knife4j-micro-spring-boot-starter ``` - 在`application.yml`这添加相关配置,配置一下Nacos注册中心即可; ```yaml server: port: 9501 spring: profiles: active: dev application: name: micro-knife4j-user cloud: nacos: discovery: server-addr: localhost:8848 management: endpoints: web: exposure: include: "*" ``` - 添加Swagger相关配置,非常常规的配置,添加`@EnableKnife4j`注解开启knife4j的增强功能。 ```java /** * Swagger API相关配置 */ @Configuration @EnableSwagger2 @EnableKnife4j public class Swagger2Config { @Bean public Docket createRestApi(){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.macro.cloud.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("micro-knife4j-user") .description("用户服务API文档") .contact("macro") .version("1.0") .build(); } } ``` ### micro-knife4j-order > 我们接下来搭建订单服务,一个普通的API服务,直接参考上面用户服务的搭建即可。 ### micro-knife4j-gateway > 最后我们搭建网关服务,作为微服务API文档的的统一入口,聚合所有微服务的API文档。 - 在`pom.xml`中添加相关依赖,Gateway相关依赖和knife4j的Starter(包含API文档的前端UI包); ```xml org.springframework.cloud spring-cloud-starter-gateway com.github.xiaoymin knife4j-spring-boot-starter ``` - 在`application.yml`这添加相关配置,配置一下Nacos注册中心,用户服务和订单服务的路由即可; ```yaml server: port: 9201 spring: profiles: active: dev application: name: micro-knife4j-gateway cloud: nacos: discovery: server-addr: localhost:8848 gateway: routes: #配置路由路径 - id: user-service uri: lb://micro-knife4j-user predicates: - Path=/user-service/** filters: - StripPrefix=1 - id: order-service uri: lb://micro-knife4j-order predicates: - Path=/order-service/** filters: - StripPrefix=1 discovery: locator: enabled: true #开启从注册中心动态创建路由的功能 lower-case-service-id: true #使用小写服务名,默认是大写 ``` - 在网关上添加Swagger资源配置,用于聚合其他微服务中Swagger的`api-docs`访问路径; ```java /** * Swagger资源配置 * Created by macro on 2020/7/9. */ @Slf4j @Component @Primary @AllArgsConstructor public class SwaggerResourceConfig implements SwaggerResourcesProvider { private final RouteLocator routeLocator; private final GatewayProperties gatewayProperties; @Override public List get() { List resources = new ArrayList<>(); List routes = new ArrayList<>(); //获取所有路由的ID routeLocator.getRoutes().subscribe(route -> routes.add(route.getId())); //过滤出配置文件中定义的路由->过滤出Path Route Predicate->根据路径拼接成api-docs路径->生成SwaggerResource gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> { route.getPredicates().stream() .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName())) .forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(), predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0") .replace("**", "v2/api-docs")))); }); return resources; } private SwaggerResource swaggerResource(String name, String location) { log.info("name:{},location:{}", name, location); SwaggerResource swaggerResource = new SwaggerResource(); swaggerResource.setName(name); swaggerResource.setLocation(location); swaggerResource.setSwaggerVersion("2.0"); return swaggerResource; } } ``` - 什么是Swagger的`api-docs`访问路径?该路径会返回JSON格式数据,Swagger渲染API文档页面的所有数据就是来源于此,比如我们的用户服务会返回如下信息,访问地址:http://localhost:9201/user-service/v2/api-docs ![](../images/knife4j_cloud_01.png) - 接下来我们需要自定义Swagger各个配置的节点,简单来说就是自定义Swagger内部的各个获取数据的接口; ```java /** * 自定义Swagger的各个配置节点 * Created by macro on 2020/7/9. */ @RestController public class SwaggerHandler { @Autowired(required = false) private SecurityConfiguration securityConfiguration; @Autowired(required = false) private UiConfiguration uiConfiguration; private final SwaggerResourcesProvider swaggerResources; @Autowired public SwaggerHandler(SwaggerResourcesProvider swaggerResources) { this.swaggerResources = swaggerResources; } /** * Swagger安全配置,支持oauth和apiKey设置 */ @GetMapping("/swagger-resources/configuration/security") public Mono> securityConfiguration() { return Mono.just(new ResponseEntity<>( Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK)); } /** * Swagger UI配置 */ @GetMapping("/swagger-resources/configuration/ui") public Mono> uiConfiguration() { return Mono.just(new ResponseEntity<>( Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK)); } /** * Swagger资源配置,微服务中这各个服务的api-docs信息 */ @GetMapping("/swagger-resources") public Mono swaggerResources() { return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); } } ``` - 比如说`swagger-resources`这个接口,可用于获取所有微服务的`api-docs`访问路径,获取信息如下,访问地址:http://localhost:9201/swagger-resources ![](../images/knife4j_cloud_02.png) ## 功能演示 > 接下来我们来演示下微服务API文档聚合的功能,仅需要访问网关的API文档页面即可,可自行切换到相关服务的API文档。 - 在此之前先启动我们的Nacos注册中心,然后依次启动`micro-knife4j-user`、`micro-knife4j-order`及`micro-knife4j-gateway`服务; ![](../images/knife4j_cloud_03.png) - 从网关访问API文档,访问地址:http://localhost:9201/doc.html ![](../images/knife4j_cloud_04.png) - 我们通过左上角的切换组件即可切换到对应服务的API文档; ![](../images/knife4j_cloud_05.png) - 查看API文档,我们可以发现所有接口都已经添加了对应的访问前缀,可以正常访问。 ![](../images/knife4j_cloud_06.png) ## 切换回Swagger UI > 如果你不想使用knife4j的界面,想用原来的Swagger界面,也是可以支持的,切换方法非常简单,下面我们来讲讲。 - 首先我们需要在`pom.xml`中去除knife4j的相关依赖,主要就是下面两个依赖; ```xml com.github.xiaoymin knife4j-micro-spring-boot-starter com.github.xiaoymin knife4j-spring-boot-starter ``` - 在`pom.xml`中添加Swagger相关依赖,并去除原来使用的`@EnableKnife4j`注解; ```xml io.springfox springfox-swagger2 2.9.2 io.springfox springfox-swagger-ui 2.9.2 io.swagger swagger-models 1.6.0 io.swagger swagger-annotations 1.6.0 ``` - 重新启动所有服务,访问网关的API文档路径即可查看:http://localhost:9201/swagger-ui.html ![](../images/knife4j_cloud_07.png) ## 总结 对比knife4j和原生Swagger的微服务使用,再次证明knife4j是springfox-swagger的增强UI实现,完全遵循了springfox-swagger中的使用方式。 ## 参考资料 官方文档:https://doc.xiaominfo.com/guide/ui-front-gateway.html ## 项目源码地址 https://github.com/macrozheng/springcloud-learning/tree/master/micro-knife4j ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/nacos.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Alibaba:Nacos 作为注册中心和配置中心使用 > Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案,Nacos 作为其核心组件之一,可以作为注册中心和配置中心使用,本文将对其用法进行详细介绍。 ## Nacos简介 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。 Nacos 具有如下特性: - 服务发现和服务健康监测:支持基于DNS和基于RPC的服务发现,支持对服务的实时的健康检查,阻止向不健康的主机或服务实例发送请求; - 动态配置服务:动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置; - 动态 DNS 服务:动态 DNS 服务支持权重路由,让您更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务; - 服务及其元数据管理:支持从微服务平台建设的视角管理数据中心的所有服务及元数据。 ## 使用Nacos作为注册中心 ### 安装并运行Nacos - 我们先从官网下载Nacos,这里下载的是`nacos-server-1.1.4.zip`文件,下载地址:https://github.com/alibaba/nacos/releases - 配置`JAVA_HOME`环境变量,不配置会导致无法运行Nacos; ```bash JAVA_HOME=D:\developer\env\Java\jdk1.8.0_91 ``` - 解压安装包,直接运行`bin`目录下的`startup.cmd`; - 运行成功后,访问`http://localhost:8848/nacos`可以查看Nacos的主页,默认账号密码都是nacos。 ![](../images/spingcloud_nacos_01.png) ### 创建应用注册到Nacos > 我们通过改造consul-user-service和consul-ribbon-service来演示下服务注册与发现的功能,主要是将应用原来的Consul注册中心支持改为Nacos注册中心支持。 - 创建nacos-user-service模块和nacos-ribbon-service模块; - 如果要使用Spring Cloud Alibaba 的组件都需要在pom.xml中添加如下的配置; ```xml com.alibaba.cloud spring-cloud-alibaba-dependencies 2.1.0.RELEASE pom import ``` - 修改相关依赖,把原来的Consul注册发现的依赖改为Nacos的: ```xml com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ``` - 修改配置文件application.yml,将Consul的注册发现配置改为Nacos的: ```yaml server: port: 8206 spring: application: name: nacos-user-service cloud: nacos: discovery: server-addr: localhost:8848 #配置Nacos地址 management: endpoints: web: exposure: include: '*' ``` - 运行两个nacos-user-service和一个nacos-ribbon-service,在Nacos页面上可以看到如下信息: ![](../images/spingcloud_nacos_02.png) ### 负载均衡功能 > 由于我们运行了两个nacos-user-service,而nacos-ribbon-service默认会去调用它的接口,我们调用nacos-ribbon-service的接口来演示下负载均衡功能。 多次调用接口:http://localhost:8308/user/1 ,可以发现两个nacos-user-service的控制台交替打印如下信息。 ```bash 2019-11-06 14:28:06.458 INFO 12092 --- [nio-8207-exec-2] c.macro.cloud.controller.UserController : 根据id获取用户信息,用户名称为:macro ``` ## 使用Nacos作为配置中心 > 我们通过创建nacos-config-client模块,并在Nacos页面中添加配置信息来演示下配置管理的功能。 ### 创建nacos-config-client模块 - 在pom.xml中添加相关依赖: ```xml com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ``` - 添加配置文件application.yml,启用的是dev环境的配置: ```yaml spring: profiles: active: dev ``` - 添加配置文件bootstrap.yml,主要是对Nacos的作为配置中心的功能进行配置: ```yaml server: port: 9101 spring: application: name: nacos-config-client cloud: nacos: discovery: server-addr: localhost:8848 #Nacos地址 config: server-addr: localhost:8848 #Nacos地址 file-extension: yaml #这里我们获取的yaml格式的配置 ``` - 创建ConfigClientController,从Nacos配置中心中获取配置信息: ```java /** * Created by macro on 2019/9/11. */ @RestController @RefreshScope public class ConfigClientController { @Value("${config.info}") private String configInfo; @GetMapping("/configInfo") public String getConfigInfo() { return configInfo; } } ``` ### 在Nacos中添加配置 - 我们先来讲下Nacos中的dataid的组成格式及与SpringBoot配置文件中的属性对应关系: ```bash ${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} ``` - 比如说我们现在要获取应用名称为`nacos-config-client`的应用在`dev`环境下的`yaml`配置,dataid如下: ```bash nacos-config-client-dev.yaml ``` - 按照以上dataid添加如下配置: ```yaml config: info: "config info for dev" ``` - 填写配置示意图: ![](../images/spingcloud_nacos_03.png) - 启动nacos-config-client,调用接口查看配置信息:http://localhost:9101/configInfo ```bash config info for dev ``` ## Nacos的动态刷新配置 我们只要修改下Nacos中的配置信息,再次调用查看配置的接口,就会发现配置已经刷新,Nacos和Consul一样都支持动态刷新配置。当我们在Nacos页面上修改配置并发布后,应用会刷新配置并打印如下信息 ```bash 2019-11-06 14:50:49.460 INFO 12372 --- [-localhost_8848] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$ec395f8e] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 2019-11-06 14:50:49.608 INFO 12372 --- [-localhost_8848] c.a.c.n.c.NacosPropertySourceBuilder : Loading nacos data, dataId: 'nacos-config-client-dev.yaml', group: 'DEFAULT_GROUP' 2019-11-06 14:50:49.609 INFO 12372 --- [-localhost_8848] b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource {name='NACOS', propertySources=[NacosPropertySource {name='nacos-config-client-dev.yaml'}, NacosPropertySource {name='nacos-config-client.yaml'}]} 2019-11-06 14:50:49.610 INFO 12372 --- [-localhost_8848] o.s.boot.SpringApplication : The following profiles are active: dev 2019-11-06 14:50:49.620 INFO 12372 --- [-localhost_8848] o.s.boot.SpringApplication : Started application in 0.328 seconds (JVM running for 172.085) 2019-11-06 14:50:49.638 INFO 12372 --- [-localhost_8848] o.s.c.e.event.RefreshEventListener : Refresh keys changed: [config.info] ``` ## 参考资料 Spring Cloud Alibaba 官方文档:https://github.com/alibaba/spring-cloud-alibaba/wiki ## 使用到的模块 ```lua springcloud-learning ├── nacos-config-client -- 用于演示nacos作为配置中心的nacos客户端 ├── nacos-user-service -- 注册到nacos的提供User对象CRUD接口的服务 └── nacos-ribbon-service -- 注册到nacos的ribbon服务调用测试服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/oauth2.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Security:Oauth2使用入门 > Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2可以实现单点登录、令牌中继、令牌交换等功能,本文将对其结合Oauth2入门使用进行详细介绍。 ## OAuth2 简介 OAuth 2.0是用于授权的行业标准协议。OAuth 2.0为简化客户端开发提供了特定的授权流,包括Web应用、桌面应用、移动端应用等。 ## OAuth2 相关名词解释 - Resource owner(资源拥有者):拥有该资源的最终用户,他有访问资源的账号密码; - Resource server(资源服务器):拥有受保护资源的服务器,如果请求包含正确的访问令牌,可以访问资源; - Client(客户端):访问资源的客户端,会使用访问令牌去获取资源服务器的资源,可以是浏览器、移动设备或者服务器; - Authorization server(认证服务器):用于认证用户的服务器,如果客户端认证通过,发放访问资源服务器的令牌。 ## 四种授权模式 - Authorization Code(授权码模式):正宗的OAuth2的授权模式,客户端先将用户导向认证服务器,登录后获取授权码,然后进行授权,最后根据授权码获取访问令牌; - Implicit(简化模式):和授权码模式相比,取消了获取授权码的过程,直接获取访问令牌; - Resource Owner Password Credentials(密码模式):客户端直接向用户获取用户名和密码,之后向认证服务器获取访问令牌; - Client Credentials(客户端模式):客户端直接通过客户端认证(比如client_id和client_secret)从认证服务器获取访问令牌。 ## 两种常用的授权模式 ### 授权码模式 ![](../images/spingcloud_security_01.png) - (A)客户端将用户导向认证服务器; - (B)用户在认证服务器进行登录并授权; - (C)认证服务器返回授权码给客户端; - (D)客户端通过授权码和跳转地址向认证服务器获取访问令牌; - (E)认证服务器发放访问令牌(有需要带上刷新令牌)。 ### 密码模式 ![](../images/spingcloud_security_02.png) - (A)客户端从用户获取用户名和密码; - (B)客户端通过用户的用户名和密码访问认证服务器; - (C)认证服务器返回访问令牌(有需要带上刷新令牌)。 ## Oauth2的使用 ### 创建oauth2-server模块 > 这里我们创建一个oauth2-server模块作为认证服务器来使用。 - 在pom.xml中添加相关依赖: ```xml org.springframework.cloud spring-cloud-starter-oauth2 org.springframework.cloud spring-cloud-starter-security org.springframework.boot spring-boot-starter-web ``` - 在application.yml中进行配置: ```yaml server: port: 9401 spring: application: name: oauth2-service ``` - 添加UserService实现UserDetailsService接口,用于加载用户信息: ```java /** * Created by macro on 2019/9/30. */ @Service public class UserService implements UserDetailsService { private List userList; @Autowired private PasswordEncoder passwordEncoder; @PostConstruct public void initData() { String password = passwordEncoder.encode("123456"); userList = new ArrayList<>(); userList.add(new User("macro", password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"))); userList.add(new User("andy", password, AuthorityUtils.commaSeparatedStringToAuthorityList("client"))); userList.add(new User("mark", password, AuthorityUtils.commaSeparatedStringToAuthorityList("client"))); } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { List findUserList = userList.stream().filter(user -> user.getUsername().equals(username)).collect(Collectors.toList()); if (!CollectionUtils.isEmpty(findUserList)) { return findUserList.get(0); } else { throw new UsernameNotFoundException("用户名或密码错误"); } } } ``` - 添加认证服务器配置,使用@EnableAuthorizationServer注解开启: ```java /** * 认证服务器配置 * Created by macro on 2019/9/30. */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserService userService; /** * 使用密码模式需要配置 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.authenticationManager(authenticationManager) .userDetailsService(userService); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("admin")//配置client_id .secret(passwordEncoder.encode("admin123456"))//配置client_secret .accessTokenValiditySeconds(3600)//配置访问token的有效期 .refreshTokenValiditySeconds(864000)//配置刷新token的有效期 .redirectUris("http://www.baidu.com")//配置redirect_uri,用于授权成功后跳转 .scopes("all")//配置申请的权限范围 .authorizedGrantTypes("authorization_code","password");//配置grant_type,表示授权类型 } } ``` - 添加资源服务器配置,使用@EnableResourceServer注解开启: ```java /** * 资源服务器配置 * Created by macro on 2019/9/30. */ @Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest() .authenticated() .and() .requestMatchers() .antMatchers("/user/**");//配置需要保护的资源路径 } } ``` - 添加SpringSecurity配置,允许认证相关路径的访问及表单登录: ```java /** * SpringSecurity配置 * Created by macro on 2019/10/8. */ @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override public void configure(HttpSecurity http) throws Exception { http.csrf() .disable() .authorizeRequests() .antMatchers("/oauth/**", "/login/**", "/logout/**") .permitAll() .anyRequest() .authenticated() .and() .formLogin() .permitAll(); } } ``` - 添加需要登录的接口用于测试: ```java /** * Created by macro on 2019/9/30. */ @RestController @RequestMapping("/user") public class UserController { @GetMapping("/getCurrentUser") public Object getCurrentUser(Authentication authentication) { return authentication.getPrincipal(); } } ``` ### 授权码模式使用 - 启动oauth2-server服务; - 在浏览器访问该地址进行登录授权:http://localhost:9401/oauth/authorize?response_type=code&client_id=admin&redirect_uri=http://www.baidu.com&scope=all&state=normal - 输入账号密码进行登录操作: ![](../images/spingcloud_security_03.png) - 登录后进行授权操作: ![](../images/spingcloud_security_04.png) - 之后会浏览器会带着授权码跳转到我们指定的路径: ```bash https://www.baidu.com/?code=eTsADY&state=normal ``` - 使用授权码请求该地址获取访问令牌:http://localhost:9401/oauth/token - 使用Basic认证通过client_id和client_secret构造一个Authorization头信息; ![](../images/spingcloud_security_05.png) - 在body中添加以下参数信息,通过POST请求获取访问令牌; ![](../images/spingcloud_security_06.png) - 在请求头中添加访问令牌,访问需要登录认证的接口进行测试,发现已经可以成功访问:http://localhost:9401/user/getCurrentUser ![](../images/spingcloud_security_07.png) ### 密码模式使用 - 使用密码请求该地址获取访问令牌:http://localhost:9401/oauth/token - 使用Basic认证通过client_id和client_secret构造一个Authorization头信息; ![](../images/spingcloud_security_05.png) - 在body中添加以下参数信息,通过POST请求获取访问令牌; ![](../images/spingcloud_security_08.png) ## 使用到的模块 ```lua springcloud-learning └── oauth2-server -- oauth2认证测试服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/oauth2_custom.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 我扒了半天源码,终于找到了Oauth2自定义处理结果的最佳方案! > 在[《微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!》](https://mp.weixin.qq.com/s/npyZsa4p30PLULxjskxKSA)一文中我们介绍了Oauth2在微服务中的使用,但是我们没有自定义Oauth2默认的处理结果。有时候我们真的很希望Oauth2中的认证授权能返回我们指定格式的结果,比如登录认证的结果、网关鉴权不通过的结果等等。本文将详细介绍Oauth2中自定义处理结果的方案,希望对大家有所帮助! ## 解决什么问题 > 自定义Oauth2处理结果,主要是为了统一接口返回信息的格式,从下面几个方面着手。 - 自定义Oauth2登录认证成功和失败的返回结果; - JWT令牌过期或者签名不正确,网关认证失败的返回结果; - 携带过期或者签名不正确的JWT令牌访问白名单接口,网关直接认证失败。 ## 自定义登录认证结果 ### 认证成功返回结果 - 我们先来看看默认的返回结果,访问Oauth2登录认证接口:http://localhost:9201/auth/oauth/token ![](../images/oauth2_custom_01.png) - 我们之前使用的都是统一的通用返回结果`CommonResult`,Oauth2的这个结果显然不符合,需要统一下,通用返回结果格式如下; ```java /** * 通用返回对象 * Created by macro on 2019/4/19. */ public class CommonResult { private long code; private String message; private T data; } ``` - 其实我们只要找到一个关键类就可以自定义Oauth2的登录认证接口了,它就是`org.springframework.security.oauth2.provider.endpoint.TokenEndpoint`,其中定义了我们非常熟悉的登录认证接口,我们只要自己重写登录认证接口,直接调用默认的实现逻辑,然后把默认返回的结果处理下即可,下面是默认的实现逻辑; ```java @FrameworkEndpoint public class TokenEndpoint extends AbstractEndpoint { @RequestMapping(value = "/oauth/token", method=RequestMethod.POST) public ResponseEntity postAccessToken(Principal principal, @RequestParam Map parameters) throws HttpRequestMethodNotSupportedException { if (!(principal instanceof Authentication)) { throw new InsufficientAuthenticationException( "There is no client authentication. Try adding an appropriate authentication filter."); } String clientId = getClientId(principal); ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId); TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient); if (clientId != null && !clientId.equals("")) { // Only validate the client details if a client authenticated during this // request. if (!clientId.equals(tokenRequest.getClientId())) { // double check to make sure that the client ID in the token request is the same as that in the // authenticated client throw new InvalidClientException("Given client ID does not match authenticated client"); } } if (authenticatedClient != null) { oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient); } if (!StringUtils.hasText(tokenRequest.getGrantType())) { throw new InvalidRequestException("Missing grant type"); } if (tokenRequest.getGrantType().equals("implicit")) { throw new InvalidGrantException("Implicit grant type not supported from token endpoint"); } if (isAuthCodeRequest(parameters)) { // The scope was requested or determined during the authorization step if (!tokenRequest.getScope().isEmpty()) { logger.debug("Clearing scope of incoming token request"); tokenRequest.setScope(Collections. emptySet()); } } if (isRefreshTokenRequest(parameters)) { // A refresh token has its own default scopes, so we should ignore any added by the factory here. tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE))); } OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest); if (token == null) { throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType()); } return getResponse(token); } } ``` - 我们将需要的JWT信息封装成对象,然后放入到我们的通用返回结果的`data`属性中去; ```java /** * Oauth2获取Token返回信息封装 * Created by macro on 2020/7/17. */ @Data @EqualsAndHashCode(callSuper = false) @Builder public class Oauth2TokenDto { /** * 访问令牌 */ private String token; /** * 刷新令牌 */ private String refreshToken; /** * 访问令牌头前缀 */ private String tokenHead; /** * 有效时间(秒) */ private int expiresIn; } ``` - 创建一个`AuthController`,自定义实现Oauth2默认的登录认证接口; ```java /** * 自定义Oauth2获取令牌接口 * Created by macro on 2020/7/17. */ @RestController @RequestMapping("/oauth") public class AuthController { @Autowired private TokenEndpoint tokenEndpoint; /** * Oauth2登录认证 */ @RequestMapping(value = "/token", method = RequestMethod.POST) public CommonResult postAccessToken(Principal principal, @RequestParam Map parameters) throws HttpRequestMethodNotSupportedException { OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody(); Oauth2TokenDto oauth2TokenDto = Oauth2TokenDto.builder() .token(oAuth2AccessToken.getValue()) .refreshToken(oAuth2AccessToken.getRefreshToken().getValue()) .expiresIn(oAuth2AccessToken.getExpiresIn()) .tokenHead("Bearer ").build(); return CommonResult.success(oauth2TokenDto); } } ``` - 再次调用登录认证接口,我们可以发现返回结果已经变成了符合我们通用返回结果的格式了! ![](../images/oauth2_custom_02.png) ### 认证失败返回结果 - 认证成功的结果统一了,认证失败的结果我们也得统一下吧,先来看下原来认证失败的结果; ![](../images/oauth2_custom_03.png) - 我们仔细查看下登录认证的默认实现可以发现,很多认证失败的操作都会直接抛出`OAuth2Exception`异常,对于在`Controller`中抛出的异常,我们可以使用`@ControllerAdvice`注解来进行全局处理; ```java /** * 全局处理Oauth2抛出的异常 * Created by macro on 2020/7/17. */ @ControllerAdvice public class Oauth2ExceptionHandler { @ResponseBody @ExceptionHandler(value = OAuth2Exception.class) public CommonResult handleOauth2(OAuth2Exception e) { return CommonResult.failed(e.getMessage()); } } ``` - 当我们输错密码,再次调用登录认证接口时,发现认证失败的结果也统一了。 ![](../images/oauth2_custom_04.png) ## 自定义网关鉴权失败结果 - 当我们使用过期或签名不正确的JWT令牌访问需要权限的接口时,会直接返回状态码`401`; ![](../images/oauth2_custom_05.png) - 这个返回结果不符合我们的通用结果格式,其实我们想要的是返回状态码为`200`,然后返回如下格式信息; ```json { "code": 401, "data": "Jwt expired at 2020-07-10T08:38:40Z", "message": "暂未登录或token已经过期" } ``` - 这里有个非常简单的改法,只需添加一行代码,修改网关的安全配置`ResourceServerConfig`,设置好资源服务器的`ServerAuthenticationEntryPoint`即可; ```java /** * 资源服务器配置 * Created by macro on 2020/6/19. */ @AllArgsConstructor @Configuration @EnableWebFluxSecurity public class ResourceServerConfig { private final AuthorizationManager authorizationManager; private final IgnoreUrlsConfig ignoreUrlsConfig; private final RestfulAccessDeniedHandler restfulAccessDeniedHandler; private final RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Bean public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http.oauth2ResourceServer().jwt() .jwtAuthenticationConverter(jwtAuthenticationConverter()); //自定义处理JWT请求头过期或签名错误的结果(新添加的) http.oauth2ResourceServer().authenticationEntryPoint(restAuthenticationEntryPoint); http.authorizeExchange() .pathMatchers(ArrayUtil.toArray(ignoreUrlsConfig.getUrls(),String.class)).permitAll()//白名单配置 .anyExchange().access(authorizationManager)//鉴权管理器配置 .and().exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler)//处理未授权 .authenticationEntryPoint(restAuthenticationEntryPoint)//处理未认证 .and().csrf().disable(); return http.build(); } } ``` - 添加完成后,再次访问需要权限的接口,就会返回我们想要的结果了。 ![](../images/oauth2_custom_06.png) ## 兼容白名单接口 - 其实对于白名单接口一直有个问题,当携带过期或签名不正确的JWT令牌访问时,会直接返回token过期的结果,我们可以访问下登录认证接口试试; ![](../images/oauth2_custom_07.png) - 明明就是个白名单接口,只不过携带的token不对就不让访问了,显然有点不合理。如何解决呢,我们先看看不带token访问怎么样; ![](../images/oauth2_custom_08.png) - 其实我们只要在Oauth2默认的认证过滤器前面再加个过滤器,如果是白名单接口,直接移除认证头即可,首先定义好我们的过滤器; ```java /** * 白名单路径访问时需要移除JWT请求头 * Created by macro on 2020/7/24. */ @Component public class IgnoreUrlsRemoveJwtFilter implements WebFilter { @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Override public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); URI uri = request.getURI(); PathMatcher pathMatcher = new AntPathMatcher(); //白名单路径移除JWT请求头 List ignoreUrls = ignoreUrlsConfig.getUrls(); for (String ignoreUrl : ignoreUrls) { if (pathMatcher.match(ignoreUrl, uri.getPath())) { request = exchange.getRequest().mutate().header("Authorization", "").build(); exchange = exchange.mutate().request(request).build(); return chain.filter(exchange); } } return chain.filter(exchange); } } ``` - 然后把这个过滤器配置到默认的认证过滤器之前即可,在ResourceServerConfig中进行配置; ```java /** * 资源服务器配置 * Created by macro on 2020/6/19. */ @AllArgsConstructor @Configuration @EnableWebFluxSecurity public class ResourceServerConfig { private final AuthorizationManager authorizationManager; private final IgnoreUrlsConfig ignoreUrlsConfig; private final RestfulAccessDeniedHandler restfulAccessDeniedHandler; private final RestAuthenticationEntryPoint restAuthenticationEntryPoint; private final IgnoreUrlsRemoveJwtFilter ignoreUrlsRemoveJwtFilter; @Bean public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http.oauth2ResourceServer().jwt() .jwtAuthenticationConverter(jwtAuthenticationConverter()); //自定义处理JWT请求头过期或签名错误的结果 http.oauth2ResourceServer().authenticationEntryPoint(restAuthenticationEntryPoint); //对白名单路径,直接移除JWT请求头(新添加的) http.addFilterBefore(ignoreUrlsRemoveJwtFilter, SecurityWebFiltersOrder.AUTHENTICATION); http.authorizeExchange() .pathMatchers(ArrayUtil.toArray(ignoreUrlsConfig.getUrls(),String.class)).permitAll()//白名单配置 .anyExchange().access(authorizationManager)//鉴权管理器配置 .and().exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler)//处理未授权 .authenticationEntryPoint(restAuthenticationEntryPoint)//处理未认证 .and().csrf().disable(); return http.build(); } } ``` - 携带过期请求头再次访问,发现已经可以正常访问了。 ![](../images/oauth2_custom_09.png) ## 总结 至此,微服务中使用Oauth2实现统一认证和鉴权方案终于完善了! ## 项目源码地址 https://github.com/macrozheng/springcloud-learning/tree/master/micro-oauth2 ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/oauth2_jwt.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Security:Oauth2结合JWT使用 > Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2还可以实现更多功能,比如使用JWT令牌存储信息,刷新令牌功能,本文将对其结合JWT使用进行详细介绍。 ## JWT简介 > JWT是JSON WEB TOKEN的缩写,它是基于 RFC 7519 标准定义的一种可以安全传输的的JSON对象,由于使用了数字签名,所以是可信任和安全的。 ### JWT的组成 - JWT token的格式:header.payload.signature; - header中用于存放签名的生成算法; ```json { "alg": "HS256", "typ": "JWT" } ``` - payload中用于存放数据,比如过期时间、用户名、用户所拥有的权限等; ```json { "exp": 1572682831, "user_name": "macro", "authorities": [ "admin" ], "jti": "c1a0645a-28b5-4468-b4c7-9623131853af", "client_id": "admin", "scope": [ "all" ] } ``` - signature为以header和payload生成的签名,一旦header和payload被篡改,验证将失败。 ### JWT实例 - 这是一个JWT的字符串: ``` eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzI2ODI4MzEsInVzZXJfbmFtZSI6Im1hY3JvIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiYzFhMDY0NWEtMjhiNS00NDY4LWI0YzctOTYyMzEzMTg1M2FmIiwiY2xpZW50X2lkIjoiYWRtaW4iLCJzY29wZSI6WyJhbGwiXX0.x4i6sRN49R6JSjd5hd1Fr2DdEMBsYdC4KB6Uw1huXPg ``` - 可以在该网站上获得解析结果:https://jwt.io/ ![](../images/spingcloud_security_12.png) ## 创建oauth2-jwt-server模块 该模块只是对oauth2-server模块的扩展,直接复制过来扩展下下即可。 ## oauth2中存储令牌的方式 > 在上一节中我们都是把令牌存储在内存中的,这样如果部署多个服务,就会导致无法使用令牌的问题。 Spring Cloud Security中有两种存储令牌的方式可用于解决该问题,一种是使用Redis来存储,另一种是使用JWT来存储。 ### 使用Redis存储令牌 - 在pom.xml中添加Redis相关依赖: ```xml org.springframework.boot spring-boot-starter-data-redis ``` - 在application.yml中添加redis相关配置: ```yaml spring: redis: #redis相关配置 password: 123456 #有密码时设置 ``` - 添加在Redis中存储令牌的配置: ```java /** * 使用redis存储token的配置 * Created by macro on 2019/10/8. */ @Configuration public class RedisTokenStoreConfig { @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public TokenStore redisTokenStore (){ return new RedisTokenStore(redisConnectionFactory); } } ``` - 在认证服务器配置中指定令牌的存储策略为Redis: ```java /** * 认证服务器配置 * Created by macro on 2019/9/30. */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserService userService; @Autowired @Qualifier("redisTokenStore") private TokenStore tokenStore; /** * 使用密码模式需要配置 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.authenticationManager(authenticationManager) .userDetailsService(userService) .tokenStore(tokenStore);//配置令牌存储策略 } //省略代码... } ``` - 运行项目后使用密码模式来获取令牌,访问如下地址:http://localhost:9401/oauth/token ![](../images/spingcloud_security_09.png) - 进行获取令牌操作,可以发现令牌已经被存储到Redis中。 ![](../images/spingcloud_security_10.png) ### 使用JWT存储令牌 - 添加使用JWT存储令牌的配置: ```java /** * 使用Jwt存储token的配置 * Created by macro on 2019/10/8. */ @Configuration public class JwtTokenStoreConfig { @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter(); accessTokenConverter.setSigningKey("test_key");//配置JWT使用的秘钥 return accessTokenConverter; } } ``` - 在认证服务器配置中指定令牌的存储策略为JWT: ```java /** * 认证服务器配置 * Created by macro on 2019/9/30. */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserService userService; @Autowired @Qualifier("jwtTokenStore") private TokenStore tokenStore; @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; @Autowired private JwtTokenEnhancer jwtTokenEnhancer; /** * 使用密码模式需要配置 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.authenticationManager(authenticationManager) .userDetailsService(userService) .tokenStore(tokenStore) //配置令牌存储策略 .accessTokenConverter(jwtAccessTokenConverter); } //省略代码... } ``` - 运行项目后使用密码模式来获取令牌,访问如下地址:http://localhost:9401/oauth/token ![](../images/spingcloud_security_11.png) - 发现获取到的令牌已经变成了JWT令牌,将access_token拿到https://jwt.io/ 网站上去解析下可以获得其中内容。 ```json { "exp": 1572682831, "user_name": "macro", "authorities": [ "admin" ], "jti": "c1a0645a-28b5-4468-b4c7-9623131853af", "client_id": "admin", "scope": [ "all" ] } ``` ## 扩展JWT中存储的内容 > 有时候我们需要扩展JWT中存储的内容,这里我们在JWT中扩展一个key为`enhance`,value为`enhance info`的数据。 - 继承TokenEnhancer实现一个JWT内容增强器: ```java /** * Jwt内容增强器 * Created by macro on 2019/10/8. */ public class JwtTokenEnhancer implements TokenEnhancer { @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { Map info = new HashMap<>(); info.put("enhance", "enhance info"); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info); return accessToken; } } ``` - 创建一个JwtTokenEnhancer实例: ```java /** * 使用Jwt存储token的配置 * Created by macro on 2019/10/8. */ @Configuration public class JwtTokenStoreConfig { //省略代码... @Bean public JwtTokenEnhancer jwtTokenEnhancer() { return new JwtTokenEnhancer(); } } ``` - 在认证服务器配置中配置JWT的内容增强器: ```java /** * 认证服务器配置 * Created by macro on 2019/9/30. */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserService userService; @Autowired @Qualifier("jwtTokenStore") private TokenStore tokenStore; @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; @Autowired private JwtTokenEnhancer jwtTokenEnhancer; /** * 使用密码模式需要配置 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { TokenEnhancerChain enhancerChain = new TokenEnhancerChain(); List delegates = new ArrayList<>(); delegates.add(jwtTokenEnhancer); //配置JWT的内容增强器 delegates.add(jwtAccessTokenConverter); enhancerChain.setTokenEnhancers(delegates); endpoints.authenticationManager(authenticationManager) .userDetailsService(userService) .tokenStore(tokenStore) //配置令牌存储策略 .accessTokenConverter(jwtAccessTokenConverter) .tokenEnhancer(enhancerChain); } //省略代码... } ``` - 运行项目后使用密码模式来获取令牌,之后对令牌进行解析,发现已经包含扩展的内容。 ```json { "user_name": "macro", "scope": [ "all" ], "exp": 1572683821, "authorities": [ "admin" ], "jti": "1ed1b0d8-f4ea-45a7-8375-211001a51a9e", "client_id": "admin", "enhance": "enhance info" } ``` ## Java中解析JWT中的内容 > 如果我们需要获取JWT中的信息,可以使用一个叫jjwt的工具包。 - 在pom.xml中添加相关依赖: ```xml io.jsonwebtoken jjwt 0.9.0 ``` - 修改UserController类,使用jjwt工具类来解析Authorization头中存储的JWT内容。 ```java /** * Created by macro on 2019/9/30. */ @RestController @RequestMapping("/user") public class UserController { @GetMapping("/getCurrentUser") public Object getCurrentUser(Authentication authentication, HttpServletRequest request) { String header = request.getHeader("Authorization"); String token = StrUtil.subAfter(header, "bearer ", false); return Jwts.parser() .setSigningKey("test_key".getBytes(StandardCharsets.UTF_8)) .parseClaimsJws(token) .getBody(); } } ``` - 将令牌放入`Authorization`头中,访问如下地址获取信息:http://localhost:9401/user/getCurrentUser ![](../images/spingcloud_security_13.png) ## 刷新令牌 > 在Spring Cloud Security 中使用oauth2时,如果令牌失效了,可以使用刷新令牌通过refresh_token的授权模式再次获取access_token。 - 只需修改认证服务器的配置,添加refresh_token的授权模式即可。 ````java /** * 认证服务器配置 * Created by macro on 2019/9/30. */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("admin") .secret(passwordEncoder.encode("admin123456")) .accessTokenValiditySeconds(3600) .refreshTokenValiditySeconds(864000) .redirectUris("http://www.baidu.com") .autoApprove(true) //自动授权配置 .scopes("all") .authorizedGrantTypes("authorization_code","password","refresh_token"); //添加授权模式 } } ```` - 使用刷新令牌模式来获取新的令牌,访问如下地址:http://localhost:9401/oauth/token ![](../images/spingcloud_security_14.png) ## 使用到的模块 ```lua springcloud-learning └── oauth2-jwt-server -- 使用jwt的oauth2认证测试服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/oauth2_sso.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Security:Oauth2实现单点登录 > Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2可以实现单点登录功能,本文将对其单点登录用法进行详细介绍。 ## 单点登录简介 单点登录(Single Sign On)指的是当有多个系统需要登录时,用户只需登录一个系统,就可以访问其他需要登录的系统而无需登录。 ## 创建oauth2-client模块 > 这里我们创建一个oauth2-client服务作为需要登录的客户端服务,使用上一节中的oauth2-jwt-server服务作为认证服务,当我们在oauth2-jwt-server服务上登录以后,就可以直接访问oauth2-client需要登录的接口,来演示下单点登录功能。 - 在pom.xml中添加相关依赖: ```xml org.springframework.cloud spring-cloud-starter-oauth2 org.springframework.cloud spring-cloud-starter-security org.springframework.boot spring-boot-starter-web io.jsonwebtoken jjwt 0.9.0 ``` - 在application.yml中进行配置: ```yaml server: port: 9501 servlet: session: cookie: name: OAUTH2-CLIENT-SESSIONID #防止Cookie冲突,冲突会导致登录验证不通过 oauth2-server-url: http://localhost:9401 spring: application: name: oauth2-client security: oauth2: #与oauth2-server对应的配置 client: client-id: admin client-secret: admin123456 user-authorization-uri: ${oauth2-server-url}/oauth/authorize access-token-uri: ${oauth2-server-url}/oauth/token resource: jwt: key-uri: ${oauth2-server-url}/oauth/token_key ``` - 在启动类上添加@EnableOAuth2Sso注解来启用单点登录功能: ```java @EnableOAuth2Sso @SpringBootApplication public class Oauth2ClientApplication { public static void main(String[] args) { SpringApplication.run(Oauth2ClientApplication.class, args); } } ``` - 添加接口用于获取当前登录用户信息: ```java /** * Created by macro on 2019/9/30. */ @RestController @RequestMapping("/user") public class UserController { @GetMapping("/getCurrentUser") public Object getCurrentUser(Authentication authentication) { return authentication; } } ``` ## 修改认证服务器配置 修改oauth2-jwt-server模块中的AuthorizationServerConfig类,将绑定的跳转路径为[http://localhost:9501/login](http://localhost:9501/login),并添加获取秘钥时的身份认证。 ```java /** * 认证服务器配置 * Created by macro on 2019/9/30. */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { //以上省略一堆代码... @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("admin") .secret(passwordEncoder.encode("admin123456")) .accessTokenValiditySeconds(3600) .refreshTokenValiditySeconds(864000) // .redirectUris("http://www.baidu.com") .redirectUris("http://localhost:9501/login") //单点登录时配置 .scopes("all") .authorizedGrantTypes("authorization_code","password","refresh_token"); } @Override public void configure(AuthorizationServerSecurityConfigurer security) { security.tokenKeyAccess("isAuthenticated()"); // 获取密钥需要身份认证,使用单点登录时必须配置 } } ``` ## 网页单点登录演示 - 启动oauth2-client服务和oauth2-jwt-server服务; - 访问客户端需要授权的接口[http://localhost:9501/user/getCurrentUser](http://localhost:9501/user/getCurrentUser)会跳转到授权服务的登录界面; ![](../images/spingcloud_security_15.png) - 进行登录操作后跳转到授权页面; ![](../images/spingcloud_security_16.png) - 授权后会跳转到原来需要权限的接口地址,展示登录用户信息; ![](../images/spingcloud_security_17.png) - 如果需要跳过授权操作进行自动授权可以添加`autoApprove(true)`配置: ```java /** * 认证服务器配置 * Created by macro on 2019/9/30. */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { //以上省略一堆代码... @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("admin") .secret(passwordEncoder.encode("admin123456")) .accessTokenValiditySeconds(3600) .refreshTokenValiditySeconds(864000) // .redirectUris("http://www.baidu.com") .redirectUris("http://localhost:9501/login") //单点登录时配置 .autoApprove(true) //自动授权配置 .scopes("all") .authorizedGrantTypes("authorization_code","password","refresh_token"); } } ``` ## 调用接口单点登录演示 > 这里我们使用Postman来演示下如何使用正确的方式调用需要登录的客户端接口。 - 访问客户端需要登录的接口:http://localhost:9501/user/getCurrentUser - 使用Oauth2认证方式获取访问令牌: ![](../images/spingcloud_security_18.png) - 输入获取访问令牌的相关信息,点击请求令牌: ![](../images/spingcloud_security_19.png) - 此时会跳转到认证服务器进行登录操作: ![](../images/spingcloud_security_20.png) - 登录成功后使用获取到的令牌: ![](../images/spingcloud_security_21.png) - 最后请求接口可以获取到如下信息: ```json { "authorities": [ { "authority": "admin" } ], "details": { "remoteAddress": "0:0:0:0:0:0:0:1", "sessionId": "63BB793E35383B2FFC608333B3BF4988", "tokenValue": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJtYWNybyIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE1NzI2OTAxNzUsImF1dGhvcml0aWVzIjpbImFkbWluIl0sImp0aSI6IjIwN2U5MTQzLTVjNTUtNDhkMS1iZmU3LTgwMzUyZTQ3Y2QyZCIsImNsaWVudF9pZCI6ImFkbWluIiwiZW5oYW5jZSI6ImVuaGFuY2UgaW5mbyJ9.xf3cBu9yCm0sME0j3UcP53FwF4tJVJC5aJbEj_Y2XcU", "tokenType": "bearer", "decodedDetails": null }, "authenticated": true, "userAuthentication": { "authorities": [ { "authority": "admin" } ], "details": null, "authenticated": true, "principal": "macro", "credentials": "N/A", "name": "macro" }, "clientOnly": false, "oauth2Request": { "clientId": "admin", "scope": [ "all" ], "requestParameters": { "client_id": "admin" }, "resourceIds": [], "authorities": [], "approved": true, "refresh": false, "redirectUri": null, "responseTypes": [], "extensions": {}, "grantType": null, "refreshTokenRequest": null }, "principal": "macro", "credentials": "", "name": "macro" } ``` ## oauth2-client添加权限校验 - 添加配置开启基于方法的权限校验: ```java /** * 在接口上配置权限时使用 * Created by macro on 2019/10/11. */ @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) @Order(101) public class SecurityConfig extends WebSecurityConfigurerAdapter { } ``` - 在UserController中添加需要admin权限的接口: ```java /** * Created by macro on 2019/9/30. */ @RestController @RequestMapping("/user") public class UserController { @PreAuthorize("hasAuthority('admin')") @GetMapping("/auth/admin") public Object adminAuth() { return "Has admin auth!"; } } ``` - 访问需要admin权限的接口:http://localhost:9501/user/auth/admin ![](../images/spingcloud_security_22.png) - 使用没有`admin`权限的帐号,比如`andy:123456`获取令牌后访问该接口,会发现没有权限访问。 ![](../images/spingcloud_security_23.png) ## 使用到的模块 ```lua springcloud-learning ├── oauth2-jwt-server -- 使用jwt的oauth2认证测试服务 └── oauth2-client -- 单点登录的oauth2客户端服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/ribbon.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Ribbon:负载均衡的服务调用 > Spring Cloud Ribbon 是Spring Cloud Netflix 子项目的核心组件之一,主要给服务间调用及API网关转发提供负载均衡的功能,本文将对其用法进行详细介绍。 ## Ribbon简介 在微服务架构中,很多服务都会部署多个,其他服务去调用该服务的时候,如何保证负载均衡是个不得不去考虑的问题。负载均衡可以增加系统的可用性和扩展性,当我们使用RestTemplate来调用其他服务时,Ribbon可以很方便的实现负载均衡功能。 ## RestTemplate的使用 > RestTemplate是一个HTTP客户端,使用它我们可以方便的调用HTTP接口,支持GET、POST、PUT、DELETE等方法。 ### GET请求方法 ```java T getForObject(String url, Class responseType, Object... uriVariables); T getForObject(String url, Class responseType, Map uriVariables); T getForObject(URI url, Class responseType); ResponseEntity getForEntity(String url, Class responseType, Object... uriVariables); ResponseEntity getForEntity(String url, Class responseType, Map uriVariables); ResponseEntity getForEntity(URI var1, Class responseType); ``` #### getForObject方法 返回对象为响应体中数据转化成的对象,举例如下: ```java @GetMapping("/{id}") public CommonResult getUser(@PathVariable Long id) { return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id); } ``` #### getForEntity方法 返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等,举例如下: ```java @GetMapping("/getEntityByUsername") public CommonResult getEntityByUsername(@RequestParam String username) { ResponseEntity entity = restTemplate.getForEntity(userServiceUrl + "/user/getByUsername?username={1}", CommonResult.class, username); if (entity.getStatusCode().is2xxSuccessful()) { return entity.getBody(); } else { return new CommonResult("操作失败", 500); } } ``` ### POST请求方法 ```java T postForObject(String url, @Nullable Object request, Class responseType, Object... uriVariables); T postForObject(String url, @Nullable Object request, Class responseType, Map uriVariables); T postForObject(URI url, @Nullable Object request, Class responseType); ResponseEntity postForEntity(String url, @Nullable Object request, Class responseType, Object... uriVariables); ResponseEntity postForEntity(String url, @Nullable Object request, Class responseType, Map uriVariables); ResponseEntity postForEntity(URI url, @Nullable Object request, Class responseType); ``` #### postForObject示例 ```java @PostMapping("/create") public CommonResult create(@RequestBody User user) { return restTemplate.postForObject(userServiceUrl + "/user/create", user, CommonResult.class); } ``` #### postForEntity示例 ```java @PostMapping("/create") public CommonResult create(@RequestBody User user) { return restTemplate.postForEntity(userServiceUrl + "/user/create", user, CommonResult.class).getBody(); } ``` ### PUT请求方法 ```java void put(String url, @Nullable Object request, Object... uriVariables); void put(String url, @Nullable Object request, Map uriVariables); void put(URI url, @Nullable Object request); ``` #### PUT请求示例 ```java @PutMapping("/update") public CommonResult update(@RequestBody User user) { restTemplate.put(userServiceUrl + "/user/update", user); return new CommonResult("操作成功",200); } ``` ### DELETE请求方法 ```java void delete(String url, Object... uriVariables); void delete(String url, Map uriVariables); void delete(URI url); ``` #### DELETE请求示例 ```java @DeleteMapping("/delete/{id}") public CommonResult delete(@PathVariable Long id) { restTemplate.delete(userServiceUrl + "/user/delete/{1}", null, id); return new CommonResult("操作成功",200); } ``` ## 创建一个user-service模块 > 首先我们创建一个user-service,用于给Ribbon提供服务调用。 ### 在pom.xml中添加相关依赖 ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-web ``` ### 在application.yml进行配置 > 主要是配置了端口和注册中心地址。 ```yaml server: port: 8201 spring: application: name: user-service eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8001/eureka/ ``` ### 添加UserController用于提供调用接口 > UserController类定义了对User对象常见的CRUD接口。 ```java /** * Created by macro on 2019/8/29. */ @RestController @RequestMapping("/user") public class UserController { private Logger LOGGER = LoggerFactory.getLogger(this.getClass()); @Autowired private UserService userService; @PostMapping("/create") public CommonResult create(@RequestBody User user) { userService.create(user); return new CommonResult("操作成功", 200); } @GetMapping("/{id}") public CommonResult getUser(@PathVariable Long id) { User user = userService.getUser(id); LOGGER.info("根据id获取用户信息,用户名称为:{}",user.getUsername()); return new CommonResult<>(user); } @GetMapping("/getUserByIds") public CommonResult> getUserByIds(@RequestParam List ids) { List userList= userService.getUserByIds(ids); LOGGER.info("根据ids获取用户信息,用户列表为:{}",userList); return new CommonResult<>(userList); } @GetMapping("/getByUsername") public CommonResult getByUsername(@RequestParam String username) { User user = userService.getByUsername(username); return new CommonResult<>(user); } @PostMapping("/update") public CommonResult update(@RequestBody User user) { userService.update(user); return new CommonResult("操作成功", 200); } @PostMapping("/delete/{id}") public CommonResult delete(@PathVariable Long id) { userService.delete(id); return new CommonResult("操作成功", 200); } } ``` ## 创建一个ribbon-service模块 > 这里我们创建一个ribbon-service模块来调用user-service模块演示负载均衡的服务调用。 ### 在pom.xml中添加相关依赖 ```xml org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-netflix-ribbon ``` ### 在application.yml进行配置 > 主要是配置了端口、注册中心地址及user-service的调用路径。 ```yaml server: port: 8301 spring: application: name: ribbon-service eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8001/eureka/ service-url: user-service: http://user-service ``` ### 使用@LoadBalanced注解赋予RestTemplate负载均衡的能力 > 可以看出使用Ribbon的负载均衡功能非常简单,和直接使用RestTemplate没什么两样,只需给RestTemplate添加一个@LoadBalanced即可。 ```java /** * Created by macro on 2019/8/29. */ @Configuration public class RibbonConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } } ``` ### 添加UserRibbonController类 > 注入RestTemplate,使用其调用user-service中提供的相关接口,这里对GET和POST调用进行了演示,其他方法调用均可参考。 ```java /** * Created by macro on 2019/8/29. */ @RestController @RequestMapping("/user") public class UserRibbonController { @Autowired private RestTemplate restTemplate; @Value("${service-url.user-service}") private String userServiceUrl; @GetMapping("/{id}") public CommonResult getUser(@PathVariable Long id) { return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id); } @GetMapping("/getByUsername") public CommonResult getByUsername(@RequestParam String username) { return restTemplate.getForObject(userServiceUrl + "/user/getByUsername?username={1}", CommonResult.class, username); } @GetMapping("/getEntityByUsername") public CommonResult getEntityByUsername(@RequestParam String username) { ResponseEntity entity = restTemplate.getForEntity(userServiceUrl + "/user/getByUsername?username={1}", CommonResult.class, username); if (entity.getStatusCode().is2xxSuccessful()) { return entity.getBody(); } else { return new CommonResult("操作失败", 500); } } @PostMapping("/create") public CommonResult create(@RequestBody User user) { return restTemplate.postForObject(userServiceUrl + "/user/create", user, CommonResult.class); } @PostMapping("/update") public CommonResult update(@RequestBody User user) { return restTemplate.postForObject(userServiceUrl + "/user/update", user, CommonResult.class); } @PostMapping("/delete/{id}") public CommonResult delete(@PathVariable Long id) { return restTemplate.postForObject(userServiceUrl + "/user/delete/{1}", null, CommonResult.class, id); } } ``` ## 负载均衡功能演示 - 启动eureka-server于8001端口; - 启动user-service于8201端口; - 启动另一个user-service于8202端口,可以通过修改IDEA中的SpringBoot的启动配置实现: ![](../images/springcloud_ribbon_01.png) - 此时运行中的服务如下: ![](../images/springcloud_ribbon_02.png) - 调用接口进行测试:[http://localhost:8301/user/1](http://localhost:8301/user/1) ![](../images/springcloud_ribbon_05.png) - 可以发现运行在8201和8202的user-service控制台交替打印如下信息: ![](../images/springcloud_ribbon_03.png) ![](../images/springcloud_ribbon_04.png) ## Ribbon的常用配置 ### 全局配置 ```yaml ribbon: ConnectTimeout: 1000 #服务请求连接超时时间(毫秒) ReadTimeout: 3000 #服务请求处理超时时间(毫秒) OkToRetryOnAllOperations: true #对超时请求启用重试机制 MaxAutoRetriesNextServer: 1 #切换重试实例的最大个数 MaxAutoRetries: 1 # 切换实例后重试最大次数 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改负载均衡算法 ``` ### 指定服务进行配置 > 与全局配置的区别就是ribbon节点挂在服务名称下面,如下是对ribbon-service调用user-service时的单独配置。 ```yaml user-service: ribbon: ConnectTimeout: 1000 #服务请求连接超时时间(毫秒) ReadTimeout: 3000 #服务请求处理超时时间(毫秒) OkToRetryOnAllOperations: true #对超时请求启用重试机制 MaxAutoRetriesNextServer: 1 #切换重试实例的最大个数 MaxAutoRetries: 1 # 切换实例后重试最大次数 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改负载均衡算法 ``` ## Ribbon的负载均衡策略 > 所谓的负载均衡策略,就是当A服务调用B服务时,此时B服务有多个实例,这时A服务以何种方式来选择调用的B实例,ribbon可以选择以下几种负载均衡策略。 - com.netflix.loadbalancer.RandomRule:从提供服务的实例中以随机的方式; - com.netflix.loadbalancer.RoundRobinRule:以线性轮询的方式,就是维护一个计数器,从提供服务的实例中按顺序选取,第一次选第一个,第二次选第二个,以此类推,到最后一个以后再从头来过; - com.netflix.loadbalancer.RetryRule:在RoundRobinRule的基础上添加重试机制,即在指定的重试时间内,反复使用线性轮询策略来选择可用实例; - com.netflix.loadbalancer.WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择; - com.netflix.loadbalancer.BestAvailableRule:选择并发较小的实例; - com.netflix.loadbalancer.AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例; - com.netflix.loadbalancer.ZoneAwareLoadBalancer:采用双重过滤,同时过滤不是同一区域的实例和故障实例,选择并发较小的实例。 ## 使用到的模块 ``` lua springcloud-learning ├── eureka-server -- eureka注册中心 ├── user-service -- 提供User对象CRUD接口的服务 └── ribbon-service -- ribbon服务调用测试服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/seata.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 使用Seata彻底解决Spring Cloud中的分布式事务问题! > Seata是Alibaba开源的一款分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务,本文将通过一个简单的下单业务场景来对其用法进行详细介绍。 ## 什么是分布式事务问题? ### 单体应用 单体应用中,一个业务操作需要调用三个模块完成,此时数据的一致性由本地事务来保证。 ![](../images/springcloud_seata_05.png) ### 微服务应用 随着业务需求的变化,单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用独立的数据源,业务操作需要调用三个服务来完成。此时每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证。 ![](../images/springcloud_seata_06.png) ### 小结 在微服务架构中由于全局数据一致性没法保证产生的问题就是分布式事务问题。简单来说,一次业务操作需要操作多个数据源或需要进行远程调用,就会产生分布式事务问题。 ## Seata简介 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。 ## Seata原理和设计 ### 定义一个分布式事务 我们可以把一个分布式事务理解成一个包含了若干分支事务的全局事务,全局事务的职责是协调其下管辖的分支事务达成一致,要么一起成功提交,要么一起失败回滚。此外,通常分支事务本身就是一个满足ACID的本地事务。这是我们对分布式事务结构的基本认识,与 XA 是一致的。 ![](../images/springcloud_seata_07.png) ### 协议分布式事务处理过程的三个组件 - Transaction Coordinator (TC): 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚; - Transaction Manager (TM): 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议; - Resource Manager (RM): 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。 ![](../images/springcloud_seata_08.png) ### 一个典型的分布式事务过程 - TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID; - XID 在微服务调用链路的上下文中传播; - RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖; - TM 向 TC 发起针对 XID 的全局提交或回滚决议; - TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。 ![](../images/springcloud_seata_09.png) ## seata-server的安装与配置 - 我们先从官网下载seata-server,这里下载的是`seata-server-0.9.0.zip`,下载地址:https://github.com/seata/seata/releases - 这里我们使用Nacos作为注册中心,Nacos的安装及使用可以参考:[Spring Cloud Alibaba:Nacos 作为注册中心和配置中心使用](https://mp.weixin.qq.com/s/N9eAMHuDEJq7kCCJPEEJqw); - 解压seata-server安装包到指定目录,修改`conf`目录下的`file.conf`配置文件,主要修改自定义事务组名称,事务日志存储模式为`db`及数据库连接信息; ```bash service { #vgroup->rgroup vgroup_mapping.fsp_tx_group = "default" #修改事务组名称为:fsp_tx_group,和客户端自定义的名称对应 #only support single node default.grouplist = "127.0.0.1:8091" #degrade current not support enableDegrade = false #disable disable = false #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent max.commit.retry.timeout = "-1" max.rollback.retry.timeout = "-1" } ## transaction log store store { ## store mode: file、db mode = "db" #修改此处将事务信息存储到数据库中 ## database store db { ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc. datasource = "dbcp" ## mysql/oracle/h2/oceanbase etc. db-type = "mysql" driver-class-name = "com.mysql.jdbc.Driver" url = "jdbc:mysql://localhost:3306/seat-server" #修改数据库连接地址 user = "root" #修改数据库用户名 password = "root" #修改数据库密码 min-conn = 1 max-conn = 3 global.table = "global_table" branch.table = "branch_table" lock-table = "lock_table" query-limit = 100 } } ``` - 由于我们使用了db模式存储事务日志,所以我们需要创建一个seat-server数据库,建表sql在seata-server的`/conf/db_store.sql`中; - 修改`conf`目录下的`registry.conf`配置文件,指明注册中心为`nacos`,及修改`nacos`连接信息即可; ```bash registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "nacos" #改为nacos nacos { serverAddr = "localhost:8848" #改为nacos的连接地址 namespace = "" cluster = "default" } } ``` - 先启动Nacos,再使用seata-server中`/bin/seata-server.bat`文件启动seata-server。 ## 数据库准备 ### 创建业务数据库 - seat-order:存储订单的数据库; - seat-storage:存储库存的数据库; - seat-account:存储账户信息的数据库。 ### 初始化业务表 #### order表 ```sql CREATE TABLE `order` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `user_id` bigint(11) DEFAULT NULL COMMENT '用户id', `product_id` bigint(11) DEFAULT NULL COMMENT '产品id', `count` int(11) DEFAULT NULL COMMENT '数量', `money` decimal(11,0) DEFAULT NULL COMMENT '金额', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; ALTER TABLE `order` ADD COLUMN `status` int(1) DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完结' AFTER `money` ; ``` #### storage表 ```sql CREATE TABLE `storage` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `product_id` bigint(11) DEFAULT NULL COMMENT '产品id', `total` int(11) DEFAULT NULL COMMENT '总库存', `used` int(11) DEFAULT NULL COMMENT '已用库存', `residue` int(11) DEFAULT NULL COMMENT '剩余库存', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO `seat-storage`.`storage` (`id`, `product_id`, `total`, `used`, `residue`) VALUES ('1', '1', '100', '0', '100'); ``` #### account表 ```sql CREATE TABLE `account` ( `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `user_id` bigint(11) DEFAULT NULL COMMENT '用户id', `total` decimal(10,0) DEFAULT NULL COMMENT '总额度', `used` decimal(10,0) DEFAULT NULL COMMENT '已用余额', `residue` decimal(10,0) DEFAULT '0' COMMENT '剩余可用额度', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO `seat-account`.`account` (`id`, `user_id`, `total`, `used`, `residue`) VALUES ('1', '1', '1000', '0', '1000'); ``` ### 创建日志回滚表 使用Seata还需要在每个数据库中创建日志表,建表sql在seata-server的`/conf/db_undo_log.sql`中。 ### 完整数据库示意图 ![](../images/springcloud_seata_01.png) ## 制造一个分布式事务问题 这里我们会创建三个服务,一个订单服务,一个库存服务,一个账户服务。当用户下单时,会在订单服务中创建一个订单,然后通过远程调用库存服务来扣减下单商品的库存,再通过远程调用账户服务来扣减用户账户里面的余额,最后在订单服务中修改订单状态为已完成。该操作跨越三个数据库,有两次远程调用,很明显会有分布式事务问题。 ## 客户端配置 - 对seata-order-service、seata-storage-service和seata-account-service三个seata的客户端进行配置,它们配置大致相同,我们下面以seata-order-service的配置为例; - 修改application.yml文件,自定义事务组的名称; ```yaml spring: cloud: alibaba: seata: tx-service-group: fsp_tx_group #自定义事务组名称需要与seata-server中的对应 ``` - 添加并修改file.conf配置文件,主要是修改自定义事务组名称; ```bash service { #vgroup->rgroup vgroup_mapping.fsp_tx_group = "default" #修改自定义事务组名称 #only support single node default.grouplist = "127.0.0.1:8091" #degrade current not support enableDegrade = false #disable disable = false #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent max.commit.retry.timeout = "-1" max.rollback.retry.timeout = "-1" disableGlobalTransaction = false } ``` - 添加并修改registry.conf配置文件,主要是将注册中心改为nacos; ```bash registry { # file 、nacos 、eureka、redis、zk type = "nacos" #修改为nacos nacos { serverAddr = "localhost:8848" #修改为nacos的连接地址 namespace = "" cluster = "default" } } ``` - 在启动类中取消数据源的自动创建: ```java @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableDiscoveryClient @EnableFeignClients public class SeataOrderServiceApplication { public static void main(String[] args) { SpringApplication.run(SeataOrderServiceApplication.class, args); } } ``` - 创建配置使用Seata对数据源进行代理: ```java /** * 使用Seata对数据源进行代理 * Created by macro on 2019/11/11. */ @Configuration public class DataSourceProxyConfig { @Value("${mybatis.mapperLocations}") private String mapperLocations; @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } @Bean public DataSourceProxy dataSourceProxy(DataSource dataSource) { return new DataSourceProxy(dataSource); } @Bean public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSourceProxy); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources(mapperLocations)); sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory()); return sqlSessionFactoryBean.getObject(); } } ``` - 使用@GlobalTransactional注解开启分布式事务: ```java package com.macro.cloud.service.impl; import com.macro.cloud.dao.OrderDao; import com.macro.cloud.domain.Order; import com.macro.cloud.service.AccountService; import com.macro.cloud.service.OrderService; import com.macro.cloud.service.StorageService; import io.seata.spring.annotation.GlobalTransactional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * 订单业务实现类 * Created by macro on 2019/11/11. */ @Service public class OrderServiceImpl implements OrderService { private static final Logger LOGGER = LoggerFactory.getLogger(OrderServiceImpl.class); @Autowired private OrderDao orderDao; @Autowired private StorageService storageService; @Autowired private AccountService accountService; /** * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态 */ @Override @GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class) public void create(Order order) { LOGGER.info("------->下单开始"); //本应用创建订单 orderDao.create(order); //远程调用库存服务扣减库存 LOGGER.info("------->order-service中扣减库存开始"); storageService.decrease(order.getProductId(),order.getCount()); LOGGER.info("------->order-service中扣减库存结束:{}",order.getId()); //远程调用账户服务扣减余额 LOGGER.info("------->order-service中扣减余额开始"); accountService.decrease(order.getUserId(),order.getMoney()); LOGGER.info("------->order-service中扣减余额结束"); //修改订单状态为已完成 LOGGER.info("------->order-service中修改订单状态开始"); orderDao.update(order.getUserId(),0); LOGGER.info("------->order-service中修改订单状态结束"); LOGGER.info("------->下单结束"); } } ``` ## 分布式事务功能演示 - 运行seata-order-service、seata-storage-service和seata-account-service三个服务; - 数据库初始信息状态: ![](../images/springcloud_seata_02.png) - 调用接口进行下单操作后查看数据库:http://localhost:8180/order/create?userId=1&productId=1&count=10&money=100 ![](../images/springcloud_seata_03.png) - 我们在seata-account-service中制造一个超时异常后,调用下单接口: ```java /** * 账户业务实现类 * Created by macro on 2019/11/11. */ @Service public class AccountServiceImpl implements AccountService { private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class); @Autowired private AccountDao accountDao; /** * 扣减账户余额 */ @Override public void decrease(Long userId, BigDecimal money) { LOGGER.info("------->account-service中扣减账户余额开始"); //模拟超时异常,全局事务回滚 try { Thread.sleep(30*1000); } catch (InterruptedException e) { e.printStackTrace(); } accountDao.decrease(userId,money); LOGGER.info("------->account-service中扣减账户余额结束"); } } ``` - 此时我们可以发现下单后数据库数据并没有任何改变; ![](../images/springcloud_seata_03.png) - 我们可以在seata-order-service中注释掉@GlobalTransactional来看看没有Seata的分布式事务管理会发生什么情况: ```java /** * 订单业务实现类 * Created by macro on 2019/11/11. */ @Service public class OrderServiceImpl implements OrderService { /** * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态 */ @Override // @GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class) public void create(Order order) { LOGGER.info("------->下单开始"); //省略代码... LOGGER.info("------->下单结束"); } } ``` - 由于seata-account-service的超时会导致当库存和账户金额扣减后订单状态并没有设置为已经完成,而且由于远程调用的重试机制,账户余额还会被多次扣减。 ![](../images/springcloud_seata_04.png) ## 参考资料 Seata官方文档:https://github.com/seata/seata/wiki ## 使用到的模块 ```lua springcloud-learning ├── seata-order-service -- 整合了seata的订单服务 ├── seata-storage-service -- 整合了seata的库存服务 └── seata-account-service -- 整合了seata的账户服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/sentinel.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Alibaba:Sentinel实现熔断与限流 > Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案,Sentinel 作为其核心组件之一,具有熔断与限流等一系列服务保护功能,本文将对其用法进行详细介绍。 ## Sentinel简介 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 Sentinel具有如下特性: - 丰富的应用场景:承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀,可以实时熔断下游不可用应用; - 完备的实时监控:同时提供实时的监控功能。可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况; - 广泛的开源生态:提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合; - 完善的 SPI 扩展点:提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。 ## 安装Sentinel控制台 > Sentinel控制台是一个轻量级的控制台应用,它可用于实时查看单机资源监控及集群资源汇总,并提供了一系列的规则管理功能,如流控规则、降级规则、热点规则等。 - 我们先从官网下载Sentinel,这里下载的是`sentinel-dashboard-1.6.3.jar`文件,下载地址:https://github.com/alibaba/Sentinel/releases - 下载完成后在命令行输入如下命令运行Sentinel控制台: ```bash java -jar sentinel-dashboard-1.6.3.jar ``` - Sentinel控制台默认运行在8080端口上,登录账号密码均为`sentinel`,通过如下地址可以进行访问:http://localhost:8080 ![](../images/spingcloud_sentinel_01.png) - Sentinel控制台可以查看单台机器的实时监控数据。 ![](../images/spingcloud_sentinel_07.png) ## 创建sentinel-service模块 > 这里我们创建一个sentinel-service模块,用于演示Sentinel的熔断与限流功能。 - 在pom.xml中添加相关依赖,这里我们使用Nacos作为注册中心,所以需要同时添加Nacos的依赖: ```xml com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-alibaba-sentinel ``` - 在application.yml中添加相关配置,主要是配置了Nacos和Sentinel控制台的地址: ```yaml server: port: 8401 spring: application: name: sentinel-service cloud: nacos: discovery: server-addr: localhost:8848 #配置Nacos地址 sentinel: transport: dashboard: localhost:8080 #配置sentinel dashboard地址 port: 8719 service-url: user-service: http://nacos-user-service management: endpoints: web: exposure: include: '*' ``` ## 限流功能 > Sentinel Starter 默认为所有的 HTTP 服务提供了限流埋点,我们也可以通过使用@SentinelResource来自定义一些限流行为。 ### 创建RateLimitController类 > 用于测试熔断和限流功能。 ```java /** * 限流功能 * Created by macro on 2019/11/7. */ @RestController @RequestMapping("/rateLimit") public class RateLimitController { /** * 按资源名称限流,需要指定限流处理逻辑 */ @GetMapping("/byResource") @SentinelResource(value = "byResource",blockHandler = "handleException") public CommonResult byResource() { return new CommonResult("按资源名称限流", 200); } /** * 按URL限流,有默认的限流处理逻辑 */ @GetMapping("/byUrl") @SentinelResource(value = "byUrl",blockHandler = "handleException") public CommonResult byUrl() { return new CommonResult("按url限流", 200); } public CommonResult handleException(BlockException exception){ return new CommonResult(exception.getClass().getCanonicalName(),200); } } ``` ### 根据资源名称限流 > 我们可以根据@SentinelResource注解中定义的value(资源名称)来进行限流操作,但是需要指定限流处理逻辑。 - 流控规则可以在Sentinel控制台进行配置,由于我们使用了Nacos注册中心,我们先启动Nacos和sentinel-service; - 由于Sentinel采用的懒加载规则,需要我们先访问下接口,Sentinel控制台中才会有对应服务信息,我们先访问下该接口:http://localhost:8401/rateLimit/byResource - 在Sentinel控制台配置流控规则,根据@SentinelResource注解的value值: ![](../images/spingcloud_sentinel_02.png) - 快速访问上面的接口,可以发现返回了自己定义的限流处理信息: ![](../images/spingcloud_sentinel_03.png) ### 根据URL限流 > 我们还可以通过访问的URL来限流,会返回默认的限流处理信息。 - 在Sentinel控制台配置流控规则,使用访问的URL: ![](../images/spingcloud_sentinel_04.png) - 多次访问该接口,会返回默认的限流处理结果:http://localhost:8401/rateLimit/byUrl ![](../images/spingcloud_sentinel_05.png) ### 自定义限流处理逻辑 > 我们可以自定义通用的限流处理逻辑,然后在@SentinelResource中指定。 - 创建CustomBlockHandler类用于自定义限流处理逻辑: ```java /** * Created by macro on 2019/11/7. */ public class CustomBlockHandler { public CommonResult handleException(BlockException exception){ return new CommonResult("自定义限流信息",200); } } ``` - 在RateLimitController中使用自定义限流处理逻辑: ```java /** * 限流功能 * Created by macro on 2019/11/7. */ @RestController @RequestMapping("/rateLimit") public class RateLimitController { /** * 自定义通用的限流处理逻辑 */ @GetMapping("/customBlockHandler") @SentinelResource(value = "customBlockHandler", blockHandler = "handleException",blockHandlerClass = CustomBlockHandler.class) public CommonResult blockHandler() { return new CommonResult("限流成功", 200); } } ``` ## 熔断功能 > Sentinel 支持对服务间调用进行保护,对故障应用进行熔断操作,这里我们使用RestTemplate来调用nacos-user-service服务所提供的接口来演示下该功能。 - 首先我们需要使用@SentinelRestTemplate来包装下RestTemplate实例: ```java /** * Created by macro on 2019/8/29. */ @Configuration public class RibbonConfig { @Bean @SentinelRestTemplate public RestTemplate restTemplate(){ return new RestTemplate(); } } ``` - 添加CircleBreakerController类,定义对nacos-user-service提供接口的调用: ```java /** * 熔断功能 * Created by macro on 2019/11/7. */ @RestController @RequestMapping("/breaker") public class CircleBreakerController { private Logger LOGGER = LoggerFactory.getLogger(CircleBreakerController.class); @Autowired private RestTemplate restTemplate; @Value("${service-url.user-service}") private String userServiceUrl; @RequestMapping("/fallback/{id}") @SentinelResource(value = "fallback",fallback = "handleFallback") public CommonResult fallback(@PathVariable Long id) { return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id); } @RequestMapping("/fallbackException/{id}") @SentinelResource(value = "fallbackException",fallback = "handleFallback2", exceptionsToIgnore = {NullPointerException.class}) public CommonResult fallbackException(@PathVariable Long id) { if (id == 1) { throw new IndexOutOfBoundsException(); } else if (id == 2) { throw new NullPointerException(); } return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id); } public CommonResult handleFallback(Long id) { User defaultUser = new User(-1L, "defaultUser", "123456"); return new CommonResult<>(defaultUser,"服务降级返回",200); } public CommonResult handleFallback2(@PathVariable Long id, Throwable e) { LOGGER.error("handleFallback2 id:{},throwable class:{}", id, e.getClass()); User defaultUser = new User(-2L, "defaultUser2", "123456"); return new CommonResult<>(defaultUser,"服务降级返回",200); } } ``` - 启动nacos-user-service和sentinel-service服务: - 由于我们并没有在nacos-user-service中定义id为4的用户,所有访问如下接口会返回服务降级结果:http://localhost:8401/breaker/fallback/4 ```json { "data": { "id": -1, "username": "defaultUser", "password": "123456" }, "message": "服务降级返回", "code": 200 } ``` - 由于我们使用了exceptionsToIgnore参数忽略了NullPointerException,所以我们访问接口报空指针时不会发生服务降级:http://localhost:8401/breaker/fallbackException/2 ![](../images/spingcloud_sentinel_06.png) ## 与Feign结合使用 > Sentinel也适配了Feign组件,我们使用Feign来进行服务间调用时,也可以使用它来进行熔断。 - 首先我们需要在pom.xml中添加Feign相关依赖: ```xml org.springframework.cloud spring-cloud-starter-openfeign ``` - 在application.yml中打开Sentinel对Feign的支持: ```yaml feign: sentinel: enabled: true #打开sentinel对feign的支持 ``` - 在应用启动类上添加@EnableFeignClients启动Feign的功能; - 创建一个UserService接口,用于定义对nacos-user-service服务的调用: ```java /** * Created by macro on 2019/9/5. */ @FeignClient(value = "nacos-user-service",fallback = UserFallbackService.class) public interface UserService { @PostMapping("/user/create") CommonResult create(@RequestBody User user); @GetMapping("/user/{id}") CommonResult getUser(@PathVariable Long id); @GetMapping("/user/getByUsername") CommonResult getByUsername(@RequestParam String username); @PostMapping("/user/update") CommonResult update(@RequestBody User user); @PostMapping("/user/delete/{id}") CommonResult delete(@PathVariable Long id); } ``` - 创建UserFallbackService类实现UserService接口,用于处理服务降级逻辑: ```java /** * Created by macro on 2019/9/5. */ @Component public class UserFallbackService implements UserService { @Override public CommonResult create(User user) { User defaultUser = new User(-1L, "defaultUser", "123456"); return new CommonResult<>(defaultUser,"服务降级返回",200); } @Override public CommonResult getUser(Long id) { User defaultUser = new User(-1L, "defaultUser", "123456"); return new CommonResult<>(defaultUser,"服务降级返回",200); } @Override public CommonResult getByUsername(String username) { User defaultUser = new User(-1L, "defaultUser", "123456"); return new CommonResult<>(defaultUser,"服务降级返回",200); } @Override public CommonResult update(User user) { return new CommonResult("调用失败,服务被降级",500); } @Override public CommonResult delete(Long id) { return new CommonResult("调用失败,服务被降级",500); } } ``` - 在UserFeignController中使用UserService通过Feign调用nacos-user-service服务中的接口: ```java /** * Created by macro on 2019/8/29. */ @RestController @RequestMapping("/user") public class UserFeignController { @Autowired private UserService userService; @GetMapping("/{id}") public CommonResult getUser(@PathVariable Long id) { return userService.getUser(id); } @GetMapping("/getByUsername") public CommonResult getByUsername(@RequestParam String username) { return userService.getByUsername(username); } @PostMapping("/create") public CommonResult create(@RequestBody User user) { return userService.create(user); } @PostMapping("/update") public CommonResult update(@RequestBody User user) { return userService.update(user); } @PostMapping("/delete/{id}") public CommonResult delete(@PathVariable Long id) { return userService.delete(id); } } ``` - 调用如下接口会发生服务降级,返回服务降级处理信息:http://localhost:8401/user/4 ```json { "data": { "id": -1, "username": "defaultUser", "password": "123456" }, "message": "服务降级返回", "code": 200 } ``` ## 使用Nacos存储规则 > 默认情况下,当我们在Sentinel控制台中配置规则时,控制台推送规则方式是通过API将规则推送至客户端并直接更新到内存中。一旦我们重启应用,规则将消失。下面我们介绍下如何将配置规则进行持久化,以存储到Nacos为例。 ### 原理示意图 ![](../images/spingcloud_sentinel_08.png) - 首先我们直接在配置中心创建规则,配置中心将规则推送到客户端; - Sentinel控制台也从配置中心去获取配置信息。 ### 功能演示 - 先在pom.xml中添加相关依赖: ```xml com.alibaba.csp sentinel-datasource-nacos ``` - 修改application.yml配置文件,添加Nacos数据源配置: ```yaml spring: cloud: sentinel: datasource: ds1: nacos: server-addr: localhost:8848 dataId: ${spring.application.name}-sentinel groupId: DEFAULT_GROUP data-type: json rule-type: flow ``` - 在Nacos中添加配置: ![](../images/spingcloud_sentinel_09.png) - 添加配置信息如下: ```json [ { "resource": "/rateLimit/byUrl", "limitApp": "default", "grade": 1, "count": 1, "strategy": 0, "controlBehavior": 0, "clusterMode": false } ] ``` - 相关参数解释: - resource:资源名称; - limitApp:来源应用; - grade:阈值类型,0表示线程数,1表示QPS; - count:单机阈值; - strategy:流控模式,0表示直接,1表示关联,2表示链路; - controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待; - clusterMode:是否集群。 - 发现Sentinel控制台已经有了如下限流规则: ![](../images/spingcloud_sentinel_10.png) - 快速访问测试接口,可以发现返回了限流处理信息: ![](../images/spingcloud_sentinel_11.png) ## 参考资料 Spring Cloud Alibaba 官方文档:https://github.com/alibaba/spring-cloud-alibaba/wiki ## 使用到的模块 ```lua springcloud-learning ├── nacos-user-service -- 注册到nacos的提供User对象CRUD接口的服务 └── sentinel-service -- sentinel功能测试服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/sleuth.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Sleuth:分布式请求链路跟踪 > Spring Cloud Sleuth 是分布式系统中跟踪服务间调用的工具,它可以直观地展示出一次请求的调用过程,本文将对其用法进行详细介绍。 ## Spring Cloud Sleuth 简介 随着我们的系统越来越庞大,各个服务间的调用关系也变得越来越复杂。当客户端发起一个请求时,这个请求经过多个服务后,最终返回了结果,经过的每一个服务都有可能发生延迟或错误,从而导致请求失败。这时候我们就需要请求链路跟踪工具来帮助我们,理清请求调用的服务链路,解决问题。 ## 给服务添加请求链路跟踪 > 我们将通过user-service和ribbon-service之间的服务调用来演示该功能,这里我们调用ribbon-service的接口时,ribbon-service会通过RestTemplate来调用user-service提供的接口。 - 首先给user-service和ribbon-service添加请求链路跟踪功能的支持; - 在user-service和ribbon-service中添加相关依赖: ```xml org.springframework.cloud spring-cloud-starter-zipkin ``` - 修改application.yml文件,配置收集日志的zipkin-server访问地址: ```yaml spring: zipkin: base-url: http://localhost:9411 sleuth: sampler: probability: 0.1 #设置Sleuth的抽样收集概率 ``` ## 整合Zipkin获取及分析日志 > Zipkin是Twitter的一个开源项目,可以用来获取和分析Spring Cloud Sleuth 中产生的请求链路跟踪日志,它提供了Web界面来帮助我们直观地查看请求链路跟踪信息。 - SpringBoot 2.0以上版本已经不需要自行搭建zipkin-server,我们可以从该地址下载zipkin-server:https://repo1.maven.org/maven2/io/zipkin/java/zipkin-server/2.12.9/zipkin-server-2.12.9-exec.jar - 下载完成后使用以下命令运行zipkin-server: ```bash java -jar zipkin-server-2.12.9-exec.jar ``` - Zipkin页面访问地址:http://localhost:9411 ![](../images/springcloud_sleuth_01.png) - 启动eureka-sever,ribbon-service,user-service: ![](../images/springcloud_sleuth_02.png) - 多次调用(Sleuth为抽样收集)ribbon-service的接口[http://localhost:8301/user/1](http://localhost:8301/user/1) ,调用完后查看Zipkin首页发现已经有请求链路跟踪信息了; ![](../images/springcloud_sleuth_03.png) - 点击查看详情可以直观地看到请求调用链路和通过每个服务的耗时: ![](../images/springcloud_sleuth_04.png) ## 使用Elasticsearch存储跟踪信息 > 如果我们把zipkin-server重启一下就会发现刚刚的存储的跟踪信息全部丢失了,可见其是存储在内存中的,有时候我们需要将所有信息存储下来,这里以存储到Elasticsearch为例,来演示下该功能。 ### 安装Elasticsearch - 下载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) ![](../images/arch_screen_25.png) - 运行bin目录下的elasticsearch.bat启动Elasticsearch ![](../images/arch_screen_27.png) ### 修改启动参数将信息存储到Elasticsearch - 使用以下命令运行,就可以把跟踪信息存储到Elasticsearch里面去了,重新启动也不会丢失; ```bash # STORAGE_TYPE:表示存储类型 ES_HOSTS:表示ES的访问地址 java -jar zipkin-server-2.12.9-exec.jar --STORAGE_TYPE=elasticsearch --ES_HOSTS=localhost:9200 ``` - 之后需要重新启动user-service和ribbon-service才能生效,重启后多次调用ribbon-service的接口[http://localhost:8301/user/1](http://localhost:8301/user/1); - 如果安装了Elasticsearch的可视化工具Kibana的话,可以看到里面已经存储了跟踪信息: ![](../images/springcloud_sleuth_05.png) ### 更多启动参数参考 https://github.com/openzipkin/zipkin/tree/master/zipkin-server#elasticsearch-storage ## 使用到的模块 ``` lua springcloud-learning ├── eureka-server -- eureka注册中心 ├── user-service -- 提供User对象CRUD接口的服务 └── ribbon-service -- ribbon服务调用测试服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/springcloud.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # SpringCloud整体架构概览 ## 什么是SpringCloud ### 目标 协调任何服务,简化分布式系统开发。 ### 简介 构建分布式系统不应该是复杂的,SpringCloud对常见的分布式系统模式提供了简单易用的编程模型,帮助开发者构建弹性、可靠、协调的应用程序。 SpringCloud是在SpringBoot的基础上构建的,使开发者可以轻松入门并快速提高工作效率。 SpringCloud为开发人员提供了快速构建分布式系统架构的工具,例如配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁定,领导选举,分布式会话,集群状态等。 ## 整体架构 ![来自Spring官网](../images/springcloud_arch.png) ## SpringCloud的版本关系 SpringCloud是一个由许多子项目组成的综合项目,各子项目有不同的发布节奏。 为了管理SpringCloud与各子项目的版本依赖关系,发布了一个清单,其中包括了某个SpringCloud版本对应的子项目版本。 为了避免SpringCloud版本号与子项目版本号混淆,SpringCloud版本采用了名称而非版本号的命名,这些版本的名字采用了伦敦地铁站的名字,根据字母表的顺序来对应版本时间顺序,例如Angel是第一个版本, Brixton是第二个版本。 当SpringCloud的发布内容积累到临界点或者一个重大BUG被解决后,会发布一个"service releases"版本,简称SRX版本,比如Greenwich.SR2就是SpringCloud发布的Greenwich版本的第2个SRX版本。 ### SpringCloud和SpringBoot版本对应关系 | SpringCloud Version | SpringBoot Version | | ---- | ---- | | Hoxton | 2.2.x | | Greenwich | 2.1.x | | Finchley | 2.0.x | | Edgware | 1.5.x | | Dalston | 1.5.x | ### SpringCloud和各子项目版本对应关系 | Component | Edgware.SR6 | Greenwich.SR2 | | ---- | ---- | ---- | | spring-cloud-bus | 1.3.4.RELEASE | 2.1.2.RELEASE | | spring-cloud-commons | 1.3.6.RELEASE | 2.1.2.RELEASE | | spring-cloud-config | 1.4.7.RELEASE | 2.1.3.RELEASE | | spring-cloud-netflix | 1.4.7.RELEASE | 2.1.2.RELEASE | | spring-cloud-security | 1.2.4.RELEASE | 2.1.3.RELEASE | | spring-cloud-consul | 1.3.6.RELEASE | 2.1.2.RELEASE | | spring-cloud-sleuth | 1.3.6.RELEASE | 2.1.1.RELEASE | | spring-cloud-stream | Ditmars.SR5 | Fishtown.SR3 | | spring-cloud-zookeeper | 1.2.3.RELEASE | 2.1.2.RELEASE | | spring-boot | 1.5.21.RELEASE | 2.1.5.RELEASE | | spring-cloud-task | 1.2.4.RELEASE | 2.1.2.RELEASE | | spring-cloud-gateway | 1.0.3.RELEASE | 2.1.2.RELEASE | | spring-cloud-openfeign | 暂无 | 2.1.2.RELEASE | **注意:Greenwich版本是基于SpringBoot 2.1.x版本构建的,不适用于1.5.x版本。随着2019年8月SpringBoot 1.5.x版本停止维护,Edgware版本也将停止维护。** ## SpringCloud子项目简介 ### Spring Cloud Config 集中配置管理工具,分布式系统中统一的外部配置管理,默认使用Git来存储配置,可以支持客户端配置的刷新及加密、解密操作。 ### Spring Cloud Netflix Netflix OSS 开源组件集成,包括Eureka、Hystrix、Ribbon、Feign、Zuul等核心组件。 - Eureka:服务治理组件,包括服务端的注册中心和客户端的服务发现机制; - Ribbon:负载均衡的服务调用组件,具有多种负载均衡调用策略; - Hystrix:服务容错组件,实现了断路器模式,为依赖服务的出错和延迟提供了容错能力; - Feign:基于Ribbon和Hystrix的声明式服务调用组件; - Zuul:API网关组件,对请求提供路由及过滤功能。 ### Spring Cloud Bus 用于传播集群状态变化的消息总线,使用轻量级消息代理链接分布式系统中的节点,可以用来动态刷新集群中的服务配置。 ### Spring Cloud Consul 基于Hashicorp Consul的服务治理组件。 ### Spring Cloud Security 安全工具包,对Zuul代理中的负载均衡OAuth2客户端及登录认证进行支持。 ### Spring Cloud Sleuth SpringCloud应用程序的分布式请求链路跟踪,支持使用Zipkin、HTrace和基于日志(例如ELK)的跟踪。 ### Spring Cloud Stream 轻量级事件驱动微服务框架,可以使用简单的声明式模型来发送及接收消息,主要实现为Apache Kafka及RabbitMQ。 ### Spring Cloud Task 用于快速构建短暂、有限数据处理任务的微服务框架,用于向应用中添加功能性和非功能性的特性。 ### Spring Cloud Zookeeper 基于Apache Zookeeper的服务治理组件。 ### Spring Cloud Gateway API网关组件,对请求提供路由及过滤功能。 ### Spring Cloud OpenFeign 基于Ribbon和Hystrix的声明式服务调用组件,可以动态创建基于Spring MVC注解的接口实现用于服务调用,在SpringCloud 2.0中已经取代Feign成为了一等公民。 ## 后续 后续SpringCloud系列教程将采用SpringCloud的最新版本Greenwich.SR2来讲解,大家保持关注! ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/cloud/zuul.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Cloud Zuul:API网关服务 > Spring Cloud Zuul 是Spring Cloud Netflix 子项目的核心组件之一,可以作为微服务架构中的API网关使用,支持动态路由与过滤功能,本文将对其用法进行详细介绍。 ## Zuul简介 API网关为微服务架构中的服务提供了统一的访问入口,客户端通过API网关访问相关服务。API网关的定义类似于设计模式中的门面模式,它相当于整个微服务架构中的门面,所有客户端的访问都通过它来进行路由及过滤。它实现了请求路由、负载均衡、校验过滤、服务容错、服务聚合等功能。 ## 创建一个zuul-proxy模块 > 这里我们创建一个zuul-proxy模块来演示zuul的常用功能。 ### 在pom.xml中添加相关依赖 ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-netflix-zuul ``` ### 在application.yml中进行配置 ```yaml server: port: 8801 spring: application: name: zuul-proxy eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8001/eureka/ ``` ### 在启动类上添加@EnableZuulProxy注解来启用Zuul的API网关功能 ```java @EnableZuulProxy @EnableDiscoveryClient @SpringBootApplication public class ZuulProxyApplication { public static void main(String[] args) { SpringApplication.run(ZuulProxyApplication.class, args); } } ``` ## 常用功能 ### 启动相关服务 这里我们通过启动eureka-server,两个user-service,feign-service和zuul-proxy来演示Zuul的常用功能,启动后注册中心显示如下。 ![](../images/springcloud_zuul_01.png) ### 配置路由规则 - 我们可以通过修改application.yml中的配置来配置路由规则,这里我们将匹配`/userService/**`的请求路由到user-service服务上去,匹配`/feignService/**`的请求路由到feign-service上去。 ```yaml zuul: routes: #给服务配置路由 user-service: path: /userService/** feign-service: path: /feignService/** ``` - 访问[http://localhost:8801/userService/user/1](http://localhost:8801/userService/user/1)可以发现请求路由到了user-service上了; - 访问[http://localhost:8801/feignService/user/1](http://localhost:8801/feignService/user/1)可以发现请求路由到了feign-service上了。 ### 默认路由规则 - Zuul和Eureka结合使用,可以实现路由的自动配置,自动配置的路由以服务名称为匹配路径,相当于如下配置: ```yaml zuul: routes: #给服务配置路由 user-service: path: /user-service/** feign-service: path: /feign-service/** ``` - 访问[http://localhost:8801/user-service/user/1](http://localhost:8801/user-service/user/1)同样可以路由到了user-service上了; - 访问[http://localhost:8801/feign-service/user/1](http://localhost:8801/feign-service/user/1)同样可以路由到了feign-service上了。 - 如果不想使用默认的路由规则,可以添加以下配置来忽略默认路由配置: ```yaml zuul: ignored-services: user-service,feign-service #关闭默认路由配置 ``` ### 负载均衡功能 多次调用[http://localhost:8801/user-service/user/1](http://localhost:8801/user-service/user/1)进行测试,可以发现运行在8201和8202的user-service服务交替打印如下信息。 ```bash 2019-10-05 10:31:58.738 INFO 11520 --- [nio-8202-exec-5] c.macro.cloud.controller.UserController : 根据id获取用户信息,用户名称为:macro 2019-10-05 10:32:00.356 INFO 11520 --- [nio-8202-exec-6] c.macro.cloud.controller.UserController : 根据id获取用户信息,用户名称为:macro ``` ### 配置访问前缀 我们可以通过以下配置来给网关路径添加前缀,此处添加了/proxy前缀,这样我们需要访问[http://localhost:8801/proxy/user-service/user/1](http://localhost:8801/proxy/user-service/user/1)才能访问到user-service中的接口。 ```yaml zuul: prefix: /proxy #给网关路由添加前缀 ``` ### Header过滤及重定向添加Host - Zuul在请求路由时,默认会过滤掉一些敏感的头信息,以下配置可以防止路由时的Cookie及Authorization的丢失: ```yaml zuul: sensitive-headers: Cookie,Set-Cookie,Authorization #配置过滤敏感的请求头信息,设置为空就不会过滤 ``` - Zuul在请求路由时,不会设置最初的host头信息,以下配置可以解决: ```yaml zuul: add-host-header: true #设置为true重定向是会添加host请求头 ``` ### 查看路由信息 > 我们可以通过SpringBoot Actuator来查看Zuul中的路由信息。 - 在pom.xml中添加相关依赖: ```xml org.springframework.boot spring-boot-starter-actuator ``` - 修改application.yaml配置文件,开启查看路由的端点: ```yaml management: endpoints: web: exposure: include: 'routes' ``` - 通过访问[http://localhost:8801/actuator/routes](http://localhost:8801/actuator/routes)查看简单路由信息: ![](../images/springcloud_zuul_02.png) - 通过访问[http://localhost:8801/actuator/routes/details](http://localhost:8801/actuator/routes)查看详细路由信息: ![](../images/springcloud_zuul_03.png) ## 过滤器 > 路由与过滤是Zuul的两大核心功能,路由功能负责将外部请求转发到具体的服务实例上去,是实现统一访问入口的基础,过滤功能负责对请求过程进行额外的处理,是请求校验过滤及服务聚合的基础。 ### 过滤器类型 > Zuul中有以下几种典型的过滤器类型。 - pre:在请求被路由到目标服务前执行,比如权限校验、打印日志等功能; - routing:在请求被路由到目标服务时执行,这是使用Apache HttpClient或Netflix Ribbon构建和发送原始HTTP请求的地方; - post:在请求被路由到目标服务后执行,比如给目标服务的响应添加头信息,收集统计数据等功能; - error:请求在其他阶段发生错误时执行。 ### 过滤器的生命周期 > 下图描述了一个HTTP请求到达API网关后,如何在各种不同类型的过滤器中流转的过程。 ![来自Zuul官网](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/springcloud_zuul_04.png) ### 自定义过滤器 > 接下来我们自定义一个过滤器来演示下过滤器的作用。 #### 添加PreLogFilter类继承ZuulFilter > 这是一个前置过滤器,用于在请求路由到目标服务前打印请求日志。 ```java /** * Created by macro on 2019/9/9. */ @Component public class PreLogFilter extends ZuulFilter { private Logger LOGGER = LoggerFactory.getLogger(this.getClass()); /** * 过滤器类型,有pre、routing、post、error四种。 */ @Override public String filterType() { return "pre"; } /** * 过滤器执行顺序,数值越小优先级越高。 */ @Override public int filterOrder() { return 1; } /** * 是否进行过滤,返回true会执行过滤。 */ @Override public boolean shouldFilter() { return true; } /** * 自定义的过滤器逻辑,当shouldFilter()返回true时会执行。 */ @Override public Object run() throws ZuulException { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); String host = request.getRemoteHost(); String method = request.getMethod(); String uri = request.getRequestURI(); LOGGER.info("Remote host:{},method:{},uri:{}", host, method, uri); return null; } } ``` #### 过滤器功能演示 添加过滤器后,我们访问[http://localhost:8801/user-service/user/1](http://localhost:8801/user-service/user/1)测试下,会打印如下日志。 ```bash 2019-10-05 15:13:10.232 INFO 11040 --- [nio-8801-exec-7] com.macro.cloud.filter.PreLogFilter : Remote host:0:0:0:0:0:0:0:1,method:GET,uri:/user-service/user/1 ``` ### 核心过滤器 | 过滤器名称 | 过滤类型 | 优先级 | 过滤器的作用 | | :---------------------- | :------- | :----- | :----------------------------------------------------------- | | ServletDetectionFilter | pre | -3 | 检测当前请求是通过DispatcherServlet处理运行的还是ZuulServlet运行处理的。 | | Servlet30WrapperFilter | pre | -2 | 对原始的HttpServletRequest进行包装。 | | FormBodyWrapperFilter | pre | -1 | 将Content-Type为application/x-www-form-urlencoded或multipart/form-data的请求包装成FormBodyRequestWrapper对象。 | | DebugFilter | route | 1 | 根据zuul.debug.request的配置来决定是否打印debug日志。 | | PreDecorationFilter | route | 5 | 对当前请求进行预处理以便执行后续操作。 | | RibbonRoutingFilter | route | 10 | 通过Ribbon和Hystrix来向服务实例发起请求,并将请求结果进行返回。 | | SimpleHostRoutingFilter | route | 100 | 只对请求上下文中有routeHost参数的进行处理,直接使用HttpClient向routeHost对应的物理地址进行转发。 | | SendForwardFilter | route | 500 | 只对请求上下文中有forward.to参数的进行处理,进行本地跳转。 | | SendErrorFilter | post | 0 | 当其他过滤器内部发生异常时的会由它来进行处理,产生错误响应。 | | SendResponseFilter | post | 1000 | 利用请求上下文的响应信息来组织请求成功的响应内容。 | ### 禁用过滤器 - 我们可以对过滤器进行禁用的配置,配置格式如下: ```yaml zuul: filterClassName: filter: disable: true ``` - 以下是禁用PreLogFilter的示例配置: ```yaml zuul: PreLogFilter: pre: disable: true ``` ## Ribbon和Hystrix的支持 > 由于Zuul自动集成了Ribbon和Hystrix,所以Zuul天生就有负载均衡和服务容错能力,我们可以通过Ribbon和Hystrix的配置来配置Zuul中的相应功能。 - 可以使用Hystrix的配置来设置路由转发时HystrixCommand的执行超时时间: ```yaml hystrix: command: #用于控制HystrixCommand的行为 default: execution: isolation: thread: timeoutInMilliseconds: 1000 #配置HystrixCommand执行的超时时间,执行超过该时间会进行服务降级处理 ``` - 可以使用Ribbon的配置来设置路由转发时请求连接及处理的超时时间: ```yaml ribbon: #全局配置 ConnectTimeout: 1000 #服务请求连接超时时间(毫秒) ReadTimeout: 3000 #服务请求处理超时时间(毫秒) ``` ## 常用配置 ```yaml zuul: routes: #给服务配置路由 user-service: path: /userService/** feign-service: path: /feignService/** ignored-services: user-service,feign-service #关闭默认路由配置 prefix: /proxy #给网关路由添加前缀 sensitive-headers: Cookie,Set-Cookie,Authorization #配置过滤敏感的请求头信息,设置为空就不会过滤 add-host-header: true #设置为true重定向是会添加host请求头 retryable: true # 关闭重试机制 PreLogFilter: pre: disable: false #控制是否启用过滤器 ``` ## 使用到的模块 ```lua springcloud-learning ├── eureka-server -- eureka注册中心 ├── user-service -- 提供User对象CRUD接口的服务 ├── feign-service -- feign服务调用测试服务 └── zuul-proxy -- zuul作为网关的测试服务 ``` ## 项目源码地址 [https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/database/mall_database_overview.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall数据库表结构概览 > mall是一套电商系统,后台系统主要包括商品管理、订单管理、营销管理(运营管理+促销管理)、内容管理、用户管理等模块,本文主要对这些模块的数据库表结构及功能做大概的介绍。 ## 商品管理 ### 数据库表结构 ![](../images/mall_pms.jpg) ### 功能结构 ![](../images/mall_func_pms.jpeg) ## 订单管理 ### 数据库表结构 ![](../images/mall_oms.jpg) ### 功能结构 ![](../images/mall_func_oms.jpeg) ## 营销管理 ### 数据库表结构 ![](../images/mall_sms.jpg) ### 功能结构 ![](../images/mall_func_sms.jpeg) ## 内容管理 ### 数据库表结构 ![](../images/mall_cms.jpg) ### 功能结构 ![](../images/mall_func_cms.jpeg) ## 用户管理 ### 数据库表结构 ![](../images/mall_ums.jpg) ### 功能结构 ![](../images/mall_func_ums.jpeg) `注意`:部分功能暂未实现,只是对表结构进行了设计,商品管理、订单管理、营销管理大部分功能均已实现。 ## 相关资料 ### PowerDesigner数据库设计文件 - 商品管理:https://github.com/macrozheng/mall-learning/blob/master/document/pdm/mall_pms.pdm - 订单管理:https://github.com/macrozheng/mall-learning/blob/master/document/pdm/mall_oms.pdm - 营销管理:https://github.com/macrozheng/mall-learning/blob/master/document/pdm/mall_sms.pdm - 内容管理:https://github.com/macrozheng/mall-learning/blob/master/document/pdm/mall_cms.pdm - 用户管理:https://github.com/macrozheng/mall-learning/blob/master/document/pdm/mall_ums.pdm ### MindMaster功能思维导图 - 商品管理:https://github.com/macrozheng/mall-learning/blob/master/document/mind/pms.emmx - 订单管理:https://github.com/macrozheng/mall-learning/blob/master/document/mind/oms.emmx - 营销管理:https://github.com/macrozheng/mall-learning/blob/master/document/mind/sms.emmx - 内容管理:https://github.com/macrozheng/mall-learning/blob/master/document/mind/cms.emmx - 用户管理:https://github.com/macrozheng/mall-learning/blob/master/document/mind/ums.emmx ## 使用到的工具 - PowerDesigner:[http://powerdesigner.de/](http://powerdesigner.de/) - MindMaster:[http://www.edrawsoft.cn/mindmaster](http://www.edrawsoft.cn/mindmaster) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/database/mall_oms_01.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 订单模块数据库表解析(一) > 本文主要对订单及订单设置功能的表进行解析,采用数据库表与功能对照的形式。 ## 订单 ### 相关表结构 #### 订单表 > 订单表,需要注意的是订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单。 ```sql create table oms_order ( id bigint not null auto_increment comment '订单id', member_id bigint not null comment '会员id', coupon_id bigint comment '优惠券id', order_sn varchar(64) comment '订单编号', create_time datetime comment '提交时间', member_username varchar(64) comment '用户帐号', total_amount decimal(10,2) comment '订单总金额', pay_amount decimal(10,2) comment '应付金额(实际支付金额)', freight_amount decimal(10,2) comment '运费金额', promotion_amount decimal(10,2) comment '促销优化金额(促销价、满减、阶梯价)', integration_amount decimal(10,2) comment '积分抵扣金额', coupon_amount decimal(10,2) comment '优惠券抵扣金额', discount_amount decimal(10,2) comment '管理员后台调整订单使用的折扣金额', pay_type int(1) comment '支付方式:0->未支付;1->支付宝;2->微信', source_type int(1) comment '订单来源:0->PC订单;1->app订单', status int(1) comment '订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单', order_type int(1) comment '订单类型:0->正常订单;1->秒杀订单', delivery_company varchar(64) comment '物流公司(配送方式)', delivery_sn varchar(64) comment '物流单号', auto_confirm_day int comment '自动确认时间(天)', integration int comment '可以获得的积分', growth int comment '可以活动的成长值', promotion_info varchar(100) comment '活动信息', bill_type int(1) comment '发票类型:0->不开发票;1->电子发票;2->纸质发票', bill_header varchar(200) comment '发票抬头', bill_content varchar(200) comment '发票内容', bill_receiver_phone varchar(32) comment '收票人电话', bill_receiver_email varchar(64) comment '收票人邮箱', receiver_name varchar(100) not null comment '收货人姓名', receiver_phone varchar(32) not null comment '收货人电话', receiver_post_code varchar(32) comment '收货人邮编', receiver_province varchar(32) comment '省份/直辖市', receiver_city varchar(32) comment '城市', receiver_region varchar(32) comment '区', receiver_detail_address varchar(200) comment '详细地址', note varchar(500) comment '订单备注', confirm_status int(1) comment '确认收货状态:0->未确认;1->已确认', delete_status int(1) not null default 0 comment '删除状态:0->未删除;1->已删除', use_integration int comment '下单时使用的积分', payment_time datetime comment '支付时间', delivery_time datetime comment '发货时间', receive_time datetime comment '确认收货时间', comment_time datetime comment '评价时间', modify_time datetime comment '修改时间', primary key (id) ); ``` #### 订单商品信息表 > 订单中包含的商品信息,一个订单中会有多个订单商品信息。 ```sql create table oms_order_item ( id bigint not null auto_increment, order_id bigint comment '订单id', order_sn varchar(64) comment '订单编号', product_id bigint comment '商品id', product_pic varchar(500) comment '商品图片', product_name varchar(200) comment '商品名称', product_brand varchar(200) comment '商品品牌', product_sn varchar(64) comment '商品条码', product_price decimal(10,2) comment '销售价格', product_quantity int comment '购买数量', product_sku_id bigint comment '商品sku编号', product_sku_code varchar(50) comment '商品sku条码', product_category_id bigint comment '商品分类id', sp1 varchar(100) comment '商品的销售属性1', sp2 varchar(100) comment '商品的销售属性2', sp3 varchar(100) comment '商品的销售属性3', promotion_name varchar(200) comment '商品促销名称', promotion_amount decimal(10,2) comment '商品促销分解金额', coupon_amount decimal(10,2) comment '优惠券优惠分解金额', integration_amount decimal(10,2) comment '积分优惠分解金额', real_amount decimal(10,2) comment '该商品经过优惠后的分解金额', gift_integration int not null default 0 comment '商品赠送积分', gift_growth int not null default 0 comment '商品赠送成长值', product_attr varchar(500) comment '商品销售属性:[{"key":"颜色","value":"颜色"},{"key":"容量","value":"4G"}]', primary key (id) ); ``` #### 订单操作记录表 > 当订单状态发生改变时,用于记录订单的操作信息。 ```sql create table oms_order_operate_history ( id bigint not null auto_increment, order_id bigint comment '订单id', operate_man varchar(100) comment '操作人:用户;系统;后台管理员', create_time datetime comment '操作时间', order_status int(1) comment '订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单', note varchar(500) comment '备注', primary key (id) ); ``` ### 管理端展现 #### 订单列表 ![](../images/database_screen_33.png) #### 查看订单 ![](../images/database_screen_34.png) ![](../images/database_screen_35.png) ![](../images/database_screen_36.png) #### 订单发货 ![](../images/database_screen_38.png) ### 移动端展现 #### 不同状态下的订单 ![](../images/database_screen_39.png) ![](../images/database_screen_40.png) ![](../images/database_screen_41.png) #### 订单详情 ![](../images/database_screen_42.png) ![](../images/database_screen_43.png) ## 订单设置 ### 相关表结构 #### 订单设置表 > 用于对订单的一些超时操作进行设置。 ```sql create table oms_order_setting ( id bigint not null auto_increment, flash_order_overtime int comment '秒杀订单超时关闭时间(分)', normal_order_overtime int comment '正常订单超时时间(分)', confirm_overtime int comment '发货后自动确认收货时间(天)', finish_overtime int comment '自动完成交易时间,不能申请售后(天)', comment_overtime int comment '订单完成后自动好评时间(天)', primary key (id) ); ``` ### 管理端展现 ![](../images/database_screen_37.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/database/mall_oms_02.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 订单模块数据库表解析(二) > 本文主要对购物车功能相关表进行解析,介绍从商品加入购物车到下单的整个流程,涉及购物车优惠计算流程、确认单生成流程、下单流程及取消订单流程。 ## 购物车表 > 用于存储购物车中每个商品信息,可用于计算商品优惠金额。 ```sql create table oms_cart_item ( id bigint not null auto_increment, product_id bigint comment '商品的id', product_sku_id bigint comment '商品sku的id', member_id bigint comment '会员id', quantity int comment '购买数量', price decimal(10,2) comment '添加到购物车的价格', sp1 varchar(200) comment '销售属性1', sp2 varchar(200) comment '销售属性2', sp3 varchar(200) comment '销售属性3', product_pic varchar(1000) comment '商品主图', product_name varchar(500) comment '商品名称', product_brand varchar(200) comment '商品品牌', product_sn varchar(200) comment '商品的条码', product_sub_title varchar(500) comment '商品副标题(卖点)', product_sku_code varchar(200) comment '商品sku条码', member_nickname varchar(500) comment '会员昵称', create_date datetime comment '创建时间', modify_date datetime comment '修改时间', delete_status int(1) default 0 comment '是否删除', product_category_id bigint comment '商品的分类', product_attr varchar(500) comment '商品销售属性:[{"key":"颜色","value":"银色"},{"key":"容量","value":"4G"}]', primary key (id) ); ``` ## 购物下单流程 ### 整体流程示意图 ![](../images/database_screen_44.png) ### 移动端流程展示 - 会员选择商品规格 ![](../images/database_screen_45.png) - 选择购物车中商品去结算 ![](../images/database_screen_46.png) - 查看确认单 ![](../images/database_screen_47.png) - 支付订单 ![](../images/database_screen_48.png) - 支付成功 ![](../images/database_screen_49.png) - 查看订单 ![](../images/database_screen_50.png) ### 实现逻辑 #### 加入购物车 > 购物车的主要功能就是存储用户选择的商品信息及计算购物车中商品的优惠。 ##### 购物车优惠计算流程 ![](../images/database_screen_51.jpg) ##### 相关注意点 - 由于商品优惠都是以商品为单位来设计的,并不是以sku为单位设计的,所以必须以商品为单位来计算商品优惠; - 代码实现逻辑可以参考OmsPromotionServiceImpl类中的calcCartPromotion方法。 #### 生成确认单 > 确认单主要用于用户确认下单的商品信息、优惠信息、价格信息,以及选择收货地址、选择优惠券和使用积分。 ##### 生成确认单流程 ![](../images/database_screen_52.jpg) ##### 相关注意点 - 总金额的计算:购物车中所有商品的总价; - 活动优惠的计算:购物车中所有商品的优惠金额累加; - 应付金额的计算:应付金额=总金额-活动优惠; - 代码实现逻辑可以参考OmsPortalOrderServiceImpl类中的generateConfirmOrder方法。 #### 生成订单 > 对购物车中信息进行处理,综合下单用户的信息来生成订单。 ##### 下单流程 ![](../images/database_screen_53.jpg) ##### 相关注意点 - 库存的锁定:库存从获取购物车优惠信息时就已经从`pms_sku_stock`表中查询出来了,lock_stock字段表示锁定库存的数量,会员看到的商品数量为真实库存减去锁定库存; - 优惠券分解金额的处理:对全场通用、指定分类、指定商品的优惠券分别进行分解金额的计算: - 全场通用:购物车中所有下单商品进行均摊; - 指定分类:购物车中对应分类的商品进行均摊; - 指定商品:购物车中包含的指定商品进行均摊。 - 订单中每个商品的实际支付金额计算:原价-促销优惠-优惠券抵扣-积分抵扣,促销优惠就是购物车计算优惠流程中计算出来的优惠金额; - 订单号的生成:使用redis来生成,生成规则:8位日期+2位平台号码+2位支付方式+6位以上自增id; - 优惠券使用完成后需要修改优惠券的使用状态; - 代码实现逻辑可以参考OmsPortalOrderServiceImpl类中的generateOrder方法。 #### 取消订单 > 订单生成之后还需开启一个延时任务来取消超时的订单。 ##### 订单取消流程 ![](../images/database_screen_54.jpg) ##### 相关注意点 - 代码实现逻辑可以参考OmsPortalOrderServiceImpl类中的cancelOrder方法。 ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/database/mall_oms_03.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 订单模块数据库表解析(三) > 本文主要对订单退货及订单退货原因设置功能相关表进行解析,采用数据库表与功能对照的形式。 ## 订单退货 ### 相关表结构 #### 订单退货申请表 > 主要用于存储会员退货申请信息,需要注意的是订单退货申请表的四种状态:0->待处理;1->退货中;2->已完成;3->已拒绝。 ```sql create table oms_order_return_apply ( id bigint not null auto_increment, order_id bigint comment '订单id', company_address_id bigint comment '收货地址表id', product_id bigint comment '退货商品id', order_sn varchar(64) comment '订单编号', create_time datetime comment '申请时间', member_username varchar(64) comment '会员用户名', return_amount decimal(10,2) comment '退款金额', return_name varchar(100) comment '退货人姓名', return_phone varchar(100) comment '退货人电话', status int(1) comment '申请状态:0->待处理;1->退货中;2->已完成;3->已拒绝', handle_time datetime comment '处理时间', product_pic varchar(500) comment '商品图片', product_name varchar(200) comment '商品名称', product_brand varchar(200) comment '商品品牌', product_attr varchar(500) comment '商品销售属性:颜色:红色;尺码:xl;', product_count int comment '退货数量', product_price decimal(10,2) comment '商品单价', product_real_price decimal(10,2) comment '商品实际支付单价', reason varchar(200) comment '原因', description varchar(500) comment '描述', proof_pics varchar(1000) comment '凭证图片,以逗号隔开', handle_note varchar(500) comment '处理备注', handle_man varchar(100) comment '处理人员', receive_man varchar(100) comment '收货人', receive_time datetime comment '收货时间', receive_note varchar(500) comment '收货备注', primary key (id) ); ``` #### 公司收货地址表 > 用于处理退货申请时选择收货地址。 ```sql create table oms_company_address ( id bigint not null auto_increment, address_name varchar(200) comment '地址名称', send_status int(1) comment '默认发货地址:0->否;1->是', receive_status int(1) comment '是否默认收货地址:0->否;1->是', name varchar(64) comment '收发货人姓名', phone varchar(64) comment '收货人电话', province varchar(64) comment '省/直辖市', city varchar(64) comment '市', region varchar(64) comment '区', detail_address varchar(200) comment '详细地址', primary key (id) ); ``` ### 管理端展现 - 退货申请列表 ![](../images/database_screen_55.png) - 待处理状态的详情 ![](../images/database_screen_56.png) ![](../images/database_screen_57.png) - 退货中状态的详情 ![](../images/database_screen_58.png) ![](../images/database_screen_59.png) - 已完成状态的详情 ![](../images/database_screen_60.png) ![](../images/database_screen_61.png) - 已拒绝状态的详情 ![](../images/database_screen_62.png) ![](../images/database_screen_63.png) ### 移动端展现 - 在我的中打开售后服务 ![](../images/database_screen_64.png) - 点击申请退货进行退货申请 ![](../images/database_screen_65.png) - 提交退货申请 ![](../images/database_screen_66.png) - 在申请记录中查看退货申请记录 ![](../images/database_screen_67.png) - 查看退货申请进度详情 ![](../images/database_screen_68.png) ## 订单退货原因设置 ### 订单退货原因表 > 用于会员退货时选择退货原因。 ```sql create table oms_order_return_reason ( id bigint not null auto_increment, name varchar(100) comment '退货类型', sort int, status int(1) comment '状态:0->不启用;1->启用', create_time datetime comment '添加时间', primary key (id) ); ``` ### 管理端展现 - 退货原因列表 ![](../images/database_screen_69.png) - 添加退货原因 ![](../images/database_screen_70.png) ### 移动端展现 - 退货申请时选择退货原因 ![](../images/database_screen_71.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/database/mall_permission.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 大家心心念念的权限管理功能,这次安排上了! > mall项目的权限管理功能发布啦!权限管理作为后台管理系统的必要功能,mall项目之前的权限管理并不完善。最近我对原先的权限管理进行了重新设计,打造了一套切实可用的权限管理功能。 ## 功能清单 - 菜单管理:可以实现对后台管理系统左侧菜单的管理,支持更换图标、更换名称、控制菜单显示和排序; - 资源管理:实现了基于访问路径的后台动态权限控制,控制的权限可以精确到接口级别; - 角色管理:可以自定义角色,并为角色分配菜单和资源; - 后台用户管理:可以对后台用户进行管理并分配角色,支持分配多个角色。 ## 功能介绍 > 接下了我们对权限管理功能做个介绍,演示一个完整的权限管理的流程。 ### 菜单管理 > 菜单主要是指管理后台左侧的菜单,管理功能可用于控制其隐藏显示及更换图片名称和排序,目前仅支持二级菜单。 - 查看菜单列表,可以控制隐藏显示及删除; ![](../images/mall_permission_01.png) - 添加及编辑菜单,可以更改菜单的基本属性,不过只能添加前端项目`路由中定义`的菜单,并且`前端名称`要与前端项目中定义的`路由名称`一致; ![](../images/mall_permission_02.png) - 菜单排序,给菜单设置排序后,菜单将按照设置的排序降序进行显示。 ![](../images/mall_permission_03.png) ### 资源管理 > 所谓资源就是后台的接口,可以是单个接口,也可以是一系列接口的集合。这里我们使用了基于Ant的路径匹配,当后台用户访问某个接口时,如果这个后台用户分配了该资源就可以访问,否则无法访问。默认情况下,如果你没有对某个接口配置资源,则该资源直接允许访问。 - 查看资源列表,目前的资源是按控制器级别配置的,即一个控制器中所有的接口定义为一个资源,也可以配置到接口级别; ![](../images/mall_permission_04.png) - 添加及编辑资源,这里我们添加了一个资源分类的概念,便于以后的资源分配; ![](../images/mall_permission_05.png) ### 角色管理 > 用于对后台用户角色进行管理,我们可以给角色分配指定的菜单和资源,这样被分配了角色的后台用户就可以访问这些菜单和资源了。 - 查看角色列表,这里我们需要注意下这三个角色`商品管理员`、`订单管理员`及`超级管理员`; ![](../images/mall_permission_06.png) - 添加及编辑角色用于管理角色的基本属性; ![](../images/mall_permission_07.png) - 分配菜单,可以给角色分配允许访问的菜单; ![](../images/mall_permission_08.png) - 分配资源,可以给角色分配允许访问的后台资源; ![](../images/mall_permission_09.png) ### 后台用户管理 > 用于对后台用户进行管理,直接修改信息(包括修改密码)及分配角色。 - 查看用户列表,可以控制帐号的启用状态及删除帐号; ![](../images/mall_permission_10.png) - 添加及编辑用户,可以管理用户的基本信息及修改密码; ![](../images/mall_permission_11.png) - 分配角色,可以为用户分配角色,指定角色用户可以访问指定菜单和资源。 ![](../images/mall_permission_12.png) ### 动态菜单控制 > 这里我们来演示下动态菜单的控制,我们有两个后台用户,系统管理员和商品管理员,系统管理员分配了所有菜单,商品管理员只分配了商品相关的菜单。 - 使用系统管理员帐号登录,左侧显示了所有菜单: ![](../images/mall_permission_13.png) - 使用商品管理员帐号登录,左侧仅显示商品相关菜单: ![](../images/mall_permission_14.png) ### 动态资源控制 > 这里我们来演示下动态资源的控制,我们给商品管理员只分配了商品相关的资源,他无法访问其他资源。 - 将订单相关菜单开放给商品管理员,但是并没有给他分配订单相关的资源; ![](../images/mall_permission_15.png) - 由于没有给商品管理员分配订单模块相关后台资源,所以当商品管理员访问订单模块时会提示没有相关权限。 ![](../images/mall_permission_16.png) ## 项目源码地址 > 友情提醒,需要导入项目中document文件夹下最新的sql文件,再下载最新的前后端代码才能体验该功能! - 后端项目:https://github.com/macrozheng/mall - 前端项目:https://github.com/macrozheng/mall-admin-web ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/database/mall_pms_01.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 商品模块数据库表解析(一) > 本文主要对商品分类、品牌管理、商品类型这三个功能的表进行解析,采用功能与表结构对照的形式。表解析只会标注一些需要理解的字段,简单字段请自行对照表注释。 ## 商品分类 ### 商品分类表 ```sql create table pms_product_category ( id bigint not null auto_increment, parent_id bigint comment '上级分类的编号:0表示一级分类', name varchar(64) comment '名称', level int(1) comment '分类级别:0->1级;1->2级', product_count int comment '商品数量', product_unit varchar(64) comment '商品单位', nav_status int(1) comment '是否显示在导航栏:0->不显示;1->显示', show_status int(1) comment '显示状态:0->不显示;1->显示', sort int comment '排序', icon varchar(255) comment '图标', keywords varchar(255) comment '关键字', description text comment '描述', primary key (id) ); ``` ### 管理端展现 - 商品分类列表 ![](../images/database_screen_02.png) - 添加商品分类 ![](../images/database_screen_01.png) ### 移动端展现 ![](../images/database_screen_03.png) ## 品牌管理 ### 商品品牌表 ```sql create table pms_brand ( id bigint not null auto_increment, name varchar(64) comment '名称', first_letter varchar(8) comment '首字母', sort int comment '排序', factory_status int(1) comment '是否为品牌制造商:0->不是;1->是', show_status int(1) comment '是否显示', product_count int comment '产品数量', product_comment_count int comment '产品评论数量', logo varchar(255) comment '品牌logo', big_pic varchar(255) comment '专区大图', brand_story text comment '品牌故事', primary key (id) ); ``` ### 管理端展现 - 品牌列表 ![](../images/database_screen_04.png) - 添加品牌 ![](../images/database_screen_05.png) ### 移动端展现 ![](../images/database_screen_06.png) ## 商品类型 > 商品类型即商品属性,主要是指商品的规格和参数,规格用于用户购买商品时选择,参数用于标示商品属性及搜索时筛选。 ### 相关表结构 #### 商品属性分类表 ```sql create table pms_product_attribute_category ( id bigint not null auto_increment, name varchar(64) comment '名称', attribute_count int comment '属性数量', param_count int comment '参数数量', primary key (id) ); ``` #### 商品属性表 > type字段用于控制其是规格还是参数 ```sql create table pms_product_attribute ( id bigint not null auto_increment, product_attribute_category_id bigint comment '商品属性分类id', name varchar(64) comment '名称', select_type int(1) comment '属性选择类型:0->唯一;1->单选;2->多选;对应属性和参数意义不同;', input_type int(1) comment '属性录入方式:0->手工录入;1->从列表中选取', input_list varchar(255) comment '可选值列表,以逗号隔开', sort int comment '排序字段:最高的可以单独上传图片', filter_type int(1) comment '分类筛选样式:1->普通;1->颜色', search_type int(1) comment '检索类型;0->不需要进行检索;1->关键字检索;2->范围检索', related_status int(1) comment '相同属性产品是否关联;0->不关联;1->关联', hand_add_status int(1) comment '是否支持手动新增;0->不支持;1->支持', type int(1) comment '属性的类型;0->规格;1->参数', primary key (id) ); ``` #### 商品属性值表 > 如果对应的参数是规格且规格支持手动添加,那么该表用于存储手动新增的值;如果对应的商品属性是参数,那么该表用于存储参数的值。 ```sql create table pms_product_attribute_value ( id bigint not null auto_increment, product_id bigint comment '商品id', product_attribute_id bigint comment '商品属性id', value varchar(64) comment '手动添加规格或参数的值,参数单值,规格有多个时以逗号隔开', primary key (id) ); ``` #### 商品分类和属性的关系表 > 用于选中分类后搜索时生成筛选属性。 ```sql create table pms_product_category_attribute_relation ( id bigint not null auto_increment, product_category_id bigint comment '商品分类id', product_attribute_id bigint comment '商品属性id', primary key (id) ); ``` ### 管理端展现 - 商品属性分类列表 ![](../images/database_screen_07.png) - 添加商品属性分类 ![](../images/database_screen_08.png) - 商品规格列表 ![](../images/database_screen_09.png) - 商品参数列表 ![](../images/database_screen_10.png) - 添加商品属性 ![](../images/database_screen_11.png) - 添加商品时,选中商品属性分类,就会显示该分类的属性,用于生成sku ![](../images/database_screen_12.png) - 添加商品时,选中商品属性分类,会显示该分类的参数用于录入 ![](../images/database_screen_13.png) ### 移动端展现 - 选择商品规格 ![](../images/database_screen_14.png) - 查看商品参数 ![](../images/database_screen_15.png) - 搜索商品时用于选择分类后的筛选 ![](../images/database_screen_16.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/database/mall_pms_02.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 商品模块数据库表解析(二) > 接上一篇文章,本文主要对编辑商品、商品评价及回复、商品操作记录这三块功能的表进行解析,采用数据库表与功能对照的形式。 ## 编辑商品 ### 相关表结构 #### 商品表 > 商品信息主要包括四部分:商品的基本信息、商品的促销信息、商品的属性信息、商品的关联,商品表是整个商品的基本信息部分。 ```sql create table pms_product ( id bigint not null auto_increment, brand_id bigint comment '品牌id', product_category_id bigint comment '品牌分类id', feight_template_id bigint comment '运费模版id', product_attribute_category_id bigint comment '品牌属性分类id', name varchar(64) not null comment '商品名称', pic varchar(255) comment '图片', product_sn varchar(64) not null comment '货号', delete_status int(1) comment '删除状态:0->未删除;1->已删除', publish_status int(1) comment '上架状态:0->下架;1->上架', new_status int(1) comment '新品状态:0->不是新品;1->新品', recommand_status int(1) comment '推荐状态;0->不推荐;1->推荐', verify_status int(1) comment '审核状态:0->未审核;1->审核通过', sort int comment '排序', sale int comment '销量', price decimal(10,2) comment '价格', promotion_price decimal(10,2) comment '促销价格', gift_growth int default 0 comment '赠送的成长值', gift_point int default 0 comment '赠送的积分', use_point_limit int comment '限制使用的积分数', sub_title varchar(255) comment '副标题', description text comment '商品描述', original_price decimal(10,2) comment '市场价', stock int comment '库存', low_stock int comment '库存预警值', unit varchar(16) comment '单位', weight decimal(10,2) comment '商品重量,默认为克', preview_status int(1) comment '是否为预告商品:0->不是;1->是', service_ids varchar(64) comment '以逗号分割的产品服务:1->无忧退货;2->快速退款;3->免费包邮', keywords varchar(255) comment '关键字', note varchar(255) comment '备注', album_pics varchar(255) comment '画册图片,连产品图片限制为5张,以逗号分割', detail_title varchar(255) comment '详情标题', detail_desc text comment '详情描述', detail_html text comment '产品详情网页内容', detail_mobile_html text comment '移动端网页详情', promotion_start_time datetime comment '促销开始时间', promotion_end_time datetime comment '促销结束时间', promotion_per_limit int comment '活动限购数量', promotion_type int(1) comment '促销类型:0->没有促销使用原价;1->使用促销价;2->使用会员价;3->使用阶梯价格;4->使用满减价格;5->限时购', product_category_name varchar(255) comment '产品分类名称', brand_name varchar(255) comment '品牌名称', primary key (id) ); ``` #### 商品SKU表 > SKU(Stock Keeping Unit)是指库存量单位,SPU(Standard Product Unit)是指标准产品单位。举个例子:iphone xs是一个SPU,而iphone xs 公开版 64G 银色是一个SKU。 ```sql create table pms_sku_stock ( id bigint not null auto_increment, product_id bigint comment '商品id', sku_code varchar(64) not null comment 'sku编码', price decimal(10,2) comment '价格', stock int default 0 comment '库存', low_stock int comment '预警库存', sp1 varchar(64) comment '规格属性1', sp2 varchar(64) comment '规格属性2', sp3 varchar(64) comment '规格属性3', pic varchar(255) comment '展示图片', sale int comment '销量', promotion_price decimal(10,2) comment '单品促销价格', lock_stock int default 0 comment '锁定库存', primary key (id) ); ``` #### 商品阶梯价格表 > 商品优惠相关表,购买同商品满足一定数量后,可以使用打折价格进行购买。如:买两件商品可以打八折。 ```sql create table pms_product_ladder ( id bigint not null auto_increment, product_id bigint comment '商品id', count int comment '满足的商品数量', discount decimal(10,2) comment '折扣', price decimal(10,2) comment '折后价格', primary key (id) ); ``` #### 商品满减表 > 商品优惠相关表,购买同商品满足一定金额后,可以减免一定金额。如:买满1000减100元。 ```sql create table pms_product_full_reduction ( id bigint not null auto_increment, product_id bigint comment '商品id', full_price decimal(10,2) comment '商品满足金额', reduce_price decimal(10,2) comment '商品减少金额', primary key (id) ); ``` #### 商品会员价格表 > 根据不同会员等级,可以以不同的会员价格购买。此处设计有缺陷,可以做成不同会员等级可以减免多少元或者按多少折扣进行购买。 ```sql create table pms_member_price ( id bigint not null auto_increment, product_id bigint comment '商品id', member_level_id bigint comment '会员等级id', member_price decimal(10,2) comment '会员价格', member_level_name varchar(100) comment '会员等级名称', primary key (id) ); ``` ### 管理端展现 #### 填写商品信息 ![](../images/database_screen_22.png) #### 填写商品促销 ![](../images/database_screen_17.png) ##### 特惠促销 ![](../images/database_screen_18.png) ##### 会员价格 ![](../images/database_screen_19.png) ##### 阶梯价格 ![](../images/database_screen_20.png) ##### 满减价格 ![](../images/database_screen_21.png) #### 填写商品属性 ![](../images/database_screen_23.png) ![](../images/database_screen_24.png) ![](../images/database_screen_25.png) #### 选择商品关联 ![](../images/database_screen_26.png) ### 移动端展现 #### 商品介绍 ![](../images/database_screen_27.png) #### 图文详情 ![](../images/database_screen_28.png) #### 相关专题 ![](../images/database_screen_29.png) ## 商品评价及回复 ### 相关表结构 #### 商品评价表 ```sql create table pms_comment ( id bigint not null auto_increment, product_id bigint comment '商品id', member_nick_name varchar(255) comment '会员昵称', product_name varchar(255) comment '商品名称', star int(3) comment '评价星数:0->5', member_ip varchar(64) comment '评价的ip', create_time datetime comment '创建时间', show_status int(1) comment '是否显示', product_attribute varchar(255) comment '购买时的商品属性', collect_couont int comment '收藏数', read_count int comment '阅读数', content text comment '内容', pics varchar(1000) comment '上传图片地址,以逗号隔开', member_icon varchar(255) comment '评论用户头像', replay_count int comment '回复数', primary key (id) ); ``` #### 产品评价回复表 ```sql create table pms_comment_replay ( id bigint not null auto_increment, comment_id bigint comment '评论id', member_nick_name varchar(255) comment '会员昵称', member_icon varchar(255) comment '会员头像', content varchar(1000) comment '内容', create_time datetime comment '创建时间', type int(1) comment '评论人员类型;0->会员;1->管理员', primary key (id) ); ``` ### 移动端展现 #### 商品评价列表 ![](../images/database_screen_30.png) #### 商品评价详情 ![](../images/database_screen_31.png) #### 商品回复列表 ![](../images/database_screen_32.png) ## 商品审核及操作记录 ### 相关表结构 #### 商品审核记录表 > 用于记录商品审核记录 ```sql create table pms_product_vertify_record ( id bigint not null auto_increment, product_id bigint comment '商品id', create_time datetime comment '创建时间', vertify_man varchar(64) comment '审核人', status int(1) comment '审核后的状态:0->未通过;2->已通过', detail varchar(255) comment '反馈详情', primary key (id) ); ``` #### 商品操作记录表 > 用于记录商品操作记录 ```sql create table pms_product_operate_log ( id bigint not null auto_increment, product_id bigint comment '商品id', price_old decimal(10,2) comment '改变前价格', price_new decimal(10,2) comment '改变后价格', sale_price_old decimal(10,2) comment '改变前优惠价', sale_price_new decimal(10,2) comment '改变后优惠价', gift_point_old int comment '改变前积分', gift_point_new int comment '改变后积分', use_point_limit_old int comment '改变前积分使用限制', use_point_limit_new int comment '改变后积分使用限制', operate_man varchar(64) comment '操作人', create_time datetime comment '创建时间', primary key (id) ); ``` ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/database/mall_sms_01.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 营销模块数据库表解析(一) > 本文主要对限时购(秒杀)功能相关表进行解析,采用数据库表与功能对照的形式。 ## 相关表结构 ### 限时购表 > 用于存储限时购活动的信息,包括开始时间、结束时间以及上下线状态。 ```sql create table sms_flash_promotion ( id bigint not null auto_increment, title varchar(200) comment '标题', start_date date comment '开始日期', end_date date comment '结束日期', status int(1) comment '上下线状态', create_time datetime comment '创建时间', primary key (id) ); ``` ### 限时购场次表 > 用于存储限时购场次的信息,在一天中,一个限时购活动会有多个不同的活动时间段。 ```sql create table sms_flash_promotion_session ( id bigint not null auto_increment comment '编号', name varchar(200) comment '场次名称', start_time time comment '每日开始时间', end_time time comment '每日结束时间', status int(1) comment '启用状态:0->不启用;1->启用', create_time datetime comment '创建时间', primary key (id) ); ``` ### 限时购与商品关系表 > 用于存储与限时购相关的商品信息,一个限时购中有多个场次,每个场次都可以设置不同活动商品。 ```sql create table sms_flash_promotion_product_relation ( id bigint not null auto_increment, flash_promotion_id bigint comment '限时购id', flash_promotion_session_id bigint comment '编号', product_id bigint comment '商品价格', flash_promotion_price decimal(10,2) comment '限时购价格', flash_promotion_count int comment '限时购数量', flash_promotion_limit int comment '每人限购数量', sort int comment '排序', primary key (id) ); ``` ### 限时购通知记录表 > 用于存储会员的限时购预约记录,当有的限时购场次还未开始时,会员可以进行预约操作,当场次开始时,系统会进行提醒。 ```sql create table sms_flash_promotion_log ( id int not null auto_increment, member_id int comment '会员id', product_id bigint comment '商品id', member_phone varchar(64) comment '会员电话', product_name varchar(100) comment '商品名称', subscribe_time datetime comment '会员订阅时间', send_time datetime comment '发送时间', primary key (id) ); ``` ## 管理端展现 ### 限时购数据列表 ![](../images/database_screen_72.png) ### 编辑限时购活动 ![](../images/database_screen_73.png) ### 限时购场次列表 ![](../images/database_screen_74.png) ### 编辑限时购场次 ![](../images/database_screen_75.png) ### 添加商品到限时购场次 #### 点击设置商品 ![](../images/database_screen_76.png) #### 点击商品列表 ![](../images/database_screen_77.png) #### 选择商品进行添加 ![](../images/database_screen_78.png) **注意:添加到限时购的商品需要修改`pms_product`表的`promotion_type`为5,优惠计算规则也应该改成使用限时购的优惠。** ### 编辑限时购商品信息 ![](../images/database_screen_79.png) ## 移动端展现 ### 已开抢的限时购 ![](../images/database_screen_80.png) ### 抢购中的限时购 ![](../images/database_screen_81.png) ### 即将开始的限时购 ![](../images/database_screen_82.png) ### 即将开始的限时购可以设置预约提醒 ![](../images/database_screen_83.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/database/mall_sms_02.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 营销模块数据库表解析(二) > 本文主要对优惠券功能相关表进行解析,采用数据库表与功能对照的形式。 ## 相关表结构 ### 优惠券表 > 用于存储优惠券信息,需要注意的是优惠券的使用类型:0->全场通用;1->指定分类;2->指定商品,不同使用类型的优惠券使用范围不一样。 ```sql create table sms_coupon ( id bigint not null auto_increment, type int(1) comment '优惠卷类型;0->全场赠券;1->会员赠券;2->购物赠券;3->注册赠券', name varchar(100) comment '名称', platform int(1) comment '使用平台:0->全部;1->移动;2->PC', count int comment '数量', amount decimal(10,2) comment '金额', per_limit int comment '每人限领张数', min_point decimal(10,2) comment '使用门槛;0表示无门槛', start_time datetime comment '开始使用时间', end_time datetime comment '结束使用时间', use_type int(1) comment '使用类型:0->全场通用;1->指定分类;2->指定商品', note varchar(200) comment '备注', publish_count int comment '发行数量', use_count int comment '已使用数量', receive_count int comment '领取数量', enable_time datetime comment '可以领取的日期', code varchar(64) comment '优惠码', member_level int(1) comment '可领取的会员类型:0->无限制', primary key (id) ); ``` ### 优惠券历史记录表 > 用于存储会员领取及使用优惠券的记录,当会员领取到优惠券时,会产生一条优惠券的记录,需要注意的是它的使用状态:0->未使用;1->已使用;2->已过期。 ```sql create table sms_coupon_history ( id bigint not null auto_increment, coupon_id bigint comment '优惠券id', member_id bigint comment '会员id', order_id bigint comment '订单id', coupon_code varchar(64) comment '优惠券码', member_nickname varchar(64) comment '领取人昵称', get_type int(1) comment '获取类型:0->后台赠送;1->主动获取', create_time datetime comment '创建时间', use_status int(1) comment '使用状态:0->未使用;1->已使用;2->已过期', use_time datetime comment '使用时间', order_sn varchar(100) comment '订单号码', primary key (id) ); ``` ### 优惠券和商品的关系表 > 用于存储优惠券与商品的关系,当优惠券的使用类型为指定商品时,优惠券与商品需要建立关系。 ```sql create table sms_coupon_product_relation ( id bigint not null auto_increment, coupon_id bigint comment '优惠券id', product_id bigint comment '商品id', product_name varchar(500) comment '商品名称', product_sn varchar(200) comment '商品条码', primary key (id) ); ``` ### 优惠券和商品分类关系表 > 用于存储优惠券与商品分类的关系,当优惠券的使用类型为指定分类时,优惠券与商品分类需要建立关系。 ```sql create table sms_coupon_product_category_relation ( id bigint not null auto_increment, coupon_id bigint comment '优惠券id', product_category_id bigint comment '商品分类id', product_category_name varchar(200) comment '商品分类名称', parent_category_name varchar(200) comment '父分类名称', primary key (id) ); ``` ## 管理端展现 ### 优惠券列表 ![](../images/database_screen_84.png) ### 编辑优惠券 #### 全场通用 ![](../images/database_screen_85.png) #### 指定商品 ![](../images/database_screen_86.png) #### 指定分类 ![](../images/database_screen_87.png) ### 查看优惠券 ![](../images/database_screen_88.png) ## 移动端展现 ### 我的优惠券 #### 未使用 ![](../images/database_screen_89.png) #### 已使用 ![](../images/database_screen_90.png) #### 已过期 ![](../images/database_screen_91.png) ### 优惠券详情 ![](../images/database_screen_92.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/database/mall_sms_03.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 营销模块数据库表解析(三) > 本文主要对首页内容推荐功能相关表进行解析,采用数据库表与功能对照的形式。 ## 相关表结构 ### 首页品牌推荐表 > 用于管理首页显示的品牌制造商直供信息。 ```sql create table sms_home_brand ( id bigint not null auto_increment, brand_id bigint comment '商品品牌id', brand_name varchar(64) comment '商品品牌名称', recommend_status int(1) comment '推荐状态:0->不推荐;1->推荐', sort int comment '排序', primary key (id) ); ``` ### 新品推荐商品表 > 用于管理首页显示的新鲜好物信息。 ```sql create table sms_home_new_product ( id bigint not null auto_increment, product_id bigint comment '商品id', product_name varchar(64) comment '商品名称', recommend_status int(1) comment '推荐状态:0->不推荐;1->推荐', sort int(1) comment '排序', primary key (id) ); ``` ### 人气推荐商品表 > 用于管理首页显示的人气推荐信息。 ```sql create table sms_home_recommend_product ( id bigint not null auto_increment, product_id bigint, product_name varchar(64), recommend_status int(1), sort int(1), primary key (id) ); ``` ### 首页专题推荐表 > 用于管理首页显示的专题精选信息。 ```sql create table sms_home_recommend_subject ( id bigint not null auto_increment, subject_id bigint comment '专题id', subject_name varchar(64) comment '专题名称', recommend_status int(1) comment '推荐状态:0->不推荐;1->推荐', sort int comment '排序', primary key (id) ); ``` ### 首页轮播广告表 > 用于管理首页显示的轮播广告信息。 ```sql create table sms_home_advertise ( id bigint not null auto_increment, name varchar(100) comment '名称', type int(1) comment '轮播位置:0->PC首页轮播;1->app首页轮播', pic varchar(500) comment '图片地址', start_time datetime comment '开始时间', end_time datetime comment '结束时间', status int(1) comment '上下线状态:0->下线;1->上线', click_count int comment '点击数', order_count int comment '下单数', url varchar(500) comment '链接地址', note varchar(500) comment '备注', sort int default 0 comment '排序', primary key (id) ); ``` ## 管理端展现 ### 品牌推荐列表 ![](../images/database_screen_93.png) ### 选择品牌 ![](../images/database_screen_94.png) ### 新品推荐列表 ![](../images/database_screen_95.png) ### 选择商品 ![](../images/database_screen_96.png) ### 人气推荐列表 ![](../images/database_screen_97.png) ### 选择商品 ![](../images/database_screen_98.png) ### 专题推荐列表 ![](../images/database_screen_99.png) ### 选择专题 ![](../images/database_screen_100.png) ### 广告列表 ![](../images/database_screen_101.png) ### 编辑广告 ![](../images/database_screen_102.png) ## 移动端展现 ### 首页轮播广告 ![](../images/database_screen_103.png) ### 品牌制造商直供 ![](../images/database_screen_104.png) ### 新鲜好物 ![](../images/database_screen_105.png) ### 人气推荐 ![](../images/database_screen_106.png) ### 专题精选 ![](../images/database_screen_107.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/deploy/mall_deploy_docker.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall在Linux环境下的部署(基于Docker容器) > 本文主要以图文的形式讲解mall在Linux环境下的部署,涉及在Docker容器中安装MySQL、Redis、Nginx、RabbitMQ、MongoDB、Elasticsearch、Logstash、Kibana,以及SpringBoot应用部署,基于`CenterOS7.6`。 ## Docker环境安装 - 安装`yum-utils`: ```bash yum install -y yum-utils device-mapper-persistent-data lvm2 ``` - 为yum源添加docker仓库位置: ```bash yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo ``` - 安装docker: ```bash yum install docker-ce ``` - 启动docker: ```bash systemctl start docker ``` ## MySQL安装 - 下载MySQL`5.7`的docker镜像: ```bash docker pull mysql:5.7 ``` - 使用如下命令启动MySQL服务: ```bash docker run -p 3306:3306 --name mysql \ -v /mydata/mysql/log:/var/log/mysql \ -v /mydata/mysql/data:/var/lib/mysql \ -v /mydata/mysql/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD=root \ -d mysql:5.7 ``` - 参数说明 - -p 3306:3306:将容器的3306端口映射到主机的3306端口 - -v /mydata/mysql/conf:/etc/mysql:将配置文件夹挂在到主机 - -v /mydata/mysql/log:/var/log/mysql:将日志文件夹挂载到主机 - -v /mydata/mysql/data:/var/lib/mysql/:将数据文件夹挂载到主机 - -e MYSQL_ROOT_PASSWORD=root:初始化root用户的密码 - 进入运行MySQL的docker容器: ```bash docker exec -it mysql /bin/bash ``` - 使用MySQL命令打开客户端: ```bash mysql -uroot -proot --default-character-set=utf8 ``` - 创建mall数据库: ```sql create database mall character set utf8 ``` - 安装上传下载插件,并将`document/sql/mall.sql`上传到Linux服务器上: ```bash yum -y install lrzsz ``` - 将`mall.sql`文件拷贝到mysql容器的`/`目录下: ```bash docker cp /mydata/mall.sql mysql:/ ``` - 将sql文件导入到数据库: ```bash use mall; source /mall.sql; ``` - 创建一个`reader:123456`帐号并修改权限,使得任何ip都能访问: ```sql grant all privileges on *.* to 'reader' @'%' identified by '123456'; ``` ## Redis安装 - 下载Redis`5.0`的docker镜像: ```bash docker pull redis:5 ``` - 使用如下命令启动Redis服务: ```bash docker run -p 6379:6379 --name redis \ -v /mydata/redis/data:/data \ -d redis:5 redis-server --appendonly yes ``` - 进入Redis容器使用`redis-cli`命令进行连接: ```bash docker exec -it redis redis-cli ``` ![](../images/mall_linux_deploy_01.png) ## Nginx安装 - 下载Nginx`1.10`的docker镜像: ```bash docker pull nginx:1.10 ``` - 先运行一次容器(为了拷贝配置文件): ```bash docker run -p 80:80 --name nginx \ -v /mydata/nginx/html:/usr/share/nginx/html \ -v /mydata/nginx/logs:/var/log/nginx \ -d nginx:1.10 ``` - 将容器内的配置文件拷贝到指定目录: ```bash docker container cp nginx:/etc/nginx /mydata/nginx/ ``` - 修改文件名称: ```bash mv nginx conf ``` - 终止并删除容器: ```bash docker stop nginx docker rm nginx ``` - 使用如下命令启动Nginx服务: ```bash docker run -p 80:80 --name nginx \ -v /mydata/nginx/html:/usr/share/nginx/html \ -v /mydata/nginx/logs:/var/log/nginx \ -v /mydata/nginx/conf:/etc/nginx \ -d nginx:1.10 ``` ## RabbitMQ安装 - 下载rabbitmq`3.7.15`的docker镜像: ```bash docker pull rabbitmq:3.7.15 ``` - 使用如下命令启动RabbitMQ服务: ```bash docker run -p 5672:5672 -p 15672:15672 --name rabbitmq \ -d rabbitmq:3.7.15 ``` - 进入容器并开启管理功能: ```bash docker exec -it rabbitmq /bin/bash rabbitmq-plugins enable rabbitmq_management ``` ![](../images/mall_linux_deploy_02.png) - 开启防火墙: ```bash firewall-cmd --zone=public --add-port=15672/tcp --permanent firewall-cmd --reload ``` - 访问地址查看是否安装成功:http://192.168.3.101:15672 ![](../images/mall_linux_deploy_03.png) - 输入账号密码并登录:guest guest - 创建帐号并设置其角色为管理员:mall mall ![](../images/mall_linux_deploy_04.png) - 创建一个新的虚拟host为:/mall ![](../images/mall_linux_deploy_05.png) - 点击mall用户进入用户配置页面 ![](../images/mall_linux_deploy_06.png) - 给mall用户配置该虚拟host的权限 ![](../images/mall_linux_deploy_07.png) ## Elasticsearch安装 - 下载Elasticsearch`7.6.2`的docker镜像: ```bash docker pull elasticsearch:7.6.2 ``` - 修改虚拟内存区域大小,否则会因为过小而无法启动: ```bash sysctl -w vm.max_map_count=262144 ``` - 使用如下命令启动Elasticsearch服务: ```bash docker run -p 9200:9200 -p 9300:9300 --name elasticsearch \ -e "discovery.type=single-node" \ -e "cluster.name=elasticsearch" \ -v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \ -v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \ -d elasticsearch:7.6.2 ``` - 启动时会发现`/usr/share/elasticsearch/data`目录没有访问权限,只需要修改`/mydata/elasticsearch/data`目录的权限,再重新启动即可; ```bash chmod 777 /mydata/elasticsearch/data/ ``` - 安装中文分词器IKAnalyzer,并重新启动: ```bash docker exec -it elasticsearch /bin/bash #此命令需要在容器中运行 elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip docker restart elasticsearch ``` - 开启防火墙: ```bash firewall-cmd --zone=public --add-port=9200/tcp --permanent firewall-cmd --reload ``` - 访问会返回版本信息:http://192.168.3.101:9200 ![](../images/mall_linux_deploy_08.png) ## Logstash安装 - 下载Logstash`7.6.2`的docker镜像: ```bash docker pull logstash:7.6.2 ``` - 修改Logstash的配置文件`logstash.conf`中`output`节点下的Elasticsearch连接地址为`es:9200`,配置文件地址:https://github.com/macrozheng/mall/blob/master/document/elk/logstash.conf ``` output { elasticsearch { hosts => "es:9200" index => "mall-%{type}-%{+YYYY.MM.dd}" } } ``` - 创建`/mydata/logstash`目录,并将Logstash的配置文件`logstash.conf`拷贝到该目录; ```bash mkdir /mydata/logstash ``` - 使用如下命令启动Logstash服务; ```bash docker run --name logstash -p 4560:4560 -p 4561:4561 -p 4562:4562 -p 4563:4563 \ --link elasticsearch:es \ -v /mydata/logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf \ -d logstash:7.6.2 ``` - 进入容器内部,安装`json_lines`插件。 ```bash logstash-plugin install logstash-codec-json_lines ``` ## Kibana安装 - 下载Kibana`7.6.2`的docker镜像: ```bash docker pull kibana:7.6.2 ``` - 使用如下命令启动Kibana服务: ```bash docker run --name kibana -p 5601:5601 \ --link elasticsearch:es \ -e "elasticsearch.hosts=http://es:9200" \ -d kibana:7.6.2 ``` - 开启防火墙: ```bash firewall-cmd --zone=public --add-port=5601/tcp --permanent firewall-cmd --reload ``` - 访问地址进行测试:http://192.168.3.101:5601 ![](../images/mall_linux_deploy_09.png) ## MongoDB安装 - 下载MongoDB`4.2.5`的docker镜像: ```bash docker pull mongo:4.2.5 ``` - 使用docker命令启动: ```bash docker run -p 27017:27017 --name mongo \ -v /mydata/mongo/db:/data/db \ -d mongo:4.2.5 ``` ## Docker全部环境安装完成 - 所有下载镜像文件: ```bash REPOSITORY TAG IMAGE ID CREATED SIZE redis 5 071538dbbd71 2 weeks ago 98.3MB mongo 4.2.5 fddee5bccba3 3 months ago 388MB logstash 7.6.2 fa5b3b1e9757 4 months ago 813MB kibana 7.6.2 f70986bc5191 4 months ago 1.01GB elasticsearch 7.6.2 f29a1ee41030 4 months ago 791MB rabbitmq 3.7.15-management 6ffc11daa8d0 13 months ago 186MB mysql 5.7 7faa3c53e6d6 15 months ago 373MB registry 2 f32a97de94e1 17 months ago 25.8MB nginx 1.10 0346349a1a64 3 years ago 182MB java 8 d23bdf5b1b1b 3 years ago 643MB ``` - 所有运行在容器里面的应用: ![](../images/mall_linux_deploy_10.png) ## SpringBoot应用部署 ### 构建所有Docker镜像并上传 - 修改项目根目录下的`pom.xml`中的`docker.host`属性: ```xml http://192.168.3.101:2375 ``` - 如果项目根目录的`pom.mxl`中`docker-maven-plugin`的``节点被注释掉了就打开注释,使项目在打包时直接构建Docker镜像; ![](../images/mall_linux_deploy_11.png) - 直接双击根项目`mall`的`package`命令可以一次性打包所有应用的Docker镜像; ![](../images/mall_linux_deploy_12.png) ```bash REPOSITORY TAG IMAGE ID CREATED SIZE mall/mall-portal 1.0-SNAPSHOT 70e0f76416a0 21 seconds ago 705MB mall/mall-search 1.0-SNAPSHOT f3290bd1d0c7 41 seconds ago 725MB mall/mall-admin 1.0-SNAPSHOT 26557b93a106 About a minute ago 705MB ``` ### 部署mall-admin ```bash docker run -p 8080:8080 --name mall-admin \ --link mysql:db \ --link redis:redis \ -v /etc/localtime:/etc/localtime \ -v /mydata/app/admin/logs:/var/logs \ -d mall/mall-admin:1.0-SNAPSHOT ``` `注意`:如果想使用Logstash收集日志的话,需要将应用容器连接到Logstsh,添加如下配置即可; ```bash --link logstash:logstash \ ``` ### 部署mall-search ```bash docker run -p 8081:8081 --name mall-search \ --link elasticsearch:es \ --link mysql:db \ -v /etc/localtime:/etc/localtime \ -v /mydata/app/search/logs:/var/logs \ -d mall/mall-search:1.0-SNAPSHOT ``` ### 部署mall-port ```bash docker run -p 8085:8085 --name mall-portal \ --link mysql:db \ --link redis:redis \ --link mongo:mongo \ --link rabbitmq:rabbit \ -v /etc/localtime:/etc/localtime \ -v /mydata/app/portal/logs:/var/logs \ -d mall/mall-portal:1.0-SNAPSHOT ``` ### 开启防火墙 ```bash firewall-cmd --zone=public --add-port=8080/tcp --permanent firewall-cmd --zone=public --add-port=8081/tcp --permanent firewall-cmd --zone=public --add-port=8085/tcp --permanent firewall-cmd --reload ``` ### 访问接口进行测试 - mall-admin的api接口文档地址:http://192.168.3.101:8080/swagger-ui.html ![](../images/mall_linux_deploy_13.png) - mall-search的api接口文档地址:http://192.168.3.101:8081/swagger-ui.html ![](../images/mall_linux_deploy_14.png) - mall-portal的api接口文档地址:http://192.168.3.101:8085/swagger-ui.html ![](../images/mall_linux_deploy_15.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/deploy/mall_deploy_docker_compose.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall在Linux环境下的部署(基于Docker Compose) > 最简单的mall在Linux下部署方式,使用两个Docker Compose脚本即可完成部署。第一个脚本用于部署mall运行所依赖的服务(MySQL、Redis、Nginx、RabbitMQ、MongoDB、Elasticsearch、Logstash、Kibana),第二个脚本用于部署mall中的应用(mall-admin、mall-search、mall-portal)。 ## docker环境搭建及使用 具体参考:[开发者必备Docker命令](../reference/docker.md) ## docker-compose环境搭建及使用 具体参考:[使用Docker Compose部署SpringBoot应用](../reference/docker_compose.md) ## mall项目的docker-compose部署 ### 运行配置要求 CenterOS7.6版本,推荐4G以上内存。 ### 部署相关文件 - 数据库脚本`mall.sql`:https://github.com/macrozheng/mall/blob/master/document/sql/mall.sql - nginx配置文件`nginx.conf`:https://github.com/macrozheng/mall/blob/master/document/docker/nginx.conf - Logstash配置文件`logstash.conf`:https://github.com/macrozheng/mall/blob/master/document/elk/logstash.conf - 系统服务运行脚本`docker-compose-env.yml`:https://github.com/macrozheng/mall/tree/master/document/docker/docker-compose-env.yml - 应用服务运行脚本`docker-compose-app.yml`:https://github.com/macrozheng/mall/tree/master/document/docker/docker-compose-app.yml ### 部署前准备 #### 打包并上传mall应用的镜像 需要打包mall-admin、mall-search、mall-portal的docker镜像,具体参考:[使用Maven插件为SpringBoot应用构建Docker镜像](../reference/docker_maven.md) #### 下载所有需要安装的Docker镜像 ```bash docker pull mysql:5.7 docker pull redis:5 docker pull nginx:1.10 docker pull rabbitmq:3.7.15-management docker pull elasticsearch:7.6.2 docker pull kibana:7.6.2 docker pull logstash:7.6.2 docker pull mongo:4.2.5 ``` #### Elasticsearch - 需要设置系统内核参数,否则会因为内存不足无法启动; ```bash # 改变设置 sysctl -w vm.max_map_count=262144 # 使之立即生效 sysctl -p ``` - 需要创建`/mydata/elasticsearch/data`目录并设置权限,否则会因为无权限访问而启动失败。 ```bash # 创建目录 mkdir /mydata/elasticsearch/data/ # 创建并改变该目录权限 chmod 777 /mydata/elasticsearch/data ``` #### Nginx 需要拷贝nginx配置文件,否则挂载时会因为没有配置文件而启动失败。 ```bash # 创建目录之后将nginx.conf文件上传到该目录下面 mkdir /mydata/nginx/ ``` #### Logstash 修改Logstash的配置文件`logstash.conf`中`output`节点下的Elasticsearch连接地址为`es:9200`。 ``` output { elasticsearch { hosts => "es:9200" index => "mall-%{type}-%{+YYYY.MM.dd}" } } ``` 创建`/mydata/logstash`目录,并将Logstash的配置文件`logstash.conf`拷贝到该目录。 ```bash mkdir /mydata/logstash ``` ### 执行docker-compose-env.yml脚本 > 将该文件上传的linux服务器上,执行docker-compose up命令即可启动mall所依赖的所有服务。 ```yaml version: '3' services: mysql: image: mysql:5.7 container_name: mysql command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci restart: always environment: MYSQL_ROOT_PASSWORD: root #设置root帐号密码 ports: - 3306:3306 volumes: - /mydata/mysql/data/db:/var/lib/mysql #数据文件挂载 - /mydata/mysql/data/conf:/etc/mysql/conf.d #配置文件挂载 - /mydata/mysql/log:/var/log/mysql #日志文件挂载 redis: image: redis:5 container_name: redis command: redis-server --appendonly yes volumes: - /mydata/redis/data:/data #数据文件挂载 ports: - 6379:6379 nginx: image: nginx:1.10 container_name: nginx volumes: - /mydata/nginx/nginx.conf:/etc/nginx/nginx.conf #配置文件挂载 - /mydata/nginx/html:/usr/share/nginx/html #静态资源根目录挂载 - /mydata/nginx/log:/var/log/nginx #日志文件挂载 ports: - 80:80 rabbitmq: image: rabbitmq:3.7.15-management container_name: rabbitmq volumes: - /mydata/rabbitmq/data:/var/lib/rabbitmq #数据文件挂载 - /mydata/rabbitmq/log:/var/log/rabbitmq #日志文件挂载 ports: - 5672:5672 - 15672:15672 elasticsearch: image: elasticsearch:7.6.2 container_name: elasticsearch environment: - "cluster.name=elasticsearch" #设置集群名称为elasticsearch - "discovery.type=single-node" #以单一节点模式启动 - "ES_JAVA_OPTS=-Xms512m -Xmx512m" #设置使用jvm内存大小 volumes: - /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins #插件文件挂载 - /mydata/elasticsearch/data:/usr/share/elasticsearch/data #数据文件挂载 ports: - 9200:9200 - 9300:9300 logstash: image: logstash:7.6.2 container_name: logstash environment: - TZ=Asia/Shanghai volumes: - /mydata/logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf #挂载logstash的配置文件 depends_on: - elasticsearch #kibana在elasticsearch启动之后再启动 links: - elasticsearch:es #可以用es这个域名访问elasticsearch服务 ports: - 4560:4560 - 4561:4561 - 4562:4562 - 4563:4563 kibana: image: kibana:7.6.2 container_name: kibana links: - elasticsearch:es #可以用es这个域名访问elasticsearch服务 depends_on: - elasticsearch #kibana在elasticsearch启动之后再启动 environment: - "elasticsearch.hosts=http://es:9200" #设置访问elasticsearch的地址 ports: - 5601:5601 mongo: image: mongo:4.2.5 container_name: mongo volumes: - /mydata/mongo/db:/data/db #数据文件挂载 ports: - 27017:27017 ``` 上传完后在当前目录下执行如下命令: ```bash docker-compose -f docker-compose-env.yml up -d ``` ![](../images/mall_docker_compose_03.png) ### 对依赖服务进行以下设置 当所有依赖服务启动完成后,需要对以下服务进行一些设置。 #### mysql > 需要创建mall数据库并创建一个可以远程访问的对象reader。 - 将mall.sql文件拷贝到mysql容器的/目录下: ```bash docker cp /mydata/mall.sql mysql:/ ``` - 进入mysql容器并执行如下操作: ```bash #进入mysql容器 docker exec -it mysql /bin/bash #连接到mysql服务 mysql -uroot -proot --default-character-set=utf8 #创建远程访问用户 grant all privileges on *.* to 'reader' @'%' identified by '123456'; #创建mall数据库 create database mall character set utf8; #使用mall数据库 use mall; #导入mall.sql脚本 source /mall.sql; ``` #### elasticsearch > 需要安装中文分词器IKAnalyzer,并重新启动。 ```bash docker exec -it elasticsearch /bin/bash #此命令需要在容器中运行 elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip docker restart elasticsearch ``` #### logstash > 需要安装`json_lines`插件,并重新启动。 ```bash docker exec -it logstash /bin/bash logstash-plugin install logstash-codec-json_lines docker restart logstash ``` #### rabbitmq > 需要创建一个mall用户并设置虚拟host为/mall。 - 访问管理页面地址:http://192.168.3.101:15672 ![](../images/mall_docker_compose_05.png) - 输入账号密码并登录:guest guest - 创建帐号并设置其角色为管理员:mall mall ![](../images/mall_docker_compose_06.png) - 创建一个新的虚拟host为:/mall ![](../images/mall_docker_compose_07.png) - 点击mall用户进入用户配置页面 ![](../images/mall_docker_compose_08.png) - 给mall用户配置该虚拟host的权限 ![](../images/mall_docker_compose_09.png) ### 执行docker-compose-app.yml脚本 > 将该文件上传的linux服务器上,执行docker-compose up命令即可启动mall所有的应用。 ```yaml version: '3' services: mall-admin: image: mall/mall-admin:1.0-SNAPSHOT container_name: mall-admin ports: - 8080:8080 volumes: - /mydata/app/mall-admin/logs:/var/logs - /etc/localtime:/etc/localtime environment: - 'TZ="Asia/Shanghai"' external_links: - mysql:db #可以用db这个域名访问mysql服务 mall-search: image: mall/mall-search:1.0-SNAPSHOT container_name: mall-search ports: - 8081:8081 volumes: - /mydata/app/mall-search/logs:/var/logs - /etc/localtime:/etc/localtime environment: - 'TZ="Asia/Shanghai"' external_links: - elasticsearch:es #可以用es这个域名访问elasticsearch服务 - mysql:db #可以用db这个域名访问mysql服务 mall-portal: image: mall/mall-portal:1.0-SNAPSHOT container_name: mall-portal ports: - 8085:8085 volumes: - /mydata/app/mall-portal/logs:/var/logs - /etc/localtime:/etc/localtime environment: - 'TZ="Asia/Shanghai"' external_links: - redis:redis #可以用redis这个域名访问redis服务 - mongo:mongo #可以用mongo这个域名访问mongo服务 - mysql:db #可以用db这个域名访问mysql服务 - rabbitmq:rabbit #可以用rabbit这个域名访问rabbitmq服务 ``` 上传完后在当前目录下执行如下命令: ```bash docker-compose -f docker-compose-app.yml up -d ``` ![](../images/mall_docker_compose_04.png) ### 开启防火墙即可在其他主机上访问所有服务 ```bash systemctl stop firewalld ``` ### 至此所有服务已经正常启动 ![](../images/mall_docker_compose_01.png) ![](../images/mall_docker_compose_02.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/deploy/mall_deploy_jenkins.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall在Linux环境下的自动化部署(基于Jenkins) > 本文是`mall`项目的专属Jenkins自动化部署方法。 ## Jenkins的基本使用 关于Jenkins的基本使用可以参考:[使用Jenkins一键打包部署SpringBoot应用,就是这么6!](https://mp.weixin.qq.com/s/tQqvgSc9cHBtnqRQSbI4aw) ## 执行脚本准备 > 首先我们先把需要远程执行的脚本准备好。 - 脚本文件都存放在了`mall`项目的`/document/sh`目录下: - 上传脚本前在IDEA中修改所有脚本文件的换行符格式为`LF`,否则脚本会无法执行; ![](../images/mall_deploy_jenkins_01.png) - 将所有脚本文件上传到指定目录,这里我们上传到`/mydata/sh`目录下; ![](../images/mall_deploy_jenkins_02.png) - 将所有脚本文件都修改为可执行文件: ```bash chmod +x ./mall-* ``` ![](../images/mall_deploy_jenkins_03.png) ## Jenkins中创建任务 > 接下来我们将通过在Jenkins中创建任务来实现自动化部署。由于我们的`mall`是个多模块的项目,部署上面和曾经的单模块项目还是有所区别的。 ### mall-admin > 由于各个模块的执行任务的创建都大同小异,下面将详细讲解mall-admin模块任务的创建,其他模块将简略讲解。 - 首先我们选择`构建一个自由风格的软件项目`mall-admin,然后配置其Git仓库地址,这里我直接使用了Gitee上面的地址: ![](../images/mall_deploy_jenkins_04.png) - 之后我们创建一个构建,构建`mall`项目中的依赖模块,否则当构建可运行的服务模块时会因为无法找到这些模块而构建失败; ```bash # 只install mall-common,mall-mbg,mall-security三个模块 clean install -pl mall-common,mall-mbg,mall-security -am ``` - 依赖项目构建示意图: ![](../images/mall_deploy_jenkins_05.png) - 再创建一个构建,单独构建并打包mall-admin模块: ![](../images/mall_deploy_jenkins_06.png) - 添加一个远程SSH执行任务,去执行mall-admin的运行脚本: ![](../images/mall_deploy_jenkins_07.png) - 点击保存,完成mall-admin的执行任务创建。 ### mall-portal > mall-portal和其他模块与mall-admin创建任务方式基本一致,只需修改构建模块时的pom.xml文件位置和执行脚本位置即可。 - 我们可以直接从mall-admin模块的任务复制一个过来创建: ![](../images/mall_deploy_jenkins_08.png) - 修改第二个构建中的pom.xml文件位置,改为:${WORKSPACE}/mall-portal/pom.xml ![](../images/mall_deploy_jenkins_09.png) - 修改第三个构建中的SSH执行脚本文件位置,改为:/mydata/sh/mall-portal.sh ![](../images/mall_deploy_jenkins_10.png) - 点击保存,完成mall-portal的执行任务创建。 ### mall-search 参考mall-admin和mall-portal的创建即可。 ### 任务创建完成 ![](../images/mall_deploy_jenkins_11.png) ## 项目地址 [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/deploy/mall_deploy_web.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall前端项目的安装与部署 > 本文主要讲解mall前端项目mall-admin-web的在Windows和Linux环境下的安装及部署。 ## Windows下的安装及部署 ### 下载nodejs并安装 下载地址:https://nodejs.org/dist/v8.9.4/node-v8.9.4-x64.msi ### 下载mall-admin-web的代码 下载地址(github):https://github.com/macrozheng/mall-admin-web 下载地址(码云):https://gitee.com/macrozheng/mall-admin-web ### 从IDEA中打开mall-admin-web项目 ![](../images/tech_screen_12.png) ### 打开控制台输入命令安装相关依赖 ```shell npm install ``` ![](../images/tech_screen_13.png) ![](../images/tech_screen_14.png) ### 已经搭建了mall后台环境的启动 #### 运行本地mall-admin服务 ![](../images/tech_screen_15.png) #### 使用命令启动mall-admin-web - 在IDEA控制台中输入如下命令: ```shell npm run dev ``` ![](../images/tech_screen_16.png) - 访问地址http://localhost:8090 查看效果: ![](../images/tech_screen_17.png) - 进行登录操作,发现调用的是本地接口: ![](../images/tech_screen_18.png) ### 未搭建mall后台环境的启动 > 未搭建mall后台的需要使用线上api进行访问,线上API地址:http://120.27.63.9:8080 。 #### 修改dev.env.js文件中的BASE_API为线上地址 ![](../images/tech_screen_19.png) #### 使用命令启动mall-admin-web - 在IDEA控制台中输入如下命令: ```shell npm run dev ``` ![](../images/tech_screen_16.png) - 访问地址http://localhost:8090 查看效果: ![](../images/tech_screen_17.png) - 进行登录操作,发现调用的是线上接口: ![](../images/tech_screen_20.png) ## Linux下的部署 - 修改prod.env.js文件的配置 ![](../images/tech_screen_21.png) - 使用命令进行打包 ```shell npm run build ``` ![](../images/tech_screen_22.png) - 打包后的代码位置 ![](../images/tech_screen_23.png) - 将dist目录打包为dist.tar.gz文件 ![](../images/tech_screen_24.png) - Linux上nginx的安装可以参考[mall在Linux环境下的部署(基于Docker容器)](https://mp.weixin.qq.com/s/0fVMK107i5bBq8kGQqg8KA)中的nginx部分 - 将dist.tar.gz上传到linux服务器(nginx相关目录) ![](../images/tech_screen_27.png) - 使用该命令进行解压操作 ```shell tar -zxvf dist.tar.gz ``` - 删除nginx的html文件夹 ```shell rm -rf html ``` - 移动dist文件夹到html文件夹 ```shell mv dist html ``` - 运行mall-admin服务 ```shell docker start mall-admin ``` - 重启nginx ```shell docker restart nginx ``` - 访问首页并登录:http://192.168.3.101 ![](../images/tech_screen_25.png) - 发现调用的是Linux服务器地址 ![](../images/tech_screen_26.png) ## 项目源码地址 [https://github.com/macrozheng/mall-admin-web](https://github.com/macrozheng/https://github.com/macrozheng/mall-admin-web) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/deploy/mall_deploy_windows.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall在Windows环境下的部署 > 本文主要以图文的形式讲解mall项目所需环境在windows下的安装,主要包括IDEA、Mysql、Redis、Mongodb、RabbitMQ、Elasticsearch、Logstash、Kibana、OSS。 ## IDEA - 关于IDEA的安装与使用请参考:https://github.com/judasn/IntelliJ-IDEA-Tutorial - 搜索插件仓库,安装插件`Lombok`; ![](../images/mall_windows_deploy_01.png) - 将项目下载到本地,然后直接打开。 ![](../images/mall_windows_deploy_02.png) ![](../images/mall_windows_deploy_03.png) ## Mysql - 下载并安装mysql`5.7`版本,下载地址:https://dev.mysql.com/downloads/installer/ - 设置数据库帐号密码:root root - 下载并安装客户端连接工具Navicat,下载地址:http://www.formysql.com/xiazai.html - 创建数据库`mall` - 导入document/sql下的`mall.sql`文件 ## Redis - 下载Redis,下载地址:https://github.com/MicrosoftArchive/redis/releases ![](../images/mall_windows_deploy_04.png) - 下载完后解压到指定目录; ![](../images/mall_windows_deploy_05.png) - 在当前地址栏输入cmd后,执行redis的启动命令:`redis-server.exe redis.windows.conf` ![](../images/mall_windows_deploy_06.png) ## Elasticsearch - 下载Elasticsearch`7.6.2`的zip包,并解压到指定目录,下载地址:https://www.elastic.co/cn/downloads/past-releases/elasticsearch-7-6-2 ![](../images/mall_windows_deploy_07.png) - 安装中文分词插件,在`elasticsearch-7.6.2\bin`目录下执行以下命令: ```bash elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip ``` ![](../images/mall_windows_deploy_08.png) - 运行bin目录下的`elasticsearch.bat`启动Elasticsearch服务。 ![](../images/mall_windows_deploy_09.png) ## Kibana - 下载Kibana,作为访问Elasticsearch的客户端,请下载`7.6.2`版本的zip包,并解压到指定目录,下载地址:https://www.elastic.co/cn/downloads/past-releases/kibana-7-6-2 ![](../images/mall_windows_deploy_10.png) - 运行bin目录下的`kibana.bat`,启动Kibana服务; ![](../images/mall_windows_deploy_11.png) - 打开Kibana的用户界面,访问地址:http://localhost:5601 ![](../images/mall_windows_deploy_12.png) ## Logstash - 下载Logstash,用于收集日志,请下载`7.6.2`版本的zip包,并解压到指定目录,下载地址:https://www.elastic.co/cn/downloads/past-releases/logstash-7-6-2 ![](../images/mall_windows_deploy_13.png) - 将Logstash的配置文件`logstash.conf`拷贝到安装目录的`bin`目录下,配置文件地址:https://github.com/macrozheng/mall/blob/master/document/elk/logstash.conf ![](../images/mall_windows_deploy_14.png) - Logstash需要安装json_lines插件。 ```bash logstash-plugin install logstash-codec-json_lines ``` - 运行bin目录下的`logstash.bat`,启动Logstash服务,启动命令如下: ```bash logstash -f logstash.conf ``` ## Mongodb - 下载MongoDB安装包,选择`Windows x64`版本安装,下载地址:https://www.mongodb.com/download-center/community ![](../images/mall_windows_deploy_15.png) - 运行MongoDB安装包并选择自定义安装,设置好安装路径; ![](../images/mall_windows_deploy_16.png) - 配置MongoDB,让MongoDB作为服务运行,并配置好数据目录和日志目录; ![](../images/mall_windows_deploy_17.png) - 取消MongoDB Compass的安装选项(不取消安装极慢),需要可自行安装; ![](../images/mall_windows_deploy_18.png) - 双击`mongo.exe`可以运行MongoDB自带客户端,操作MongoDB; ![](../images/mall_windows_deploy_19.png) - 连接成功后会显示如下信息; ![](../images/mall_windows_deploy_20.png) - 如果需要移除MongoDB服务,只需使用管理员权限运行`cmd`工具,并输入如下命令。 ```bash sc.exe delete MongoDB ``` - 下载客户端工具`Robo 3T`,下载地址:https://robomongo.org/download ![](../images/mall_windows_deploy_21.png) - 下载完成后解压,双击`robo3t.exe`即可使用; ![](../images/mall_windows_deploy_22.png) - 之后创建一个到MongoDB的连接; ![](../images/mall_windows_deploy_23.png) - 创建连接成功以后,就可以操作MongoDB了。 ![](../images/mall_windows_deploy_24.png) ## RabbitMQ - 安装Erlang,下载地址:http://erlang.org/download/otp_win64_21.3.exe ![](../images/mall_windows_deploy_25.png) - 安装RabbitMQ,下载地址:https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe ![](../images/mall_windows_deploy_26.png) - 安装完成后,进入RabbitMQ安装目录下的sbin目录 ![](../images/mall_windows_deploy_27.png) - 在地址栏输入cmd并回车启动命令行,然后输入以下命令启动管理功能: ``` rabbitmq-plugins enable rabbitmq_management ``` ![](../images/mall_windows_deploy_28.png) - 访问地址查看是否安装成功:http://localhost:15672/ ![](../images/mall_windows_deploy_29.png) - 输入账号密码并登录:guest guest - 创建帐号并设置其角色为管理员:mall mall ![](../images/mall_windows_deploy_30.png) - 创建一个新的虚拟host为:/mall ![](../images/mall_windows_deploy_31.png) - 点击mall用户进入用户配置页面 ![](../images/mall_windows_deploy_32.png) - 给mall用户配置该虚拟host的权限 ![](../images/mall_windows_deploy_33.png) - 至此,RabbitMQ的安装和配置完成。 ## OSS ### 开通OSS服务 - 登录阿里云官网; - 将鼠标移至产品标签页,单击对象存储 OSS,打开OSS 产品详情页面; - 在OSS产品详情页,单击立即开通。 ### 创建存储空间 - 点击网页右上角控制台按钮进入控制台; ![](../images/mall_windows_deploy_34.png) - 选择我的云产品中的对象存储OSS; ![](../images/mall_windows_deploy_35.png) - 点击左侧存储空间的加号新建存储空间; ![](../images/mall_windows_deploy_36.png) - 新建存储空间并设置读写权限为公共读。 ![](../images/mall_windows_deploy_37.png) ### 跨域资源共享(CORS)的设置 - 选择一个存储空间,打开其基础设置; ![](../images/mall_windows_deploy_38.png) - 点击跨越设置的设置按钮; ![](../images/mall_windows_deploy_39.png) - 点击创建规则; ![](../images/mall_windows_deploy_40.png) - 进行跨域规则设置; ![](../images/mall_windows_deploy_41.png) ## mall-admin - 启动项目:直接运行com.macro.mall.MallAdminApplication的main方法即可; - 接口文档地址:http://localhost:8080/swagger-ui.html ## mall-search - 启动项目:直接运行com.macro.mall.search.MallSearchApplication的main方法即可; - 接口文档地址:http://localhost:8081/swagger-ui.html - 使用前需要先调用接口导入数据;http://localhost:8081/esProduct/importAll - 如出现无法启动的问题,可以先删除Elasticsearch里面的数据再启动 ## mall-portal - 启动mall-portal项目:直接运行com.macro.mall.portal.MallPortalApplication的main方法即可; - 接口文档地址:http://localhost:8085/swagger-ui.html ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/deploy/mall_swarm_deploy_docker.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall-swarm在Linux环境下的部署(基于Docker容器) > 本文以`mall-swarm`项目为例,主要介绍一个微服务架构的电商项目如何在Docker容器下部署,涉及到大量系统组件的部署及多个Spring Cloud微服务应用的部署,基于CentOS7.6。 ## 环境搭建 ### 基础环境部署 > `mall-swarm`运行需要的系统组件如下,使用Docker Compose 批量安装更方便,Docker Compose使用请参考:[使用Docker Compose部署SpringBoot应用](http://www.macrozheng.com/#/reference/docker_compose) 。 | 组件 | 版本号 | | ------------- | ------ | | Mysql | 5.7 | | Redis | 5.0 | | MongoDb | 4.3.5 | | RabbitMq | 3.7.15 | | Nginx | 1.10 | | Elasticsearch | 7.6.2 | | Logstash | 7.6.2 | | Kibana | 7.6.2 | | Nacos | 1.3.0 | - 本项目已经提供好了Docker Compose脚本,直接执行即可,脚本地址:https://github.com/macrozheng/mall-swarm/blob/master/document/docker/docker-compose-env.yml ```bash docker-compose -f docker-compose-env.yml up -d ``` - 某些系统组件无法启动问题,具体参考:[mall在Linux环境下的部署(基于Docker Compose)](http://www.macrozheng.com/#/deploy/mall_deploy_docker_compose) - 由于新增了`Logstash`组件,需要预先创建好Logstash的配置文件,再安装Logstash的JSON插件,配置文件地址:https://github.com/macrozheng/mall-swarm/tree/master/document/elk/logstash.conf ```bash # 创建好配置文件目录 mkdir /mydata/logstash # 进入容器使用如下命令安装插件 logstash-plugin install logstash-codec-json_lines ``` ### 镜像打包上传 > 一共6个应用服务需要打包成Docker镜像,具体如何打包可以参考[使用Maven插件为SpringBoot应用构建Docker镜像](http://www.macrozheng.com/#/reference/docker_maven) 。需要注意的是如果打包过程中遇到找不到`mall-common`或`mall-mbg`模块,需要先按顺序将这些模块install到本地maven仓库再进行打包。 | 应用 | 说明 | | ------------ | ------------ | | mall-monitor | 监控中心 | | mall-gateway | 微服务网关 | | mall-auth | 认证中心 | | mall-admin | 商城后台服务 | | mall-portal | 商城前台服务 | | mall-search | 商城搜索服务 | 镜像打包上传完成后,完整docker仓库镜像示意图: ![](../images/mall_swarm_run_09.png) ## 应用部署 ### 在Nacos中添加配置文件 - 由于我们使用Nacos作为配置中心,统一管理配置,所以我们需要将项目`config`目录下的所有配置都添加到Nacos中,Nacos访问地址:http://192.168.3.101:8848/nacos/ ![](../images/mall_swarm_run_10.png) - 注意,配置文件的文件名称需要和Nacos中的`Data Id`一一对应; ![](../images/mall_swarm_run_11.png) ### 使用Docker Compose批量部署所有应用 - 直接使用提供好的Docker Compose脚本,启动所有应用即可,脚本地址:https://github.com/macrozheng/mall-swarm/blob/master/document/docker/docker-compose-app.yml ```bash docker-compose -f docker-compose-app.yml up -d ``` - 启动成功后,可以查看API文档信息,访问地址:http://192.168.3.101:8201/doc.html ![](../images/mall_swarm_run_05.png) ## 运行完成效果展示 - 查看注册中心注册服务信息,访问地址:http://192.168.3.101:8848/nacos/ ![](../images/mall_swarm_run_01.png) - 监控中心应用信息,访问地址:http://192.168.3.101:8101 ![](../images/mall_swarm_run_02.png) ![](../images/mall_swarm_run_03.png) ![](../images/mall_swarm_run_04.png) - 日志收集系统信息,访问地址:http://192.168.3.101:5601 ![](../images/mall_swarm_run_06.png) ## 可视化管理工具 > Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便的管理Docker环境,包括单机环境和集群环境,下面我们将用Portainer来管理Docker容器中的应用。 - 官网地址:https://github.com/portainer/portainer - 获取Docker镜像文件: ```bash docker pull portainer/portainer ``` - 使用docker容器运行Portainer: ```bash docker run -p 9000:9000 -p 8000:8000 --name portainer \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /mydata/portainer/data:/data \ -d portainer/portainer ``` - 查看Portainer的DashBoard信息,访问地址:http://192.168.3.101:9000 ![](../images/mall_swarm_linux_01.png) - 查看所有运行中的容器信息: ![](../images/mall_swarm_linux_02.png) - 查看所有已经下载的Docker镜像: ![](../images/mall_swarm_linux_03.png) - 查看`mall-portal`应用的统计信息: ![](../images/mall_swarm_linux_04.png) - 查看`mall-portal`应用的运行过程中打印的日志信息: ![](../images/mall_swarm_linux_05.png) - 进入`mall-portal`应用的容器内部来操作容器内部系统: ![](../images/mall_swarm_linux_06.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/deploy/mall_swarm_deploy_jenkins.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 微服务架构下的自动化部署,使用Jenkins来实现! > 之前对`mall-swarm`项目做了升级,注册中心和配置中心都改为使用Nacos,但是Jenkins的自动化部署文档一直都没更新。有些朋友参考原来的文档部署有点小问题,这次对`mall-swarm`的自动化部署文档做个升级,希望对大家有所帮助! ## Jenkins的基本使用 使用该部署方案需要对Jenkins有所了解,关于Jenkins的基本使用可以参考:[《使用Jenkins一键打包部署SpringBoot应用,就是这么6!》](https://mp.weixin.qq.com/s/tQqvgSc9cHBtnqRQSbI4aw) ## 部署准备 > 部署之前需要先安装`mall-swarm`需要的依赖服务,并打包好所有应用的Docker镜像。由于之前已经写过相关教程,这里只提示下关键的步骤,具体可以参考《mall-swarm在Linux环境下的部署(基于Docker容器)》,文档地址:http://www.macrozheng.com/#/deploy/mall_swarm_deploy_docker 。 - 需要安装好项目所需的依赖服务,直接使用`Docker Compose`安装即可,具体服务和版本信息如下; | 组件 | 版本号 | | ------------- | ------ | | Mysql | 5.7 | | Redis | 5.0 | | MongoDb | 4.3.5 | | RabbitMq | 3.7.15 | | Nginx | 1.10 | | Elasticsearch | 7.6.2 | | Logstash | 7.6.2 | | Kibana | 7.6.2 | | Nacos | 1.3.0 | - 打包好所有SpringBoot应用的Docker镜像,具体应用服务信息如下; | 应用 | 说明 | | ------------ | ------------ | | mall-monitor | 监控中心 | | mall-gateway | 微服务网关 | | mall-auth | 认证中心 | | mall-admin | 商城后台服务 | | mall-portal | 商城前台服务 | | mall-search | 商城搜索服务 | - 将应用所有配置添加到Nacos注册中心中去,具体配置文件如下。 ![](../images/mall_swarm_jks_01.png) ## 执行脚本准备 > Jenkins自动化部署是需要依赖Linux执行脚本的,我们先把需要执行的脚本准备好。 - 脚本文件都存放在了`mall-swarm`项目的`/document/sh`目录下: ![](../images/mall_swarm_jks_02.png) - 上传脚本前在IDEA中修改所有脚本文件的换行符格式为`LF`,否则脚本会无法执行; ![](../images/mall_swarm_jks_03.png) - 将所有脚本文件上传到指定目录,这里我们上传到`/mydata/sh`目录下; ![](../images/mall_swarm_jks_04.png) - 将所有脚本文件都修改为可执行文件: ```bash chmod +x ./mall-* ``` ![](../images/mall_swarm_jks_05.png) ## Jenkins中创建任务 > 接下来我们将通过在Jenkins中创建任务来实现自动化部署。由于我们的`mall-swarm`是个多模块的项目,部署上面和曾经的单模块项目还是有所区别的。 ### mall-admin > 由于各个模块执行任务的创建都大同小异,下面将详细讲解`mall-admin`模块任务的创建,其他模块将简略讲解。 - 首先我们选择`构建一个自由风格的软件项目`,然后输入任务名称为`mall-admin`,配置其Git仓库地址,这里我直接使用了Gitee上面的地址: ![](../images/mall_swarm_jks_06.png) - 之后我们创建一个构建,构建`mall-swarm`项目中的依赖模块,否则当构建可运行的服务模块时会因为无法找到这些模块而构建失败; ```bash # 只install mall-common,mall-mbg两个模块 clean install -pl mall-common,mall-mbg -am ``` - 依赖模块构建示意图: ![](../images/mall_swarm_jks_07.png) - 再创建一个构建,单独构建并打包`mall-admin`模块: ![](../images/mall_swarm_jks_08.png) - 再创建一个构建,通过SSH去执行`sh`脚本,这里执行的是`mall-admin`的运行脚本: ![](../images/mall_swarm_jks_09.png) - 点击保存,完成mall-admin的执行任务创建。 ### mall-gateway > `mall-gateway`和其他模块与`mall-admin`的创建任务方式基本一致,只需修改构建模块时的`pom.xml`文件位置和`执行脚本`位置即可。 - 我们可以直接从`mall-admin`模块的任务复制一个过来创建: ![](../images/mall_swarm_jks_10.png) - 修改第二个构建中的`pom.xml`文件位置,改为:`${WORKSPACE}/mall-gateway/pom.xml` ![](../images/mall_swarm_jks_11.png) - 修改第三个构建中的SSH执行脚本文件位置,改为:`/mydata/sh/mall-gateway.sh` ![](../images/mall_swarm_jks_12.png) - 点击保存,完成mall-gateway的执行任务创建。 ### 其他模块 其他模块的执行任务创建,参考`mall-admin`和`mall-gateway`的创建即可。 ### 任务创建完成 ![](../images/mall_swarm_jks_21.png) ## Docker网络问题 > 如果之前使用的是`Docker Compose`启动所有依赖服务,会默认创建一个网络,所有的依赖服务都会在此网络之中,不同网络内的服务无法互相访问。我这里创建的网络是`deploy_default`,所以需要指定`sh`脚本中服务运行的的网络,否则启动的应用服务会无法连接到依赖服务。 - 可以使用`docker inspect mysql`命令来查看mysql服务所在的网络; ![](../images/mall_swarm_jks_19.png) - 也可以通过`docker network ls`来查看所有网络; ```bash [root@local-linux ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 59b309a5c12f bridge bridge local 3a6f76a8920d deploy_default bridge local ef34fe69992b host host local a65be030c632 none ``` - 修改所有`sh`脚本,修改服务运行的网络,添加一行`--network deploy_default \`即可。 ![](../images/mall_swarm_jks_20.png) ## 模块启动顺序问题 > 由于作为注册中心和配置中心的Nacos已经启动了,其他模块基本没有启动顺序的限制,但是最好还是按照下面的顺序启动。 推荐启动顺序: - mall-auth - mall-gateway - mall-monitor - mall-admin - mall-portal - mall-search ## 运行完成效果展示 - 查看API文档信息,访问地址:http://192.168.3.101:8201/doc.html ![](../images/mall_swarm_jks_13.png) - 查看注册中心注册服务信息,访问地址:http://192.168.3.101:8848/nacos/ ![](../images/mall_swarm_jks_14.png) - 监控中心应用信息,访问地址:http://192.168.3.101:8101 ![](../images/mall_swarm_jks_15.png) ![](../images/mall_swarm_jks_16.png) ![](../images/mall_swarm_jks_17.png) - 日志收集系统信息,访问地址:http://192.168.3.101:5601 ![](../images/mall_swarm_jks_18.png) ## 总结 我们通过在Jenkins中创建任务,完成了`mall-swarm`项目的自动化部署工作,这样当我们每次修改完代码后,只需点击启动任务,就可以实现一键打包部署,省去了频繁打包部署的麻烦。 ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/deploy/mall_swarm_deploy_k8s.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall-swarm微服务项目在K8S下的实践! > 由于K8S目前实在是太火了,之前一直说要给mall项目出个K8S部署方案,这次它来啦!在更新完K8S系列教程后,是时候来波实践了,拿`mall-swarm`项目来实践真的是刚刚好。如果你有在Docker下部署该项目的经历,而且已经学习了我的K8S系列教程的话,相信你能够轻松上手! ## 服务器规划 > 我们先来说下服务器规划吧,之前一直使用的是单机部署所有服务,这次我们使用两台服务器来部署。 - 基础服务器(192.168.3.101):用于部署`mall-swarm`的依赖服务,包括MySql、Redis、Elasticsearch等与应用无关的服务,采用Docker方式来部署。 - 应用服务器(192.168.3.102):用于部署`mall-swarm`的应用服务,包括mall-admin、mall-portal、mall-search等应用服务,采用K8S方式来部署。 ## 镜像打包及推送 > 为了方便部署,我们把`mall-swarm`的所有应用镜像都上传到Docker Hub上去。 - 首先修改项目根目录下的`pom.xml`文件; ```xml http://192.168.3.101:2375 ``` - 使用Maven插件将所有镜像打包到Linux服务器上,直接使用根项目下的`package`命令即可; ![](../images/mall_swarm_deploy_k8s_01.png) - 修改所有镜像标签名称,修改本地镜像标签名称为远程镜像标签名称; ```bash docker tag mall/mall-gateway:1.0-SNAPSHOT macrodocker/mall-gateway:1.0-SNAPSHOT docker tag mall/mall-auth:1.0-SNAPSHOT macrodocker/mall-auth:1.0-SNAPSHOT docker tag mall/mall-monitor:1.0-SNAPSHOT macrodocker/mall-monitor:1.0-SNAPSHOT docker tag mall/mall-admin:1.0-SNAPSHOT macrodocker/mall-admin:1.0-SNAPSHOT docker tag mall/mall-portal:1.0-SNAPSHOT macrodocker/mall-portal:1.0-SNAPSHOT docker tag mall/mall-search:1.0-SNAPSHOT macrodocker/mall-search:1.0-SNAPSHOT ``` - 修改完成后查询`macrodocker`相关镜像显示如下,`macrodocker`为我们在Docker Hub上的仓库地址; ```bash [root@local-linux ~]# docker images |grep macrodocker macrodocker/mall-auth 1.0-SNAPSHOT 72df5f91f2d7 9 minutes ago 699MB macrodocker/mall-gateway 1.0-SNAPSHOT 4055dfc1e601 9 minutes ago 708MB macrodocker/mall-monitor 1.0-SNAPSHOT 492d9bb4375c 9 minutes ago 696MB macrodocker/mall-portal 1.0-SNAPSHOT 8dd79675f40c 10 minutes ago 719MB macrodocker/mall-search 1.0-SNAPSHOT f0d0d80c590f 10 minutes ago 734MB macrodocker/mall-admin 1.0-SNAPSHOT 15737ce903a9 11 minutes ago 715MB ``` - 之后推送镜像到Docker Hub,如果你不想自己推送镜像,可以使用我已经上传好到镜像。 ```bash # 登录Docker Hub docker login # 推送到远程仓库 docker push macrodocker/mall-gateway:1.0-SNAPSHOT docker push macrodocker/mall-auth:1.0-SNAPSHOT docker push macrodocker/mall-monitor:1.0-SNAPSHOT docker push macrodocker/mall-admin:1.0-SNAPSHOT docker push macrodocker/mall-portal:1.0-SNAPSHOT docker push macrodocker/mall-search:1.0-SNAPSHOT ``` ## 基础服务器部署 > 我们依然使用Docker来部署依赖服务,`mall-swarm`运行需要的依赖服务如下,使用Docker Compose脚本安装更方便,Docker Compose使用请参考:[《使用Docker Compose部署SpringBoot应用》](https://mp.weixin.qq.com/s/iMl9bJ4SxUsNHBbiS5VUcw) 。 | 组件 | 版本号 | | ------------- | ------ | | Mysql | 5.7 | | Redis | 5.0 | | MongoDb | 4.3.5 | | RabbitMq | 3.7.15 | | Nginx | 1.10 | | Elasticsearch | 7.6.2 | | Logstash | 7.6.2 | | Kibana | 7.6.2 | | Nacos | 1.3.0 | - 本项目已经提供好了Docker Compose脚本,直接执行如下命令即可,脚本地址:https://github.com/macrozheng/mall-swarm/blob/master/document/docker/docker-compose-env.yml ```bash docker-compose -f docker-compose-env.yml up -d ``` - 某些系统组件无法启动问题,具体参考:[《mall在Linux环境下的部署(基于Docker Compose)》](https://mp.weixin.qq.com/s/JYkvdub9DP5P9ULX4mehUw) - 部署完成后,查看Docker中运行的服务显示如下。 ```bash [root@local-linux ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fef5695319d1 nacos/nacos-server:1.3.0 "bin/docker-startup.…" 3 months ago Up 6 hours 0.0.0.0:8848->8848/tcp nacos-registry e7ec37fcafda elasticsearch:7.6.2 "/usr/local/bin/dock…" 3 months ago Up 6 hours 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp elasticsearch 427f0176c426 mongo:4.2.5 "docker-entrypoint.s…" 3 months ago Up 6 hours 0.0.0.0:27017->27017/tcp mongo 5618ed50942a redis:5 "docker-entrypoint.s…" 3 months ago Up 6 hours 0.0.0.0:6379->6379/tcp redis 1744f412c6f4 rabbitmq:3.7.15-management "docker-entrypoint.s…" 3 months ago Up 6 hours 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp rabbitmq 6b26f63dfad6 mysql:5.7 "docker-entrypoint.s…" 3 months ago Up 6 hours 0.0.0.0:3306->3306/tcp, 33060/tcp mysql ``` ## 应用服务器部署 > 我们将把`mall-swarm`中所有应用服务都部署到K8S上,使用Rancher来进行可视化管理。 ### 安装Rancher - 首先需要安装Rancher,先下载Rancher的Docker镜像; ```bash docker pull rancher/rancher:v2.5-head ``` - 下载完成后在Docker容器中运行Rancher服务; ```bash docker run -p 80:80 -p 443:443 --name rancher \ --privileged \ --restart=unless-stopped \ -d rancher/rancher:v2.5-head ``` - Rancher启动成功后,通过如下地址就可以访问它的主页了:http://192.168.3.102 ![](../images/mall_swarm_deploy_k8s_02.png) ### 修改Nacos配置 > 将项目`config`目录下的所有配置都添加到Nacos中,由于应用服务都部署到了K8S中,所以需要修改相关配置。 - 将配置信息添加到Nacos中后显示信息如下,Nacos访问地址:http://192.168.3.101:8848/nacos/index.html ![](../images/mall_swarm_deploy_k8s_03.png) - 修改`mall-admin-prod.yaml`配置,修改MySql和Redis连接地址即可,之前是通过`--link`的形式访问的,这次需要改为IP进行访问; ```yaml spring: datasource: url: jdbc:mysql://192.168.3.101:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root redis: host: 192.168.3.101 # Redis服务器地址 database: 0 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 password: #不设置密码 ``` - 修改`mall-gateway-prod.yaml`配置,修改Redis连接地址及JWT的publicKey访问地址即可(当在K8S中创建服务后,可以通过服务名进行访问); ```yaml spring: redis: host: 192.168.3.101 # Redis服务器地址 database: 0 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 password: #不设置密码 security: oauth2: resourceserver: jwt: jwk-set-uri: 'http://mall-gateway-service:8201/mall-auth/rsa/publicKey' ``` - 修改`mall-portal-prod.yaml`配置,修改MySql、MongoDb、Redis、RabbitMq连接地址即可; ```yaml spring: datasource: url: jdbc:mysql://192.168.3.101:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root data: mongodb: host: 192.168.3.101 port: 27017 database: mall-port redis: host: 192.168.3.101 # Redis服务器地址 database: 0 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 password: #不设置密码 rabbitmq: host: 192.168.3.101 port: 5672 virtual-host: /mall username: mall password: mall publisher-confirms: true #如果对异步消息需要回调必须设置为true ``` - 修改`mall-search-prod.yaml`配置,修改MySql、Elasticsearch连接地址即可。 ```yaml spring: datasource: url: jdbc:mysql://192.168.3.101:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root elasticsearch: rest: uris: http://192.168.3.101:9200 ``` ### 使用Rancher部署应用 > 当使用Rancher创建Deployment时,如果镜像下载过慢会出现超时,可以进入到Rancher容器中手动进行下载。 - 首先进入Rancher容器内部; ```bash docker exex -it rancher /bin/bash ``` - 通过`crictl`命令下载应用镜像,下载过程有点慢而且没有进度条,需要耐心等待; ```bash k3s crictl pull macrodocker/mall-gateway:1.0-SNAPSHOT k3s crictl pull macrodocker/mall-auth:1.0-SNAPSHOT k3s crictl pull macrodocker/mall-monitor:1.0-SNAPSHOT k3s crictl pull macrodocker/mall-admin:1.0-SNAPSHOT k3s crictl pull macrodocker/mall-portal:1.0-SNAPSHOT k3s crictl pull macrodocker/mall-search:1.0-SNAPSHOT ``` - 下载完成后查看镜像显示内容如下; ```bash root@ae85f823208f:/var/lib/rancher# k3s crictl images |grep macrodocker docker.io/macrodocker/mall-admin 1.0-SNAPSHOT 15737ce903a94 308MB docker.io/macrodocker/mall-auth 1.0-SNAPSHOT 72df5f91f2d74 293MB docker.io/macrodocker/mall-gateway 1.0-SNAPSHOT 4055dfc1e6016 301MB docker.io/macrodocker/mall-monitor 1.0-SNAPSHOT 492d9bb4375c6 291MB docker.io/macrodocker/mall-portal 1.0-SNAPSHOT 8dd79675f40c1 312MB docker.io/macrodocker/mall-search 1.0-SNAPSHOT f0d0d80c590f1 325MB ``` - 镜像都下载完成后,我们就可以使用Rancher来可视化创建Deployment了,这里以`YAML`的方式来创建; ![](../images/mall_swarm_deploy_k8s_04.png) - 直接将项目`k8s`文件夹中的`mall-admin-deployment.yaml`文件内容复制下即可; ![](../images/mall_swarm_deploy_k8s_05.png) - 之后再使用`mall-admin-service.yaml`文件创建Service; ![](../images/mall_swarm_deploy_k8s_06.png) - 这里仅以`mall-admin`应用为例创建Deployment和Service,其他应用创建过程基本相同,脚本均在项目的k8s文件夹中。 ![](../images/mall_swarm_deploy_k8s_07.png) ## 效果展示 > 使用Rancher部署完所有应用服务之后,让我们来看看效果。 - 首先查看所有创建的Deployment; ![](../images/mall_swarm_deploy_k8s_08.png) - 查看所有创建的Service; ![](../images/mall_swarm_deploy_k8s_09.png) - 如果你想查看应用的启动日志,在Pod列表查看即可; ![](../images/mall_swarm_deploy_k8s_10.png) - 由于应用服务被部署在Rancher容器内部,无法直接访问,我们可以使用Nginx反向代理来访问,Nginx服务运行在`2080`端口上; ```bash docker run -p 2080:2080 --name nginx \ -v /mydata/nginx/html:/usr/share/nginx/html \ -v /mydata/nginx/logs:/var/log/nginx \ -v /mydata/nginx/conf:/etc/nginx \ -d nginx:1.10 ``` - 接下来需要获得Rancher容器运行的IP地址,使用如下命令即可; ```bash [root@linux-local ~]# docker inspect rancher |grep IPAddress "SecondaryIPAddresses": null, "IPAddress": "172.17.0.2", "IPAddress": "172.17.0.2", ``` - 创建完Nginx容器后,添加配置文件`api.conf`,将`api.macrozheng.com`域名的访问反向代理到K8S中的`mall-gateway-service`服务上去; ``` server { listen 2080; server_name api.macrozheng.com; #修改域名 location / { proxy_set_header Host $host:$server_port; proxy_pass http://172.17.0.2:30201; #修改为代理服务地址 index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` - 再修改访问Linux服务器的本机host文件,添加如下记录; ``` 192.168.3.102 api.macrozheng.com ``` - 其实也可以不用Nginx来进行反向代理,创建容器的时候将`mall-gateway-service`的端口映射出来即可; ```bash docker run -p 80:80 -p 443:443 -p 8201:30201 --name rancher \ --privileged \ --restart=unless-stopped \ -d rancher/rancher:v2.5-head ``` - 通过Nginx即可访问`mall-swarm`的接口文档,访问地址:http://api.macrozheng.com:2080/doc.html ![](../images/mall_swarm_deploy_k8s_11.png) ## 总结 通过把`mall-swarm`项目部署到K8S上,我们可以发现K8S也没有想象中到那么难,K8S中的很多东西都是和Docker相通的!当我们听到消息称某个新技术要取代旧技术了,往往会困惑我们学的那些旧技术是不是过时了,没用了?其实我们大可不必担心,往往新技术都是在旧技术的基础上产生的,而且那些会旧技术的人会更容易掌握新技术! ## K8S系列教程 > 再次推荐一波我的K8S系列教程,K8S实战看这些就对了! - [K8S太火了!花10分钟玩转它不香么?](https://mp.weixin.qq.com/s/N-9xVPYO_VVL5JZu5UPbtQ) - [自从上了K8S,项目更新都不带停机的!](https://mp.weixin.qq.com/s/dwrxKr4ONfzCLn01QbkpQg) - [我把SpringBoot应用部署到了K8S上,怎么感觉用起来像Docker!](https://mp.weixin.qq.com/s/RXAQtsNXUS2kiSCMFDxKqA) - [再见命令行!K8S傻瓜式安装,图形化管理真香!](https://mp.weixin.qq.com/s/NOkaYGz9ZT3bsG1J7yXVRA) - [看看人家那开源项目文档写的,那叫一个友好!](https://mp.weixin.qq.com/s/a7w2qvDg8c2BEQU7A1NqaA) - [据说只有高端机器才配运行K8S,网友:1G内存的渣渣跑起来了!](https://mp.weixin.qq.com/s/iDG7Wzq9DQHLFPtnqokOxQ) - [再见 Docker !5分钟转型 containerd !](https://mp.weixin.qq.com/s/jBP7E7DL5vJ0zp5UnSFNBg) ## 项目源码地址 https://github.com/macrozheng/mall-swarm ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/deploy/mall_swarm_deploy_windows.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall-swarm在Windows环境下的部署 ## 开发环境搭建 > `mall-swarm`中使用到的环境和`mall`项目中大致相同,具体可以查看[mall在Windows环境下的部署](http://www.macrozheng.com/#/deploy/mall_deploy_windows)。 ### 简易环境搭建流程 - 安装IDEA并导入项目源码; - 安装MySQL,创建一个`mall`数据库,并导入`/document/sql/mall.sql`文件; - 安装Redis、Elasticsearch、MongoDB、RabbitMQ等环境。 ### Nacos注册中心搭建 - 由于使用了Nacos注册中心,我们需要先搭建Nacos注册中心,下载地址:https://github.com/alibaba/nacos/releases/download/1.3.1/nacos-server-1.3.1.zip - 下载完成后解压到指定文件夹,使用bin目录下的`startup.cmd`启动Nacos服务,访问地址:http://localhost:8848/nacos/ ![](../images/swarm_deploy_windows_01.png) - 将项目`config`目录下的配置文件添加到`Nacos`中,只要添加包含`dev`的配置即可,配置文件的文件名称需要和Nacos中的`Data Id`一一对应; ![](../images/swarm_deploy_windows_02.png) ![](../images/swarm_deploy_windows_03.png) ## 项目部署 > `mall-swarm`项目启动有先后顺序,大家可以按照以下顺序启动。 - 启动网关服务`mall-gateway`,直接运行`MallGatewayApplication`的main函数即可; - 启动认证中心`mall-auth`,直接运行`MallAuthApplication`的main函数即可; - 启动后台管理服务`mall-admin`,直接运行`MallAdminApplication`的main函数即可; - 启动前台服务`mall-portal`,直接运行`MallPortalApplication`的main函数即可; - 启动搜索服务`mall-search`,直接运行`MallSearchApplication`的main函数即可; - 启动监控中心`mall-monitor`,直接运行`MallMonitorApplication`的main函数即可; - 运行完成后可以通过监控中心查看监控信息,账号密码为`macro:123456`:http://localhost:8101 - 运行完成后可以直接通过如下地址访问API文档:http://localhost:8201/doc.html ![](../images/swarm_deploy_windows_04.png) - 如何访问需要登录的接口,先调用认证中心接口获取token,后台管理`client_id`和`client_secret`为`admin-app:123456`,前台系统为`portal-app:123456`; ![](../images/swarm_deploy_windows_05.png) - 然后将token添加到请求头中,即可访问需要权限的接口了。 ![](../images/swarm_deploy_windows_06.png) ## 效果展示 - 注册中心服务信息,访问地址:http://localhost:8848/nacos/ ![](../images/swarm_deploy_windows_07.png) - 监控中心服务概览信息,访问地址:http://localhost:8101 ![](../images/swarm_deploy_windows_08.png) ![](../images/swarm_deploy_windows_09.png) ![](../images/swarm_deploy_windows_10.png) - 日志收集系统信息,访问地址:http://localhost:5601 ![](../images/swarm_deploy_windows_11.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/foreword/mall_foreword_01.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall架构及功能概览 > mall架构、功能及数据库结构概览 ## mall项目简介 mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。 ## 项目演示 - 后台项目演示:[http://www.macrozheng.com/admin/index.html](http://www.macrozheng.com/admin/index.html) - 移动端项目演示:[http://www.macrozheng.com/app/index.html](http://www.macrozheng.com/app/index.html) ## mall中使用的技术 > mall采用现阶主流技术实现,涵盖了一般项目中几乎所有使用的技术。 技术 | 版本 | 说明 ----|----|---- Spring Boot | 2.3.0 | 容器+MVC框架 Spring Security | 5.1.4 | 认证和授权框架 MyBatis | 3.4.6 | ORM框架 MyBatisGenerator | 1.3.3 | 数据层代码生成 PageHelper | 5.1.8 | MyBatis物理分页插件 Swagger-UI | 2.9.2 | 文档生产工具 Elasticsearch | 7.6.2 | 搜索引擎 RabbitMq |3.7.14 | 消息队列 Redis | 5.0 | 分布式缓存 MongoDb | 4.2.5 | NoSql数据库 Docker | 18.09.0 | 应用容器引擎 Druid | 1.1.10 | 数据库连接池 OSS | 2.5.0 | 对象存储 JWT | 0.9.0 | JWT登录支持 Lombok | 1.18.6 | 简化对象封装工具 ## mall实现的功能概览 > 具体可以看下演示地址,亲自体验下:[http://www.macrozheng.com/admin/index.html](http://www.macrozheng.com/admin/index.html) - 商品模块 - 商品管理 - 商品分类管理 - 商品类型管理 - 品牌管理 - 订单模块 - 订单管理 - 订单设置 - 退货申请处理 - 退货原因设置 - 营销模块 - 秒杀活动管理 - 优惠价管理 - 品牌推荐管理 - 新品推荐管理 - 人气推荐管理 - 专题推荐管理 - 首页广告管理 ## mall数据库表概览 > mall项目目前有71张数据表,业务逻辑有一定复杂度,平时做项目参考也够了。 ![mall数据库表展示](../images/mall_mysql_all.png) ### 数据库表前缀说明 - cms_*:内容管理模块相关表 - oms_*:订单管理模块相关表 - pms_*:商品模块相关表 - sms_*:营销模块相关表 - ums_*:会员模块相关表 ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/foreword/mall_foreword_02.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # mall学习所需知识点 > 由于mall项目涉及到很多知识点,比如SpringBoot、ElasticSearch、Redis、Mongodb等,本教程不会详细讲述这些,只会讲述本项目相关部分,所以推荐以下资料。对其中一些知识点并不熟悉的同学,可以看下下面推荐的资料。 ## 推荐资料 ### IDEA 《IntelliJ-IDEA-Tutorial》:[https://github.com/judasn/IntelliJ-IDEA-Tutorial](https://github.com/judasn/IntelliJ-IDEA-Tutorial) > 特别全的IDEA使用教程,可以学到很多实用的技巧。 ### Spring 《Spring实战(第4版)》:[https://book.douban.com/subject/26767354/](https://book.douban.com/subject/26767354/) >经典的、畅销的Spring学习和实践指南,从此书可以学习到Spring的实用用法,对Spring有个整体的了解,推荐整本阅读。 ### SpringBoot 《Spring Boot实战》:[https://book.douban.com/subject/26857423/](https://book.douban.com/subject/26857423/) > SpringBoot的入门书,一共也就200多页,反正我是看完了,其中关于Groovy和Grails部分大可不看。 ### MyBatis 《MyBatis从入门到精通》:[https://book.douban.com/subject/27074809/](https://book.douban.com/subject/27074809/) > 很好的一本MyBatis入门书,作者是开源插件PageHelper的项目主,平时忘了MyBatis的一些用法的时候可以当工具书使用,推荐整本阅读 ### MySql 《深入浅出MySQL》:[https://book.douban.com/subject/25817684/](https://book.douban.com/subject/25817684/) >网易DBA写的一本MySql书籍,作为一个开发者,我们只要看第一部分基础篇、第二部分开发篇、第三部分优化篇即可。 ### Linux 《循序渐进Linux(第2版)》:[https://book.douban.com/subject/26758194/](https://book.douban.com/subject/26758194/) > 南非蚂蚁写的一本Linux书籍,作为一个开发者,我们只要看第一篇基础知识篇、第二篇服务器搭建篇即可,后面讲到生产环境部署项目会用到。 ### Elasticsearch 《Elasticsearch 权威指南》:[https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html](https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html) > Elasticsearch官方推荐的中文学习资料,基于Elasticsearch2.4.x版本,比较老,但是可堪一用。大多数成熟的框架,版本迭代用法相差不会很大。 《Elasticsearch 技术解析与实战》:[https://book.douban.com/subject/26967826/](https://book.douban.com/subject/26967826/) > 如果你觉得上面那本ElasticSearch版本太老的话可以看这本。 ### Mongodb 《MongoDB实战(第二版)》:[https://book.douban.com/subject/27061123/](https://book.douban.com/subject/27061123/) > 很好的一本MongoDB实战书,作者参与过MongoDB的驱动开发,感兴趣的可以都看下。 ### Docker 《Spring Cloud与Docker微服务架构实战》:[https://book.douban.com/subject/27028228/](https://book.douban.com/subject/27028228/) > 我们只需要看下这本书的Docker部分即可,后面讲到生产环境部署项目会用到。 ## 结语 > 如果你按照我的推荐看了以上部分的资料,或者你已经有了以上部分的基础,那么你学习mall的时候会非常顺利。 ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/index.html ================================================ Document
================================================ FILE: docs/lib/docsify/lib/plugins/ga.js ================================================ (function () { // From https://github.com/egoist/vue-ga/blob/master/src/index.js function appendScript() { var script = document.createElement('script'); script.async = true; script.src = 'https://www.google-analytics.com/analytics.js'; document.body.appendChild(script); } function init(id) { appendScript(); window.ga = window.ga || function () { (window.ga.q = window.ga.q || []).push(arguments); }; window.ga.l = Number(new Date()); window.ga('create', id, 'auto'); } function collect() { if (!window.ga) { init($docsify.ga); } window.ga('set', 'page', location.hash); window.ga('send', 'pageview'); } var install = function (hook) { if (!$docsify.ga) { console.error('[Docsify] ga is required.'); return } hook.beforeEach(collect); }; $docsify.plugins = [].concat(install, $docsify.plugins); }()); ================================================ FILE: docs/lib/docsify/lib/plugins/search.js ================================================ (function () { var INDEXS = {}; var LOCAL_STORAGE = { EXPIRE_KEY: 'docsify.search.expires', INDEX_KEY: 'docsify.search.index' }; function resolveExpireKey(namespace) { return namespace ? ((LOCAL_STORAGE.EXPIRE_KEY) + "/" + namespace) : LOCAL_STORAGE.EXPIRE_KEY } function resolveIndexKey(namespace) { return namespace ? ((LOCAL_STORAGE.INDEX_KEY) + "/" + namespace) : LOCAL_STORAGE.INDEX_KEY } function escapeHtml(string) { var entityMap = { '&': '&', '<': '<', '>': '>', '"': '"', '\'': ''', '/': '/' }; return String(string).replace(/[&<>"'/]/g, function (s) { return entityMap[s]; }) } function getAllPaths(router) { var paths = []; Docsify.dom.findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])').forEach(function (node) { var href = node.href; var originHref = node.getAttribute('href'); var path = router.parse(href).path; if ( path && paths.indexOf(path) === -1 && !Docsify.util.isAbsolutePath(originHref) ) { paths.push(path); } }); return paths } function saveData(maxAge, expireKey, indexKey) { localStorage.setItem(expireKey, Date.now() + maxAge); localStorage.setItem(indexKey, JSON.stringify(INDEXS)); } function genIndex(path, content, router, depth) { if ( content === void 0 ) content = ''; var tokens = window.marked.lexer(content); var slugify = window.Docsify.slugify; var index = {}; var slug; tokens.forEach(function (token) { if (token.type === 'heading' && token.depth <= depth) { slug = router.toURL(path, {id: slugify(token.text)}); index[slug] = {slug: slug, title: token.text, body: ''}; } else { if (!slug) { return } if (!index[slug]) { index[slug] = {slug: slug, title: '', body: ''}; } else if (index[slug].body) { index[slug].body += '\n' + (token.text || ''); } else { index[slug].body = token.text; } } }); slugify.clear(); return index } /** * @param {String} query * @returns {Array} */ function search(query) { var matchingResults = []; var data = []; Object.keys(INDEXS).forEach(function (key) { data = data.concat(Object.keys(INDEXS[key]).map(function (page) { return INDEXS[key][page]; })); }); query = query.trim(); var keywords = query.split(/[\s\-,\\/]+/); if (keywords.length !== 1) { keywords = [].concat(query, keywords); } var loop = function ( i ) { var post = data[i]; var matchesScore = 0; var resultStr = ''; var postTitle = post.title && post.title.trim(); var postContent = post.body && post.body.trim(); var postUrl = post.slug || ''; if (postTitle) { keywords.forEach( function (keyword) { // From https://github.com/sindresorhus/escape-string-regexp var regEx = new RegExp( keyword.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'), 'gi' ); var indexTitle = -1; var indexContent = -1; indexTitle = postTitle ? postTitle.search(regEx) : -1; indexContent = postContent ? postContent.search(regEx) : -1; if (indexTitle >= 0 || indexContent >= 0) { matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0; if (indexContent < 0) { indexContent = 0; } var start = 0; var end = 0; start = indexContent < 11 ? 0 : indexContent - 10; end = start === 0 ? 70 : indexContent + keyword.length + 60; if (end > postContent.length) { end = postContent.length; } var matchContent = '...' + escapeHtml(postContent) .substring(start, end) .replace(regEx, ("" + keyword + "")) + '...'; resultStr += matchContent; } }); if (matchesScore > 0) { var matchingPost = { title: escapeHtml(postTitle), content: postContent ? resultStr : '', url: postUrl, score: matchesScore }; matchingResults.push(matchingPost); } } }; for (var i = 0; i < data.length; i++) loop( i ); return matchingResults.sort(function (r1, r2) { return r2.score - r1.score; }); } function init(config, vm) { var isAuto = config.paths === 'auto'; var expireKey = resolveExpireKey(config.namespace); var indexKey = resolveIndexKey(config.namespace); var isExpired = localStorage.getItem(expireKey) < Date.now(); INDEXS = JSON.parse(localStorage.getItem(indexKey)); if (isExpired) { INDEXS = {}; } else if (!isAuto) { return } var paths = isAuto ? getAllPaths(vm.router) : config.paths; var len = paths.length; var count = 0; paths.forEach(function (path) { if (INDEXS[path]) { return count++ } Docsify .get(vm.router.getFile(path), false, vm.config.requestHeaders) .then(function (result) { INDEXS[path] = genIndex(path, result, vm.router, config.depth); len === ++count && saveData(config.maxAge, expireKey, indexKey); }); }); } var NO_DATA_TEXT = ''; var options; function style() { var code = "\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"; Docsify.dom.style(code); } function tpl(defaultValue) { if ( defaultValue === void 0 ) defaultValue = ''; var html = "
\n \n
\n \n \n \n \n \n
\n
\n
\n "; var el = Docsify.dom.create('div', html); var aside = Docsify.dom.find('aside'); Docsify.dom.toggleClass(el, 'search'); Docsify.dom.before(aside, el); } function doSearch(value) { var $search = Docsify.dom.find('div.search'); var $panel = Docsify.dom.find($search, '.results-panel'); var $clearBtn = Docsify.dom.find($search, '.clear-button'); var $sidebarNav = Docsify.dom.find('.sidebar-nav'); var $appName = Docsify.dom.find('.app-name'); if (!value) { $panel.classList.remove('show'); $clearBtn.classList.remove('show'); $panel.innerHTML = ''; if (options.hideOtherSidebarContent) { $sidebarNav.classList.remove('hide'); $appName.classList.remove('hide'); } return } var matchs = search(value); var html = ''; matchs.forEach(function (post) { html += ""; }); $panel.classList.add('show'); $clearBtn.classList.add('show'); $panel.innerHTML = html || ("

" + NO_DATA_TEXT + "

"); if (options.hideOtherSidebarContent) { $sidebarNav.classList.add('hide'); $appName.classList.add('hide'); } } function bindEvents() { var $search = Docsify.dom.find('div.search'); var $input = Docsify.dom.find($search, 'input'); var $inputWrap = Docsify.dom.find($search, '.input-wrap'); var timeId; // Prevent to Fold sidebar Docsify.dom.on( $search, 'click', function (e) { return e.target.tagName !== 'A' && e.stopPropagation(); } ); Docsify.dom.on($input, 'input', function (e) { clearTimeout(timeId); timeId = setTimeout(function (_) { return doSearch(e.target.value.trim()); }, 100); }); Docsify.dom.on($inputWrap, 'click', function (e) { // Click input outside if (e.target.tagName !== 'INPUT') { $input.value = ''; doSearch(); } }); } function updatePlaceholder(text, path) { var $input = Docsify.dom.getNode('.search input[type="search"]'); if (!$input) { return } if (typeof text === 'string') { $input.placeholder = text; } else { var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0]; $input.placeholder = text[match]; } } function updateNoData(text, path) { if (typeof text === 'string') { NO_DATA_TEXT = text; } else { var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0]; NO_DATA_TEXT = text[match]; } } function updateOptions(opts) { options = opts; } function init$1(opts, vm) { var keywords = vm.router.parse().query.s; updateOptions(opts); style(); tpl(keywords); bindEvents(); keywords && setTimeout(function (_) { return doSearch(keywords); }, 500); } function update(opts, vm) { updateOptions(opts); updatePlaceholder(opts.placeholder, vm.route.path); updateNoData(opts.noData, vm.route.path); } var CONFIG = { placeholder: 'Type to search', noData: 'No Results!', paths: 'auto', depth: 2, maxAge: 86400000, // 1 day hideOtherSidebarContent: false, namespace: undefined }; var install = function (hook, vm) { var util = Docsify.util; var opts = vm.config.search || CONFIG; if (Array.isArray(opts)) { CONFIG.paths = opts; } else if (typeof opts === 'object') { CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto'; CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge; CONFIG.placeholder = opts.placeholder || CONFIG.placeholder; CONFIG.noData = opts.noData || CONFIG.noData; CONFIG.depth = opts.depth || CONFIG.depth; CONFIG.hideOtherSidebarContent = opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent; CONFIG.namespace = opts.namespace || CONFIG.namespace; } var isAuto = CONFIG.paths === 'auto'; hook.mounted(function (_) { init$1(CONFIG, vm); !isAuto && init(CONFIG, vm); }); hook.doneEach(function (_) { update(CONFIG, vm); isAuto && init(CONFIG, vm); }); }; $docsify.plugins = [].concat(install, $docsify.plugins); }()); ================================================ FILE: docs/lib/docsify/lib/themes/vue.css ================================================ @import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");*{-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:none;-webkit-touch-callout:none;box-sizing:border-box}body:not(.ready){overflow:hidden}body:not(.ready) .app-nav,body:not(.ready)>nav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:"Loading..."}.emoji{height:1.2rem;vertical-align:middle}.progress{background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:999999}.search .search-keyword,.search a:hover{color:var(--theme-color,#42b983)}.search .search-keyword{font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}li input[type=checkbox]{margin:0 .2em .25em 0;vertical-align:middle}.app-nav{margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:10}.app-nav.no-badge{margin-right:25px}.app-nav p{margin:0}.app-nav>a{margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a.active,.app-nav a:hover{color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 1rem;padding:5px 0;position:relative}.app-nav li ul{background-color:#fff;border:1px solid;border-color:#ddd #ddd #ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:auto;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:1rem;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner:hover .octo-arm{-webkit-animation:octocat-wave .56s ease-in-out;animation:octocat-wave .56s ease-in-out}.github-corner svg{color:#fff;fill:var(--theme-color,#42b983);height:80px;width:80px}main{display:block;position:relative;width:100vw;height:100%;z-index:0}main.hidden{display:none}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;position:absolute;top:0;bottom:0;left:0;transition:transform .25s ease-out;width:300px;z-index:20}.sidebar>h1{margin:0 auto 1rem;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar li.collapse .app-sub-sidebar{display:none}.sidebar ul{margin:0 0 0 15px;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53.3%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53.3%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;position:absolute;bottom:0;left:0;text-align:center;transition:opacity .3s;width:284px;z-index:30}.sidebar-toggle .sidebar-toggle-button:hover{opacity:.4}.sidebar-toggle span{background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;position:absolute;top:0;right:0;bottom:0;left:300px;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:800px;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section iframe{border:1px solid #eee;width:1px;min-width:100%}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section p.tip,.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:"!";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;height:20px;width:20px;text-align:center;top:14px}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:1rem}.markdown-section ul.task-list>li{list-style-type:none}body.close .sidebar{transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;overflow-x:hidden}.sidebar{left:-300px;transition:transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:transform .25s ease}.app-nav,.github-corner{transition:transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto;padding:30px 30px 10px 10px}body.close .sidebar{transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px;padding:10px}body.close .content{transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner:hover .octo-arm{-webkit-animation:none;animation:none}.github-corner .octo-arm{-webkit-animation:octocat-wave .56s ease-in-out;animation:octocat-wave .56s ease-in-out}}@-webkit-keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}section.cover{align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;display:none}section.cover.show{display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;top:0;height:100%;width:100%}section.cover .cover-main{flex:1;margin:-20px 16px 0;text-align:center;z-index:1}section.cover a{color:inherit}section.cover a,section.cover a:hover{text-decoration:none}section.cover p{line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:2.5rem;font-weight:300;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-.4375rem;font-size:1rem;position:absolute}section.cover blockquote{font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border-radius:2rem;border:1px solid var(--theme-color,#42b983);box-sizing:border-box;color:var(--theme-color,#42b983);display:inline-block;font-size:1.05rem;letter-spacing:.1rem;margin:.5rem 1rem;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:var(--theme-color,#42b983);color:#fff}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:var(--theme-color,#42b983)}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:"-";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:2rem;margin:0 0 1rem}.markdown-section h2{font-size:1.75rem;margin:45px 0 .8rem}.markdown-section h3{font-size:1.5rem;margin:40px 0 .6rem}.markdown-section h4{font-size:1.25rem}.markdown-section h5{font-size:1rem}.markdown-section h6{color:#777;font-size:1rem}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:1.6rem;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code{border-radius:2px;color:#e96900;font-size:.8rem;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section code,.markdown-section pre{background-color:#f8f8f8;font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section pre{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;line-height:1.5rem;margin:1.2em 0;overflow:auto;padding:0 1.4rem;position:relative;word-wrap:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:var(--theme-color,#42b983)}.token.function,.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;background-color:#f8f8f8;border-radius:2px;color:#525252;display:block;font-family:Roboto Mono,Monaco,courier,monospace;font-size:.8rem;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;padding:2.2em 5px;white-space:inherit}.markdown-section code:after,.markdown-section code:before{letter-spacing:.05rem}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:1.5rem;position:relative;left:auto}pre:after{color:#ccc;content:attr(data-lang);font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0} ================================================ FILE: docs/lib/prismjs/components/prism-bash.js ================================================ (function(Prism) { // $ set | grep '^[A-Z][^[:space:]]*=' | cut -d= -f1 | tr '\n' '|' // + LC_ALL, RANDOM, REPLY, SECONDS. // + make sure PS1..4 are here as they are not always set, // - some useless things. var envVars = '\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b'; var insideString = { 'environment': { pattern: RegExp("\\$" + envVars), alias: 'constant' }, 'variable': [ // [0]: Arithmetic Environment { pattern: /\$?\(\([\s\S]+?\)\)/, greedy: true, inside: { // If there is a $ sign at the beginning highlight $(( and )) as variable 'variable': [ { pattern: /(^\$\(\([\s\S]+)\)\)/, lookbehind: true }, /^\$\(\(/ ], 'number': /\b0x[\dA-Fa-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee]-?\d+)?/, // Operators according to https://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic 'operator': /--?|-=|\+\+?|\+=|!=?|~|\*\*?|\*=|\/=?|%=?|<<=?|>>=?|<=?|>=?|==?|&&?|&=|\^=?|\|\|?|\|=|\?|:/, // If there is no $ sign at the beginning highlight (( and )) as punctuation 'punctuation': /\(\(?|\)\)?|,|;/ } }, // [1]: Command Substitution { pattern: /\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/, greedy: true, inside: { 'variable': /^\$\(|^`|\)$|`$/ } }, // [2]: Brace expansion { pattern: /\$\{[^}]+\}/, greedy: true, inside: { 'operator': /:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/, 'punctuation': /[\[\]]/, 'environment': { pattern: RegExp("(\\{)" + envVars), lookbehind: true, alias: 'constant' } } }, /\$(?:\w+|[#?*!@$])/ ], // Escape sequences from echo and printf's manuals, and escaped quotes. 'entity': /\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|x[0-9a-fA-F]{1,2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})/ }; Prism.languages.bash = { 'shebang': { pattern: /^#!\s*\/.*/, alias: 'important' }, 'comment': { pattern: /(^|[^"{\\$])#.*/, lookbehind: true }, 'function-name': [ // a) function foo { // b) foo() { // c) function foo() { // but not “foo {” { // a) and c) pattern: /(\bfunction\s+)\w+(?=(?:\s*\(?:\s*\))?\s*\{)/, lookbehind: true, alias: 'function' }, { // b) pattern: /\b\w+(?=\s*\(\s*\)\s*\{)/, alias: 'function' } ], // Highlight variable names as variables in for and select beginnings. 'for-or-select': { pattern: /(\b(?:for|select)\s+)\w+(?=\s+in\s)/, alias: 'variable', lookbehind: true }, // Highlight variable names as variables in the left-hand part // of assignments (“=” and “+=”). 'assign-left': { pattern: /(^|[\s;|&]|[<>]\()\w+(?=\+?=)/, inside: { 'environment': { pattern: RegExp("(^|[\\s;|&]|[<>]\\()" + envVars), lookbehind: true, alias: 'constant' } }, alias: 'variable', lookbehind: true }, 'string': [ // Support for Here-documents https://en.wikipedia.org/wiki/Here_document { pattern: /((?:^|[^<])<<-?\s*)(\w+?)\s*(?:\r?\n|\r)(?:[\s\S])*?(?:\r?\n|\r)\2/, lookbehind: true, greedy: true, inside: insideString }, // Here-document with quotes around the tag // → No expansion (so no “inside”). { pattern: /((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s*(?:\r?\n|\r)(?:[\s\S])*?(?:\r?\n|\r)\3/, lookbehind: true, greedy: true }, // “Normal” string { pattern: /(["'])(?:\\[\s\S]|\$\([^)]+\)|`[^`]+`|(?!\1)[^\\])*\1/, greedy: true, inside: insideString } ], 'environment': { pattern: RegExp("\\$?" + envVars), alias: 'constant' }, 'variable': insideString.variable, 'function': { pattern: /(^|[\s;|&]|[<>]\()(?:add|apropos|apt|aptitude|apt-cache|apt-get|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/, lookbehind: true }, 'keyword': { pattern: /(^|[\s;|&]|[<>]\()(?:if|then|else|elif|fi|for|while|in|case|esac|function|select|do|done|until)(?=$|[)\s;|&])/, lookbehind: true }, // https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html 'builtin': { pattern: /(^|[\s;|&]|[<>]\()(?:\.|:|break|cd|continue|eval|exec|exit|export|getopts|hash|pwd|readonly|return|shift|test|times|trap|umask|unset|alias|bind|builtin|caller|command|declare|echo|enable|help|let|local|logout|mapfile|printf|read|readarray|source|type|typeset|ulimit|unalias|set|shopt)(?=$|[)\s;|&])/, lookbehind: true, // Alias added to make those easier to distinguish from strings. alias: 'class-name' }, 'boolean': { pattern: /(^|[\s;|&]|[<>]\()(?:true|false)(?=$|[)\s;|&])/, lookbehind: true }, 'file-descriptor': { pattern: /\B&\d\b/, alias: 'important' }, 'operator': { // Lots of redirections here, but not just that. pattern: /\d?<>|>\||\+=|==?|!=?|=~|<<[<-]?|[&\d]?>>|\d?[<>]&?|&[>&]?|\|[&|]?|<=?|>=?/, inside: { 'file-descriptor': { pattern: /^\d/, alias: 'important' } } }, 'punctuation': /\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/, 'number': { pattern: /(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/, lookbehind: true } }; /* Patterns in command substitution. */ var toBeCopied = [ 'comment', 'function-name', 'for-or-select', 'assign-left', 'string', 'environment', 'function', 'keyword', 'builtin', 'boolean', 'file-descriptor', 'operator', 'punctuation', 'number' ]; var inside = insideString.variable[1].inside; for(var i = 0; i < toBeCopied.length; i++) { inside[toBeCopied[i]] = Prism.languages.bash[toBeCopied[i]]; } Prism.languages.shell = Prism.languages.bash; })(Prism); ================================================ FILE: docs/lib/prismjs/components/prism-java.js ================================================ (function (Prism) { var keywords = /\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|null|open|opens|package|private|protected|provides|public|requires|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/; // based on the java naming conventions var className = /\b[A-Z](?:\w*[a-z]\w*)?\b/; Prism.languages.java = Prism.languages.extend('clike', { 'class-name': [ className, // variables and parameters // this to support class names (or generic parameters) which do not contain a lower case letter (also works for methods) /\b[A-Z]\w*(?=\s+\w+\s*[;,=())])/ ], 'keyword': keywords, 'function': [ Prism.languages.clike.function, { pattern: /(\:\:)[a-z_]\w*/, lookbehind: true } ], 'number': /\b0b[01][01_]*L?\b|\b0x[\da-f_]*\.?[\da-f_p+-]+\b|(?:\b\d[\d_]*\.?[\d_]*|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i, 'operator': { pattern: /(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m, lookbehind: true } }); Prism.languages.insertBefore('java', 'string', { 'triple-quoted-string': { // http://openjdk.java.net/jeps/355#Description pattern: /"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/, greedy: true, alias: 'string' } }); Prism.languages.insertBefore('java', 'class-name', { 'annotation': { alias: 'punctuation', pattern: /(^|[^.])@\w+/, lookbehind: true }, 'namespace': { pattern: /(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)[a-z]\w*(?:\.[a-z]\w*)+/, lookbehind: true, inside: { 'punctuation': /\./, } }, 'generics': { pattern: /<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<[\w\s,.&?]*>)*>)*>)*>/, inside: { 'class-name': className, 'keyword': keywords, 'punctuation': /[<>(),.:]/, 'operator': /[?&|]/ } } }); }(Prism)); ================================================ FILE: docs/lib/prismjs/components/prism-sql.js ================================================ Prism.languages.sql = { 'comment': { pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/, lookbehind: true }, 'variable': [ { pattern: /@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/, greedy: true }, /@[\w.$]+/ ], 'string': { pattern: /(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/, greedy: true, lookbehind: true }, 'function': /\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i, // Should we highlight user defined functions too? 'keyword': /\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:_INSERT|COL)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURNS?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i, 'boolean': /\b(?:TRUE|FALSE|NULL)\b/i, 'number': /\b0x[\da-f]+\b|\b\d+\.?\d*|\B\.\d+\b/i, 'operator': /[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|IN|LIKE|NOT|OR|IS|DIV|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i, 'punctuation': /[;[\]()`,.]/ }; ================================================ FILE: docs/lib/prismjs/components/prism-yaml.js ================================================ Prism.languages.yaml = { 'scalar': { pattern: /([\-:]\s*(?:![^\s]+)?[ \t]*[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)[^\r\n]+(?:\2[^\r\n]+)*)/, lookbehind: true, alias: 'string' }, 'comment': /#.*/, 'key': { pattern: /(\s*(?:^|[:\-,[{\r\n?])[ \t]*(?:![^\s]+)?[ \t]*)[^\r\n{[\]},#\s]+?(?=\s*:\s)/, lookbehind: true, alias: 'atrule' }, 'directive': { pattern: /(^[ \t]*)%.+/m, lookbehind: true, alias: 'important' }, 'datetime': { pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?)?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?)(?=[ \t]*(?:$|,|]|}))/m, lookbehind: true, alias: 'number' }, 'boolean': { pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:true|false)[ \t]*(?=$|,|]|})/im, lookbehind: true, alias: 'important' }, 'null': { pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:null|~)[ \t]*(?=$|,|]|})/im, lookbehind: true, alias: 'important' }, 'string': { pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)("|')(?:(?!\2)[^\\\r\n]|\\.)*\2(?=[ \t]*(?:$|,|]|}|\s*#))/m, lookbehind: true, greedy: true }, 'number': { pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+\.?\d*|\.?\d+)(?:e[+-]?\d+)?|\.inf|\.nan)[ \t]*(?=$|,|]|})/im, lookbehind: true }, 'tag': /![^\s]+/, 'important': /[&*][\w]+/, 'punctuation': /---|[:[\]{}\-,|>?]|\.\.\./ }; Prism.languages.yml = Prism.languages.yaml; ================================================ FILE: docs/mine/mall_learning_path.md ================================================ # Mall电商实战项目专属学习路线,主流技术一网打尽! > 之前经常有朋友问我,mall项目该如何学习,按照什么顺序学习?我一般都会把《mall学习教程》的目录发给他,希望他按照教程顺序学习。我一直认为基于项目实战的学习,可以更好的掌握技术。mall项目是个综合项目,学习它既可以掌握主流技术又可以获得项目经验。为了让大家学习mall项目时,少走弯路,最近又整理了个更好的学习路线,希望对大家有所帮助! ## 推荐资料 由于mall项目涵盖了现阶段主流技术,如果你是个Java初学者的话,最好先看下面的资料打个基础,资料具体介绍可以参考[mall学习所需知识点](../foreword/mall_foreword_01.md)。 ![学习资料](../images/mall_learning_path_01.svg) ## 学习后端技术栈 如果你对Java有一定的经验,基础还可以的话,直接学习mall项目中所运用的技术即可。学习开源项目的第一步,一般都是要把开源项目中运用的技术学习一遍,这样可以为你搭建项目和阅读项目源码打下扎实的基础。其实在我们学习开源项目的时候,最主要的还是学习项目中所用到的技术,业务反而是次要的。因为我们做不同项目的时候,业务都会有所不同,反而使用到的技术却相差不多。 我们先来看下,mall项目中用到了哪些主流技术: ![学习后端技术栈](../images/mall_learning_path_02.svg) 在《mall学习教程》参考篇当中,很多主流技术都有讲解了,其实学习这些知识点,看这些文章基本就够了。学习mall项目遇到没学过的技术不愁,看《mall学习教程》参考篇就对了! 项目框架搭建方面的技术可以参考如下文章: - [Spring Boot入门教程](../reference/springboot_start.md) - [Spring Boot整合MyBatis,并使用MyBatis Generator生成代码](../reference/mybatis_generator_start.md) - [Spring Boot整合Swagger使用教程](../reference/swagger_starter.md) - [Lombok使用教程](../reference/lombok_start.md) - [Hutool使用教程](../reference/hutool_start.md) 项目数据存储方面的技术可以参考如下文章: - [常用MySQL命令整理](../reference/mysql.md) - [Spring Boot整合Redis使用教程](../reference/spring_data_redis.md) - [Elasticsearch入门教程](../reference/elasticsearch_start.md) - [MongoDB入门教程](../reference/mongodb_start.md) - [MinIO入门教程](../reference/minio.md) 项目运维部署方面的技术可以参考如下文章: - [在虚拟机中安装使用Linux的教程](../reference/linux_install.md) - [常用Linux命令整理](../reference/linux_command.md) - [常用Docker命令整理](../reference/docker_command.md) - [使用Maven插件为Spring Boot应用构建Docker镜像](../reference/docker_maven.md) - [使用Docker Compose部署SpringBoot应用](../reference/docker_compose.md) - [Nginx使用教程](../reference/nginx.md) - [Nginx支持HTTPS](../reference/nginx_https_start.md) - [使用Jenkins自动化部署Spring Boot应用](../reference/jenkins.md) - [使用Jenkins自动化部署Vue前端应用](../reference/jenkins_vue.md) 其他项目中运用到的技术可以参考: - [RabbitMQ使用教程](../reference/rabbitmq_start.md) - [ELK日志收集系统搭建教程](../reference/mall_elk_advance.md) - [Kibana设置密码保护教程](../reference/elk_security.md) ## 搭建项目骨架 在我们平时开发项目的时候,一般会先搭建一个项目骨架,当我们能自己搭建项目骨架,并使用它开发一些功能的时候,基本就能熟练运用骨架中的技术了。学会搭建项目骨架,离成为独当一面的程序员就不远了。《mall学习教程》中的架构篇就是一套教你搭建项目骨架的教程,照着下面的教程一步步搭建,就可以搭建出一个mall项目正在使用的项目骨架。 先看下都有哪些步骤: ![搭建项目骨架](../images/mall_learning_path_03.svg) 项目骨架搭建可以参考如下文章: - [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学习教程》参考篇中提到的运维部署技术,就可以将mall项目跑起来了,这里提供了多种部署方式,总有一种适合你的。 看下mall项目都提供了哪些部署方式: ![项目部署](../images/mall_learning_path_04.svg) 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前端项目部署可以参考如下文章: - [mall前端项目的安装与部署](../deploy/mall_deploy_web.md) ## 学习电商业务 项目跑起来以后,熟悉一下项目有哪些功能是很有必要的。一边熟悉功能,一边熟悉表结构,如果你能把表和相关功能都对应上,那么学习mall项目的业务基本就没什么问题了。 下面是mall中已经实现了的业务模块,mall项目中的功能结构可以直接参考:[mall数据库表结构概览](../database/mall_database_overview.md) ![电商业务](../images/mall_learning_path_05.svg) 如果你想知道更细致的数据库表和功能的对应关系,可以参考《mall学习教程》业务篇中的文章: - [商品模块数据库表解析(一)](../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) ## 解析技术要点 熟悉了项目的业务之后,接下来就是阅读项目的源码了,通过阅读源码可以更清晰地知道每个功能的具体实现。我们可以按模块来学习项目源码,比如按照`权限管理->商品模块->订单模块->营销模块`这样的顺序。通过使用功能,捕获功能中调用的接口路径,然后查看接口的代码来阅读源码。 来看下都有哪些技术要点值得解析的: ![技术要点](../images/mall_learning_path_06.svg) 如果你在项目中遇到了一些问题,可以参考《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 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) - [给Swagger升级了新版本,没想到居然有这么多坑!](../technology/swagger_upgrade.md) - [Elasticsearch 升级 7.x 版本后,我感觉掉坑里了!](../technology/elasticsearch_upgrade.md) - [搞定Mall项目中的权限管理功能,弄懂这些问题就妥了!](../technology/mall_permission_question.md) ## 学习前端技术栈 如何你想做个全栈开发的话,可以学习下mall的前端项目`mall-admin-web`。`mall-admin-web`采用Vue+Element-UI实现,《mall学习教程》中并没有专门的前端教程,学习前端的话这里有个学习方法,具体参考[如何在5天内学会Vue?聊聊我的学习方法!](../mine/vue_learning.md)。当你掌握了下面这些前端技术之后,可以看下`mall-admin-web`的源码,还是推荐按模块学习,可以先看下权限管理模块的源码,看懂这个模块,基本就可以看懂所有前端源码了。 ![前端技术栈](../images/mall_learning_path_07.svg) ## 进阶微服务 最近微服务技术很火,Java程序员哪有不会点微服务技术的?Spring Cloud是Java体系中主流的微服务技术,mall-swarm是mall项目的微服务版本,基于Spring Cloud技术栈。如果你想进阶学习微服务的话,学习mall-swarm项目准没错。下面是我们学习微服务需要掌握的一些内容,一份涵盖Spring Cloud&Alibaba核心组件的学习教程。 ![进阶微服务](../images/mall_learning_path_08.svg) 首先我们得学习下Spring Cloud&Alibaba中的核心组件,对Spring Cloud技术栈有个深入的了解,可以参考下面的文章: - [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 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) 接下来就是把mall-swarm项目跑起来了,mall-swarm项目部署可以参考下面的文章: - [mall-swarm在Windows环境下的部署](../deploy/mall_swarm_deploy_windows.md) - [mall-swarm在Linux环境下的部署(基于Docker容器)](../deploy/mall_swarm_deploy_docker.md) - [mall-swarm在Linux环境下的自动化部署(基于Jenkins)](../deploy/mall_deploy_jenkins.md) 想要更加深入学习mall-swarm项目,就要阅读项目源码了,遇到不懂的知识点可以参考下面的文章: - [微服务网关Gateway跨域问题解决](../technology/gateway_cors.md) - [使用SpringBoot Admin监控微服务应用](../cloud/admin.md) - [使用APM工具监控微服务应用性能](../reference/elastic_apm_start.md) - [基于Gateway+Oauth2的微服务权限解决方案](../cloud/gateway_oauth2.md) - [Gateway+Oauth2自定义处理结果](../cloud/gateway_oauth2.md) - [使用Knife4j聚合微服务接口文档](../cloud/knife4j_cloud.md) ## 开发工具使用 熟练掌握一些开发工具,对提高开发效率很有帮助。在开发mall项目的过程中,我把一些工具的使用技巧都整理成了文章,希望对大家有所帮助。 ![开发工具](../images/mall_learning_path_09.svg) IDEA使用技巧可以参考: - [IDEA常用设置](../reference/idea.md) - [IDEA推荐插件](../reference/idea_plugins.md) - [IDEA中的Git操作](../reference/idea_git.md) - [IDEA中创建和启动SpringBoot应用的技巧](../reference/idea_springboot.md) - [IDEA调试技巧](../reference/my_debug_skill.md) Navicat使用技巧可以参考: - [Navicat实现数据备份和结构同步](../reference/navicat.md) - [Navicat设计数据库](../reference/navicat_designer.md) Postman使用技巧可以参考: - [Postman API接口调试](../reference/postman.md) - [Postman 增强Swagger功能](../reference/swagger_postman.md) Git仓库的两种搭建方式和使用可以参考: - [Gitlab搭建与使用](../reference/gitlab.md) - [Gogs搭建与使用](../reference/gogs_start.md) 其他工具使用技巧可以参考: - [Arthas线上调试工具的使用](../reference/arthas_start.md) - [界面漂亮的Redis客户端使用](../reference/redis_desktop_start.md) - [MySQL官方客户端的使用](../reference/mysql_workbench.md) - [IDEA同款数据库管理工具DataGrip的使用](../reference/datagrip_start.md) 另外推荐一些我常用的开发工具和在线工具网站: - 开发工具:[我常用的开发工具](../reference/my_tools.md) - 在线工具:[我常用的在线工具网站](../reference/my_web_tools.md) ## 扩展学习 除了mall项目中涉及到的一些技术,还有一些其他实用的技术,或者是项目中某些技术的替代技术,我也写成了文章,大家有兴趣的话也可以看下。具体涵盖内容如下: ![扩展学习](../images/mall_learning_path_10.svg) MySQL实用技术,主从复制、读写分离与数据同步: - [MySql主从复制搭建](../reference/mysql_master_slave.md) - [小米开源的读写分离数据库中间件Gaea的使用](../reference/gaea.md) - [MySQL数据同步工具Canal的使用](../reference/canal_start.md) 另外两种MyBatis增强工具使用方法: - [MyBatis-Plus 使用教程](../reference/mybatis_plus_start.md) - [MyBatis新特性动态SQL的使用](../reference/mybatis_dynamic_sql.md) 另外两种日志收集工具: - [轻量级日志收集工具Fluentd的使用](../reference/efk_fluent.md) - [轻量级日志收集工具Filebeat的使用](../reference/filebeat_start.md) 另外两种API文档生成工具: - [API文档生成工具Knife4j的使用](../reference/knife4j_start.md) - [API文档生成工具YApi的使用](../reference/yapi_start.md) Docker相关实用技术: - [如何安全访问Docker服务](../reference/docker_protect_socket.md) - [fabric8io出品的Docker Maven插件的使用](../reference/maven_docker_fabric8.md) - [SpringBoot官方Docker插件的使用](../reference/springboot_docker_plugin.md) - [使用Dockerfile为SpringBoot应用构建Docker镜像](../reference/docker_file.md) - [企业级镜像仓库Harbor的使用](../reference/harbor_start.md) 另外两种定时任务框架的使用: - [SpringBoot官方支持任务调度框架Quartz的使用](../reference/quartz_start.md) - [可视化任务调度框架PowerJob的使用](../reference/power_job_start.md) 其他实用技术: - [Redis集群的搭建和使用](../reference/redis_cluster.md) - [一个很受欢迎的JWT库的使用](../reference/jose_jwt_start.md) - [RabbitMQ实现即时通讯](../reference/rabbitmq_mqtt_start.md) - [使用SQL查询Elasticsearch中的数据](../reference/elasticsearch_sql_start.md) - [Jenkins自动化部署技巧](../technology/springboot_auto_deploy.md) ## 总结 《mall学习教程》已更新130+篇原创文章。《mall学习教程》其实已经不仅仅是一个项目的学习教程了,可以认为是项目实战驱动的Java学习教程。教程中所涉及到的技术,可以运用到其他很多项目中去。按《mall学习教程》学习,你既可以掌握主流技术,又可以获取项目实战经验,学完之后自己写个开源项目,此时你已成为独当一面的程序员了! ## 项目地址 - mall:https://github.com/macrozheng/mall - mall-admin-web:https://github.com/macrozheng/mall-admin-web - mall-learning:https://github.com/macrozheng/mall-learning - mall-swarm:https://github.com/macrozheng/mall-swarm ## 完整思维导图 ![mall学习路线](../images/mall_learning_path_11.svg) ================================================ FILE: docs/mine/vue_learning.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 如何在5天内学会Vue?聊聊我的学习方法! > 先说下为什么用学习Vue这个例子来谈我的学习方法?其实关注我的朋友很多都是从我的Github上面来的,大多数都是Java后端开发者,Vue作为一种前端技术,掌握的人并不多。所以使用Vue从零开始的学习过程为例子,对于大家学习新技术有很好的借鉴价值,接下来我们来聊聊我的学习方法吧! ## 明确目标 我们在学习某项新技术的时候,一定是有`目标`的,有`目标`的学习才不是瞎忙。比如说我学习Vue的目标是啥,其实就是`做个后台管理系统的前端界面`,就是长下面这样的! ![](../images/vue_learning_01.png) 做前端有很多技术,最流行的无外乎这三个:Vue、React、Angular,但是我为什么选择了Vue呢?起初的时候这三种都研究了下,首先研究的是React,但是看了一下语法,感觉和自己以前的习惯不太符合(以前学过一点JavaScript)就没选它,Angular倒是研究了一整子,用了一段时间TypeScript还是不太习惯,最终还是选择了Vue。 选择Vue的原因主要有这三个: - 首先Vue在Github上面`Star最多`,既然它能排第一,总有它的优点; - 其次是它所提倡的`渐进式学习`,从JavaScript过度到Vue是很平滑的,没有陡峭的学习曲线; - 还有就是它的`中文文档非常完善`,有利于我们的学习。 ## 基础学习 选定了我们需要学习的技术以后,接下来就是学习Vue的基础了。`强烈建议`看一遍官方教程,我学习新技术的时候,一般官方文档有中文的我都会看一遍。 官方文档地址:https://cn.vuejs.org/v2/guide/ 刚开始学习的时候只看`教程`这块即可: ![](../images/vue_learning_02.png) 说说我看文档时的一些思考吧,核心是以`理解`为主,难以理解的内容可以暂时跳过。不需要去死磕一些难以理解的部分,有可能你当时死磕的东西以后根本用不上。快速把文档看一遍,扩充你对这门新技术的认知才是最重要的。你对新技术认知越足,你就越能更好地去运用它。 ## 项目导向 当你对Vue有一定的了解之后,你就可以去找一个和你的目标最接近的开源项目,借鉴开源项目中所使用的技术栈,快速完成你的目标。 怎么找到这个项目呢?直接去Github上面找就可以了,直接搜索关键字`Vue`,按`Star数最多`排行! ![](../images/vue_learning_03.png) 于是我们就找到了`vue-element-admin`这个开源项目:https://github.com/PanJiaChen/vue-element-admin 一般这种前端项目都会有在线预览的地址,我们先看下:https://panjiachen.github.io/vue-element-admin ![](../images/vue_learning_04.png) 这个项目和我们的目标很符合,不过看着太复杂了,我们刚刚学完Vue的基础,学它就和劝退差不多了。算了我们还是先去它的官方文档上面看看:https://panjiachen.gitee.io/vue-element-admin-site/zh/guide/ ![](../images/vue_learning_05.png) 我们可以发现原来还有一个叫`vue-admin-template`的基础模板项目:https://github.com/PanJiaChen/vue-admin-template 看了一下它的预览页面,发现这个比上面的简单多了,就决定是它了。 ![](../images/vue_learning_06.png) ## 扩充知识面 找到目标项目以后,我们就要开始学习它了。学习开源项目,我们暂时不用去看它的源码,先去学习它的技术栈。对它所用技术都有所了解以后,学它就轻松了! 一般情况下,开源项目用到的技术都会在README中说明,但这个项目刚好没有。对于Vue项目只要看下它的`package.json`文件我们就能大概了解它的技术栈了。 ```json { "dependencies": { "axios": "0.18.1", "element-ui": "2.13.0", "js-cookie": "2.2.0", "normalize.css": "7.0.0", "nprogress": "0.2.0", "path-to-regexp": "2.4.0", "vue": "2.6.10", "vue-router": "3.0.6", "vuex": "3.1.0" } } ``` 接下来我们要做的就是学习技术栈里面所有的技术,还是`理解`为主,扩充自己的知识面。 ### axios 一款基于JavaScript的http请求客户端,用于在浏览器中发起请求,这个只要看下项目的README就大概知道怎么用了。 https://github.com/axios/axios ### element-ui 一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库。说白了就是个UI组件库,看下官方文档即可。 https://element.eleme.cn/ ### js-cookie 一款操作Cookie的JavaScript组件库,这个只要看下项目的README就大概知道怎么用了。 https://github.com/js-cookie/js-cookie ### normalize.css 一款css库,在默认的HTML元素样式上提供了跨浏览器的高度一致性,说白了就是浏览器样式兼容用的,不看也可以。 https://github.com/necolas/normalize.css ### nprogress 一款基于JavaScript的进度条UI组件,这个只要看下项目的README就大概知道怎么用了。 https://github.com/rstacruz/nprogress ### path-to-regexp 将路径字符串(如/user/:name)转换为正则表达式的工具库,这个只要看下项目的README就大概知道怎么用了。 https://github.com/pillarjs/path-to-regexp ### vue 我们之前就学过Vue的基础了,看下官方教程即可。 https://cn.vuejs.org/v2/guide/ ### vue-router Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。看下官方教程即可。 https://router.vuejs.org/zh/ ### vuex Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。其实就是个全局状态管理器,比如用户登录信息,你很多页面都会用到,就存到这个里面。看下官方文档即可。 https://vuex.vuejs.org/zh/ ### vue-admin-template 学完了上面这些框架,接下来我们就可以开始学习我们的目标项目了,在中文的README里面有配套的教程文章,看一遍即可。 https://github.com/PanJiaChen/vue-admin-template/blob/master/README-zh.md ![](../images/vue_learning_07.png) 学习了目标项目的整套技术栈,同时看过了它的配套教程文章,看懂源码应该不是难事了,接下来就是看一遍该项目源码了。 ## 实践 我们可以这样分配时间,1天看Vue的官方文档,2天把`vue-admin-template`所使用的技术栈都学习一遍,再花2天学习`vue-admin-template`的源码,这样5天就能学会Vue了。 基本学会了Vue以后,要能牢牢掌握就要进行实践,否则过几天这些所学的东西就会离你而去了。 如何进行实践呢?回到我们学习目标:`做个后台管理系统的前端界面`,这是一个很好的实践。我当时就是通过做我的`mall-admin-web`项目来实践的。 我的`mall`项目有着完善的后台管理API,大家只要对照我的前端项目自行实现一些功能就是一次很好的实践,就能掌握Vue了。 这里我推荐大家自行实现下我的商品和订单模块,这两个模块最复杂,只要能实现这两个模块,其他的就都能实现了,使用Vue开发一整套后台管理系统也不在话下了。 ![](../images/vue_learning_08.png) ## 总结 最后总结一下我的学习方法,首先要明确自己的学习目标,然后针对目标去学习相关技术的基础,然后可以找个相关的开源项目学习下,学习其中的技术栈,之后进行实践,这样就能很好地掌握这门技术了。 ## 项目地址 [https://github.com/macrozheng/mall-admin-web](https://github.com/macrozheng/mall-admin-web) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/arthas_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 线上项目出BUG没法调试?推荐这款阿里开源的诊断神器! > 线上项目遇到问题无法调试,线下又无法重现,难道只能加日志再重新发布么?有了这款神器,既可以线上调试,又可以实现热修复,推荐给大家! ## Arthas 简介 Arthas是Alibaba开源的Java诊断工具,深受开发者喜爱。它采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。 ## 安装 > 为了还原一个真实的线上环境,我们将通过Arthas来对Docker容器中的Java程序进行诊断。 - 使用`arthas-boot`,下载对应jar包,下载地址:https://alibaba.github.io/arthas/arthas-boot.jar - 将我们的Spring Boot应用`mall-tiny-arthas`使用Docker容器的方式启动起来,打包和运行脚本在项目的`src\main\docker`目录下; - 将`arthas-boot.jar`拷贝到我们应用容器的`\`目录下; ```bash docker container cp arthas-boot.jar mall-tiny-arthas:/ ``` - 进入容器并启动`arthas-boot`,直接当做jar包启动即可; ```bash docker exec -it mall-tiny-arthas /bin/bash java -jar arthas-boot.jar ``` - 启动成功后,选择当前需要诊断的Java程序的序列号,这里是`1`,就可以开始诊断了; ![](../images/arthas_start_01.png) - 期间会下载一些所需的文件,完成后控制台打印信息如下,至此Arthas就安装启动完成了。 ![](../images/arthas_start_02.png) ## 常用命令 > 我们先来介绍一些Arthas的常用命令,会结合实际应用来讲解,带大家了解下Arthas的使用。 ### dashboard 使用`dashboard`命令可以显示当前系统的实时数据面板,包括线程信息、JVM内存信息及JVM运行时参数。 ![](../images/arthas_start_03.png) ### thread 查看当前线程信息,查看线程的堆栈,可以找出当前最占CPU的线程。 ![](../images/arthas_start_04.png) 常用命令: ```bash # 打印当前最忙的3个线程的堆栈信息 thread -n 3 # 查看ID为1都线程的堆栈信息 thread 1 # 找出当前阻塞其他线程的线程 thread -b # 查看指定状态的线程 thread -state WAITING ``` ### sysprop 查看当前JVM的系统属性,比如当容器时区与宿主机不一致时,可以使用如下命令查看时区信息。 ```bash sysprop |grep timezone ``` ``` user.timezone Asia/Shanghai ``` ### sysenv 查看JVM的环境属性,比如查看下我们当前启用的是什么环境的Spring Boot配置。 ![](../images/arthas_start_05.png) ### logger 使用`logger`命令可以查看日志信息,并改变日志级别,这个命令非常有用。 比如我们在生产环境上一般是不会打印`DEBUG`级别的日志的,当我们在线上排查问题时可以临时开启`DEBUG`级别的日志,帮助我们排查问题,下面介绍下如何操作。 - 我们的应用默认使用的是`INFO`级别的日志,使用`logger`命令可以查看; ![](../images/arthas_start_06.png) - 使用如下命令改变日志级别为`DEBUG`,需要使用`-c`参数指定类加载器的HASH值; ```bash logger -c 21b8d17c --name ROOT --level debug ``` - 再使用`logger`命令查看,发现`ROOT`级别日志已经更改; ![](../images/arthas_start_07.png) - 使用`docker logs -f mall-tiny-arthas`命令查看容器日志,发现已经打印了DEBUG级别的日志; ![](../images/arthas_start_08.png) - 查看完日志以后记得要把日志级别再调回`INFO`级别。 ```bash logger -c 21b8d17c --name ROOT --level info ``` ### sc 查看JVM已加载的类信息,`Search-Class`的简写,搜索出所有已经加载到 JVM 中的类信息。 - 搜索`com.macro.mall`包下所有的类; ```bash sc com.macro.mall.* ``` ![](../images/arthas_start_09.png) - 打印类的详细信息,加入`-d`参数并指定全限定类名; ```bash sc -d com.macro.mall.tiny.common.api.CommonResult ``` ![](../images/arthas_start_10.png) - 打印出类的Field信息,使用`-f`参数。 ```bash sc -d -f com.macro.mall.tiny.common.api.CommonResult ``` ![](../images/arthas_start_11.png) ### sm 查看已加载类的方法信息,`Search-Method`的简写,搜索出所有已经加载的类的方法信息。 - 查看类中的所有方法; ```bash sm com.macro.mall.tiny.common.api.CommonResult ``` ![](../images/arthas_start_12.png) - 查看指定方法信息,使用`-d`参数并指定方法名称; ```bash sm -d com.macro.mall.tiny.common.api.CommonResult getCode ``` ![](../images/arthas_start_13.png) ### jad 反编译已加载类的源码,觉得线上代码和预期不一致,可以反编译看看。 - 查看启动类的相关信息,默认会带有`ClassLoader`信息; ```bash jad com.macro.mall.tiny.MallTinyApplication ``` ![](../images/arthas_start_14.png) - 使用`--source-only`参数可以只打印类信息。 ```bash jad --source-only com.macro.mall.tiny.MallTinyApplication ``` ![](../images/arthas_start_15.png) ### mc 内存编译器,`Memory Compiler`的缩写,编译`.java`文件生成`.class`。 ### redefine 加载外部的`.class`文件,覆盖掉 JVM中已经加载的类。 ### monitor 实时监控方法执行信息,可以查看方法执行成功此时、失败次数、平均耗时等信息。 ```bash monitor -c 5 com.macro.mall.tiny.controller.PmsBrandController listBrand ``` ![](../images/arthas_start_19.png) ### watch 方法执行数据观测,可以观察方法执行过程中的参数和返回值。 使用如下命令观察方法执行参数和返回值,`-x`表示结果属性遍历深度。 ```bash watch com.macro.mall.tiny.service.impl.PmsBrandServiceImpl listBrand "{params,returnObj}" -x 2 ``` ![](../images/arthas_start_20.png) ## 热更新 > 尽管在线上环境热更代码并不是一个很好的行为,但有的时候我们真的很需要热更代码。下面介绍下如何使用`jad/mc/redefine`来热更新代码。 - 首先我们有一个商品详情的接口,当我们传入`id<=0`时,会抛出`IllegalArgumentException`; ```java /** * 品牌管理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("获取指定id的品牌详情") @RequestMapping(value = "/{id}", method = RequestMethod.GET) @ResponseBody public CommonResult brand(@PathVariable("id") Long id) { if(id<=0){ throw new IllegalArgumentException("id not excepted id:"+id); } return CommonResult.success(brandService.getBrand(id)); } } ``` - 调用接口会返回如下信息,调用地址:http://192.168.5.94:8088/brand/0 ```json { "timestamp": "2020-06-12T06:20:20.951+0000", "status": 500, "error": "Internal Server Error", "message": "id not excepted id:0", "path": "/brand/0" } ``` - 我们想对该问题进行修复,如果传入`id<=0`时,直接返回空数据的`CommonResult`,代码修改内容如下; ```java /** * 品牌管理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("获取指定id的品牌详情") @RequestMapping(value = "/{id}", method = RequestMethod.GET) @ResponseBody public CommonResult brand(@PathVariable("id") Long id) { if(id<=0){ // throw new IllegalArgumentException("id not excepted id:"+id); return CommonResult.success(null); } return CommonResult.success(brandService.getBrand(id)); } } ``` - 首先我们需要对`PmsBrandController`类代码进行修改,接着上传到服务器,然后使用如下命令将`java`文件拷贝到容器的`/tmp`目录下; ```bash docker container cp /tmp/PmsBrandController.java mall-tiny-arthas:/tmp/ ``` - 之后我们需要查看该类的类加载器的Hash值; ```bash sc -d *PmsBrandController | grep classLoaderHash ``` ![](../images/arthas_start_16.png) - 之后使用内存编译器把改`.java`文件编译成`.class`文件,注意需要使用`-c`指定类加载器; ```bash mc -c 21b8d17c /tmp/PmsBrandController.java -d /tmp ``` ![](../images/arthas_start_17.png) - 最后使用`redefine`命令加载`.class`文件,将原来加载的类覆盖掉; ```bash redefine -c 21b8d17c /tmp/com/macro/mall/tiny/controller/PmsBrandController.class ``` ![](../images/arthas_start_18.png) - 我们再次调用接口进行测试,发现已经返回了预期的结果,调用地址:http://192.168.3.101:8088/brand/0 ```json { "code": 200, "message": "操作成功", "data": null } ``` ## 参考资料 官方文档:https://alibaba.github.io/arthas/ ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-arthas ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/canal_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # MySQL如何实时同步数据到ES?试试这款阿里开源的神器! > `mall`项目中的商品搜索功能,一直都没有做实时数据同步。最近发现阿里巴巴开源的`canal`可以把MySQL中的数据实时同步到Elasticsearch中,能很好地解决数据同步问题。今天我们来讲讲`canal`的使用,希望对大家有所帮助! ## canal简介 canal主要用途是对MySQL数据库增量日志进行解析,提供增量数据的订阅和消费,简单说就是可以对MySQL的增量数据进行实时同步,支持同步到MySQL、Elasticsearch、HBase等数据存储中去。 ## canal工作原理 canal会模拟MySQL主库和从库的交互协议,从而伪装成MySQL的从库,然后向MySQL主库发送dump协议,MySQL主库收到dump请求会向canal推送binlog,canal通过解析binlog将数据同步到其他存储中去。 ![canal工作原理图](../images/canal_start_01.png) ## canal使用 > 接下来我们来学习下canal的使用,以MySQL实时同步数据到Elasticsearch为例。 ### 组件下载 - 首先我们需要下载canal的各个组件`canal-server`、`canal-adapter`、`canal-admin`,下载地址:https://github.com/alibaba/canal/releases ![](../images/canal_start_02.png) - canal的各个组件的用途各不相同,下面分别介绍下: - canal-server(canal-deploy):可以直接监听MySQL的binlog,把自己伪装成MySQL的从库,只负责接收数据,并不做处理。 - canal-adapter:相当于canal的客户端,会从canal-server中获取数据,然后对数据进行同步,可以同步到MySQL、Elasticsearch和HBase等存储中去。 - canal-admin:为canal提供整体配置管理、节点运维等面向运维的功能,提供相对友好的WebUI操作界面,方便更多用户快速和安全的操作。 - 由于不同版本的MySQL、Elasticsearch和canal会有兼容性问题,所以我们先对其使用版本做个约定。 | 应用 | 端口 | 版本 | | ------------- | ----- | ------ | | MySQL | 3306 | 5.7 | | Elasticsearch | 9200 | 7.6.2 | | Kibanba | 5601 | 7.6.2 | | canal-server | 11111 | 1.1.15 | | canal-adapter | 8081 | 1.1.15 | | canal-admin | 8089 | 1.1.15 | ### MySQL配置 - 由于canal是通过订阅MySQL的binlog来实现数据同步的,所以我们需要开启MySQL的binlog写入功能,并设置`binlog-format`为ROW模式,我的配置文件为`/mydata/mysql/conf/my.cnf`,改为如下内容即可; ```bash [mysqld] ## 设置server_id,同一局域网中需要唯一 server_id=101 ## 指定不需要同步的数据库名称 binlog-ignore-db=mysql ## 开启二进制日志功能 log-bin=mall-mysql-bin ## 设置二进制日志使用内存大小(事务) binlog_cache_size=1M ## 设置使用的二进制日志格式(mixed,statement,row) binlog_format=row ## 二进制日志过期清理时间。默认值为0,表示不自动清理。 expire_logs_days=7 ## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。 ## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致 slave_skip_errors=1062 ``` - 配置完成后需要重新启动MySQL,重启成功后通过如下命令查看binlog是否启用; ```sql show variables like '%log_bin%' ``` ``` +---------------------------------+-------------------------------------+ | Variable_name | Value | +---------------------------------+-------------------------------------+ | log_bin | ON | | log_bin_basename | /var/lib/mysql/mall-mysql-bin | | log_bin_index | /var/lib/mysql/mall-mysql-bin.index | | log_bin_trust_function_creators | OFF | | log_bin_use_v1_row_events | OFF | | sql_log_bin | ON | +---------------------------------+-------------------------------------+ ``` - 再查看下MySQL的binlog模式; ```sql show variables like 'binlog_format%'; ``` ``` +---------------+-------+ | Variable_name | Value | +---------------+-------+ | binlog_format | ROW | +---------------+-------+ ``` - 接下来需要创建一个拥有从库权限的账号,用于订阅binlog,这里创建的账号为`canal:canal`; ```sql CREATE USER canal IDENTIFIED BY 'canal'; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%'; FLUSH PRIVILEGES; ``` - 创建好测试用的数据库`canal-test`,之后创建一张商品表`product`,建表语句如下。 ```sql CREATE TABLE `product` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `sub_title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `price` decimal(10, 2) NULL DEFAULT NULL, `pic` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; ``` ### canal-server使用 - 将我们下载好的压缩包`canal.deployer-1.1.5-SNAPSHOT.tar.gz`上传到Linux服务器,然后解压到指定目录`/mydata/canal-server`,可使用如下命令解压; ````bash tar -zxvf canal.deployer-1.1.5-SNAPSHOT.tar.gz ```` - 解压完成后目录结构如下; ``` ├── bin │   ├── restart.sh │   ├── startup.bat │   ├── startup.sh │   └── stop.sh ├── conf │   ├── canal_local.properties │   ├── canal.properties │   └── example │      └── instance.properties ├── lib ├── logs │   ├── canal │   │   └── canal.log │   └── example │   ├── example.log │   └── example.log └── plugin ``` - 修改配置文件`conf/example/instance.properties`,按如下配置即可,主要是修改数据库相关配置; ``` # 需要同步数据的MySQL地址 canal.instance.master.address=127.0.0.1:3306 canal.instance.master.journal.name= canal.instance.master.position= canal.instance.master.timestamp= canal.instance.master.gtid= # 用于同步数据的数据库账号 canal.instance.dbUsername=canal # 用于同步数据的数据库密码 canal.instance.dbPassword=canal # 数据库连接编码 canal.instance.connectionCharset = UTF-8 # 需要订阅binlog的表过滤正则表达式 canal.instance.filter.regex=.*\\..* ``` - 使用`startup.sh`脚本启动`canal-server`服务; ```bash sh bin/startup.sh ``` - 启动成功后可使用如下命令查看服务日志信息; ```bash tail -f logs/canal/canal.log ``` ```bash 2020-10-26 16:18:13.354 [main] INFO com.alibaba.otter.canal.deployer.CanalController - ## start the canal server[172.17.0.1(172.17.0.1):11111] 2020-10-26 16:18:19.978 [main] INFO com.alibaba.otter.canal.deployer.CanalStarter - ## the canal server is running now ...... ``` - 启动成功后可使用如下命令查看instance日志信息; ```bash tail -f logs/example/example.log ``` ```bash 2020-10-26 16:18:16.056 [main] INFO c.a.o.c.i.spring.support.PropertyPlaceholderConfigurer - Loading properties file from class path resource [canal.properties] 2020-10-26 16:18:16.061 [main] INFO c.a.o.c.i.spring.support.PropertyPlaceholderConfigurer - Loading properties file from class path resource [example/instance.properties] 2020-10-26 16:18:18.259 [main] INFO c.a.otter.canal.instance.spring.CanalInstanceWithSpring - start CannalInstance for 1-example 2020-10-26 16:18:18.282 [main] WARN c.a.o.canal.parse.inbound.mysql.dbsync.LogEventConvert - --> init table filter : ^.*\..*$ 2020-10-26 16:18:18.282 [main] WARN c.a.o.canal.parse.inbound.mysql.dbsync.LogEventConvert - --> init table black filter : ^mysql\.slave_.*$ 2020-10-26 16:18:19.543 [destination = example , address = /127.0.0.1:3306 , EventParser] WARN c.a.o.c.p.inbound.mysql.rds.RdsBinlogEventParserProxy - ---> begin to find start position, it will be long time for reset or first position 2020-10-26 16:18:19.578 [main] INFO c.a.otter.canal.instance.core.AbstractCanalInstance - start successful.... 2020-10-26 16:18:19.912 [destination = example , address = /127.0.0.1:3306 , EventParser] WARN c.a.o.c.p.inbound.mysql.rds.RdsBinlogEventParserProxy - prepare to find start position just last position {"identity":{"slaveId":-1,"sourceAddress":{"address":"localhost","port":3306}},"postion":{"gtid":"","included":false,"journalName":"mall-mysql-bin.000006","position":2271,"serverId":101,"timestamp":1603682664000}} 2020-10-26 16:18:22.435 [destination = example , address = /127.0.0.1:3306 , EventParser] WARN c.a.o.c.p.inbound.mysql.rds.RdsBinlogEventParserProxy - ---> find start position successfully, EntryPosition[included=false,journalName=mall-mysql-bin.000006,position=2271,serverId=101,gtid=,timestamp=1603682664000] cost : 2768ms , the next step is binlog dump ``` - 如果想要停止`canal-server`服务可以使用如下命令。 ```bash sh bin/stop.sh ``` ### canal-adapter使用 - 将我们下载好的压缩包`canal.adapter-1.1.5-SNAPSHOT.tar.gz`上传到Linux服务器,然后解压到指定目录`/mydata/canal-adpter`,解压完成后目录结构如下; ``` ├── bin │   ├── adapter.pid │   ├── restart.sh │   ├── startup.bat │   ├── startup.sh │   └── stop.sh ├── conf │   ├── application.yml │   ├── es6 │   ├── es7 │   │   ├── biz_order.yml │   │   ├── customer.yml │   │   └── product.yml │   ├── hbase │   ├── kudu │   ├── logback.xml │   ├── META-INF │   │   └── spring.factories │   └── rdb ├── lib ├── logs │   └── adapter │   └── adapter.log └── plugin ``` - 修改配置文件`conf/application.yml`,按如下配置即可,主要是修改canal-server配置、数据源配置和客户端适配器配置; ```yaml canal.conf: mode: tcp # 客户端的模式,可选tcp kafka rocketMQ flatMessage: true # 扁平message开关, 是否以json字符串形式投递数据, 仅在kafka/rocketMQ模式下有效 zookeeperHosts: # 对应集群模式下的zk地址 syncBatchSize: 1000 # 每次同步的批数量 retries: 0 # 重试次数, -1为无限重试 timeout: # 同步超时时间, 单位毫秒 accessKey: secretKey: consumerProperties: # canal tcp consumer canal.tcp.server.host: 127.0.0.1:11111 #设置canal-server的地址 canal.tcp.zookeeper.hosts: canal.tcp.batch.size: 500 canal.tcp.username: canal.tcp.password: srcDataSources: # 源数据库配置 defaultDS: url: jdbc:mysql://127.0.0.1:3306/canal_test?useUnicode=true username: canal password: canal canalAdapters: # 适配器列表 - instance: example # canal实例名或者MQ topic名 groups: # 分组列表 - groupId: g1 # 分组id, 如果是MQ模式将用到该值 outerAdapters: - name: logger # 日志打印适配器 - name: es7 # ES同步适配器 hosts: 127.0.0.1:9200 # ES连接地址 properties: mode: rest # 模式可选transport(9300) 或者 rest(9200) # security.auth: test:123456 # only used for rest mode cluster.name: elasticsearch # ES集群名称 ``` - 添加配置文件`canal-adapter/conf/es7/product.yml`,用于配置MySQL中的表与Elasticsearch中索引的映射关系; ```yaml dataSourceKey: defaultDS # 源数据源的key, 对应上面配置的srcDataSources中的值 destination: example # canal的instance或者MQ的topic groupId: g1 # 对应MQ模式下的groupId, 只会同步对应groupId的数据 esMapping: _index: canal_product # es 的索引名称 _id: _id # es 的_id, 如果不配置该项必须配置下面的pk项_id则会由es自动分配 sql: "SELECT p.id AS _id, p.title, p.sub_title, p.price, p.pic FROM product p" # sql映射 etlCondition: "where a.c_time>={}" #etl的条件参数 commitBatch: 3000 # 提交批大小 ``` - 使用`startup.sh`脚本启动`canal-adapter`服务; ```bash sh bin/startup.sh ``` - 启动成功后可使用如下命令查看服务日志信息; ```bash tail -f logs/adapter/adapter.log ``` ``` 20-10-26 16:52:55.148 [main] INFO c.a.o.canal.adapter.launcher.loader.CanalAdapterLoader - Load canal adapter: logger succeed 2020-10-26 16:52:57.005 [main] INFO c.a.o.c.client.adapter.es.core.config.ESSyncConfigLoader - ## Start loading es mapping config ... 2020-10-26 16:52:57.376 [main] INFO c.a.o.c.client.adapter.es.core.config.ESSyncConfigLoader - ## ES mapping config loaded 2020-10-26 16:52:58.615 [main] INFO c.a.o.canal.adapter.launcher.loader.CanalAdapterLoader - Load canal adapter: es7 succeed 2020-10-26 16:52:58.651 [main] INFO c.alibaba.otter.canal.connector.core.spi.ExtensionLoader - extension classpath dir: /mydata/canal-adapter/plugin 2020-10-26 16:52:59.043 [main] INFO c.a.o.canal.adapter.launcher.loader.CanalAdapterLoader - Start adapter for canal-client mq topic: example-g1 succeed 2020-10-26 16:52:59.044 [main] INFO c.a.o.canal.adapter.launcher.loader.CanalAdapterService - ## the canal client adapters are running now ...... 2020-10-26 16:52:59.057 [Thread-4] INFO c.a.otter.canal.adapter.launcher.loader.AdapterProcessor - =============> Start to connect destination: example <============= 2020-10-26 16:52:59.100 [main] INFO org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8081"] 2020-10-26 16:52:59.153 [main] INFO org.apache.tomcat.util.net.NioSelectorPool - Using a shared selector for servlet write/read 2020-10-26 16:52:59.590 [main] INFO o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port(s): 8081 (http) with context path '' 2020-10-26 16:52:59.626 [main] INFO c.a.otter.canal.adapter.launcher.CanalAdapterApplication - Started CanalAdapterApplication in 31.278 seconds (JVM running for 33.99) 2020-10-26 16:52:59.930 [Thread-4] INFO c.a.otter.canal.adapter.launcher.loader.AdapterProcessor - =============> Subscribe destination: example succeed <============= ``` - 如果需要停止`canal-adapter`服务可以使用如下命令。 ```bash sh bin/stop.sh ``` ### 数据同步演示 > 经过上面的一系列步骤,canal的数据同步功能已经基本可以使用了,下面我们来演示下数据同步功能。 - 首先我们需要在Elasticsearch中创建索引,和MySQL中的product表相对应,直接在Kibana的`Dev Tools`中使用如下命令创建即可; ``` PUT canal_product { "mappings": { "properties": { "title": { "type": "text" }, "sub_title": { "type": "text" }, "pic": { "type": "text" }, "price": { "type": "double" } } } } ``` ![](../images/canal_start_03.png) - 创建完成后可以查看下索引的结构; ``` GET canal_product/_mapping ``` ![](../images/canal_start_04.png) - 之后使用如下SQL语句在数据库中创建一条记录; ```sql INSERT INTO product ( id, title, sub_title, price, pic ) VALUES ( 5, '小米8', ' 全面屏游戏智能手机 6GB+64GB', 1999.00, NULL ); ``` - 创建成功后,在Elasticsearch中搜索下,发现数据已经同步了; ``` GET canal_product/_search ``` ![](../images/canal_start_05.png) - 再使用如下SQL对数据进行修改; ```sql UPDATE product SET title='小米10' WHERE id=5 ``` - 修改成功后,在Elasticsearch中搜索下,发现数据已经修改了; ![](../images/canal_start_06.png) - 再使用如下SQL对数据进行删除操作; ```sql DELETE FROM product WHERE id=5 ``` - 删除成功后,在Elasticsearch中搜索下,发现数据已经删除了,至此MySQL同步到Elasticsearch的功能完成了! ![](../images/canal_start_07.png) ### canal-admin使用 - 将我们下载好的压缩包`canal.admin-1.1.5-SNAPSHOT.tar.gz`上传到Linux服务器,然后解压到指定目录`/mydata/canal-admin`,解压完成后目录结构如下; ``` ├── bin │   ├── restart.sh │   ├── startup.bat │   ├── startup.sh │   └── stop.sh ├── conf │   ├── application.yml │   ├── canal_manager.sql │   ├── canal-template.properties │   ├── instance-template.properties │   ├── logback.xml │   └── public │   ├── avatar.gif │   ├── index.html │   ├── logo.png │   └── static ├── lib └── logs ``` - 创建canal-admin需要使用的数据库`canal_manager`,创建SQL脚本为`/mydata/canal-admin/conf/canal_manager.sql`,会创建如下表; ![](../images/canal_start_08.png) - 修改配置文件`conf/application.yml`,按如下配置即可,主要是修改数据源配置和`canal-admin`的管理账号配置,注意需要用一个有读写权限的数据库账号,比如管理账号`root:root`; ```yaml server: port: 8089 spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 spring.datasource: address: 127.0.0.1:3306 database: canal_manager username: root password: root driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://${spring.datasource.address}/${spring.datasource.database}?useUnicode=true&characterEncoding=UTF-8&useSSL=false hikari: maximum-pool-size: 30 minimum-idle: 1 canal: adminUser: admin adminPasswd: admin ``` - 接下来对之前搭建的`canal-server`的`conf/canal_local.properties`文件进行配置,主要是修改`canal-admin`的配置,修改完成后使用`sh bin/startup.sh local`重启`canal-server`: ``` # register ip canal.register.ip = # canal admin config canal.admin.manager = 127.0.0.1:8089 canal.admin.port = 11110 canal.admin.user = admin canal.admin.passwd = 4ACFE3202A5FF5CF467898FC58AAB1D615029441 # admin auto register canal.admin.register.auto = true canal.admin.register.cluster = ``` - 使用`startup.sh`脚本启动`canal-admin`服务; ```bash sh bin/startup.sh ``` - 启动成功后可使用如下命令查看服务日志信息; ```bash tail -f logs/admin.log ``` ``` 020-10-27 10:15:04.210 [main] INFO org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8089"] 2020-10-27 10:15:04.308 [main] INFO org.apache.tomcat.util.net.NioSelectorPool - Using a shared selector for servlet write/read 2020-10-27 10:15:04.534 [main] INFO o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port(s): 8089 (http) with context path '' 2020-10-27 10:15:04.573 [main] INFO com.alibaba.otter.canal.admin.CanalAdminApplication - Started CanalAdminApplication in 31.203 seconds (JVM running for 34.865) ``` - 访问canal-admin的Web界面,输入账号密码`admin:123456`即可登录,访问地址:http://192.168.3.101:8089 ![](../images/canal_start_09.png) - 登录成功后即可使用Web界面操作canal-server。 ![](../images/canal_start_10.png) ## 参考资料 canal官方文档:https://github.com/alibaba/canal/wiki ## 配置文件地址 https://github.com/macrozheng/mall-learning/tree/master/document/canal-config ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/datagrip_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # IDEA同款数据库管理工具,提示太全了,用起来贼香! > 最近体验了一把Jetbrains出品的数据库管理工具`DataGrip`,发现SQL提示真的很全,写起SQL来特别顺手,各种数据库支持也很全。整理了下其用法和使用技巧,助大家码出更高质量的SQL。 ## 下载 > 直接从Jetbrains的官网下载即可,下载地址:https://www.jetbrains.com/datagrip/ ![](../images/datagrip_start_30.png) ## 外观配置 > 有时候我们的电脑屏幕过大,需要调整下字体大小,要不然看起来太小不适应,可以修改下下面两个配置。 - 软件的外观配置,通过`File->Settings`打开软件设置(用过IDEA的朋友一定很熟悉),然后可以设置软件外观的字体大小; ![](../images/datagrip_start_05.png) - 编辑器字体大小配置,通过软件设置中的`Editor->Font`配置可以调整编辑器字体大小。 ![](../images/datagrip_start_06.png) ## 创建数据源 > 使用DataGrip操作数据库时,我们需要首先创建数据源。 - 我们可以通过`左上角的加号->Data Source->MySQL`来创建一个MySQL的数据源; ![](../images/datagrip_start_01.png) - 输入相关数据库配置以后,点击`测试连接`,我们发现连接失败了,缺少`serverTimezone`参数的配置; ![](../images/datagrip_start_02.png) - 只需在`高级`选项中,设置`serverTimezone`属性的值为`Asia/Shanghai`即可; ![](../images/datagrip_start_03.png) - 设置成功后,点击`测试连接`,就会返回连接成功的信息了。 ![](../images/datagrip_start_04.png) ## 管理表相关操作 > 我们先来介绍下数据库表相关操作,包括设计表、查看表数据及导出表数据。 - 连接成功后,在左侧就可以查看数据库中所有的表了; ![](../images/datagrip_start_07.png) - 选中表`右键->修改表`即可查看数据库表的相关信息; ![](../images/datagrip_start_08.png) - 双击表就可以分页查看表中存储的数据了; ![](../images/datagrip_start_09.png) - 有时候有些列的数据我们并不关心,可以右键表头选择隐藏列来隐藏它; ![](../images/datagrip_start_10.png) - 我们可以在顶部的过滤条件中直接编写WHERE语句来实现对数据的过滤筛选; ![](../images/datagrip_start_11.png) - 我们可以通过右键数据库名称,选择新建表; ![](../images/datagrip_start_12.png) - 新建时可以添加表中的列,并且可以预览对应的SQL脚本; ![](../images/datagrip_start_13.png) - 数据导出功能,可以将数据导出为CSV、Html、Excel、JSON等格式。 ![](../images/datagrip_start_14.png) ## SQL操作技巧 > 下面再介绍下在DataGrip中编写SQL的各种小技巧! - 打开查询控制台,右键数据库,选择`Open Query Console`打开编辑器; ![](../images/datagrip_start_15.png) - 强大的提示功能,对于SQL语句、数据库中的表和列均有提示; ![](../images/datagrip_start_16.gif) - 编写`SELECT *`语句并不是好习惯,可以通过选中`*`再使用`Alt+Enter`快捷键来直接扩展成相关列; ![](../images/datagrip_start_17.gif) - 当我们查询的表取了别名以后,可以通过`Alt+Enter`快捷键来直接为所有查询的列添加前缀; ![](../images/datagrip_start_18.gif) - 当我们使用`INSERT INTO`语句时,可用直接生成所有需要插入的列名; ![](../images/datagrip_start_19.gif) - 当我们把鼠标悬停在函数上方时,会显示非常详细的函数使用说明; ![](../images/datagrip_start_20.png) - 使用`Ctrl+Alt+L`快捷键可以格式化我们的SQL语句; ![](../images/datagrip_start_21.gif) - 对于执行的各种操作都会显示SQL日志; ![](../images/datagrip_start_22.png) - 查看代码历史,直接右键编辑器,选择`Local History->Show History`可以打开查看SQL执行的历史记录; ![](../images/datagrip_start_23.png) - 查看执行计划,选中目标SQL并右键,选择`Explain Plain`,即可在底部查看。 ![](../images/datagrip_start_24.png) ![](../images/datagrip_start_25.png) ## MongoDB支持 > DataGrip不仅对关系型数据库有所支持,对非关系型数据库也有所支持,下面简单介绍下如何用它管理MongoDB数据库。 - 创建数据源,通过`左上角的加号->Data Source->MongoDB`来创建一个MongoDB的数据源; ![](../images/datagrip_start_26.png) - 修改数据源配置并测试连接; ![](../images/datagrip_start_27.png) - 查看集合中的信息,可以设置筛选条件; ![](../images/datagrip_start_28.png) - 往集合中插入数据。 ![](../images/datagrip_start_29.png) ## 总结 本文主要讲述了在DataGrip中管理MySQL和MongoDB的常用操作及使用技巧,用过Jetbrains公司其他产品的朋友应该很容易就可以上手了! ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/docker.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 开发者必备Docker命令 > 本文主要讲解Docker环境的安装以及Docker常用命令的使用,掌握这些对Docker环境下应用的部署具有很大帮助。 ## Docker 简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows机器上。使用Docker可以更方便低打包、测试以及部署应用程序。 ## Docker 环境安装 - 安装yum-utils: ```bash yum install -y yum-utils device-mapper-persistent-data lvm2 ``` - 为yum源添加docker仓库位置: ```bash yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo ``` - 安装docker: ```bash yum install docker-ce ``` - 启动docker: ```bash systemctl start docker ``` ## Docker 镜像常用命令 ### 搜索镜像 ```bash docker search java ``` ![](../images/refer_screen_51.png) ### 下载镜像 ```bash docker pull java:8 ``` ### 如何查找镜像支持的版本 > 由于docker search命令只能查找出是否有该镜像,不能找到该镜像支持的版本,所以我们需要通过docker hub来搜索支持的版本。 - 进入docker hub的官网,地址:[https://hub.docker.com](https://hub.docker.com) - 然后搜索需要的镜像: ![](../images/refer_screen_52.png) - 查看镜像支持的版本: ![](../images/refer_screen_53.png) - 进行镜像的下载操作: ```bash docker pull nginx:1.17.0 ``` ### 列出镜像 ```bash docker images ``` ![](../images/refer_screen_54.png) ### 删除镜像 - 指定名称删除镜像 ```bash docker rmi java:8 ``` - 指定名称删除镜像(强制) ```bash docker rmi -f java:8 ``` - 删除所有没有引用的镜像 ```bash docker rmi `docker images | grep none | awk '{print $3}'` ``` - 强制删除所有镜像 ```bash docker rmi -f $(docker images) ``` ## Docker 容器常用命令 ### 新建并启动容器 ```bash docker run -p 80:80 --name nginx -d nginx:1.17.0 ``` - -d选项:表示后台运行 - --name选项:指定运行后容器的名字为nginx,之后可以通过名字来操作容器 - -p选项:指定端口映射,格式为:hostPort:containerPort ### 列出容器 - 列出运行中的容器: ```bash docker ps ``` ![](../images/refer_screen_55.png) - 列出所有容器 ```bash docker ps -a ``` ![](../images/refer_screen_56.png) ### 停止容器 ```bash # $ContainerName及$ContainerId可以用docker ps命令查询出来 docker stop $ContainerName(或者$ContainerId) ``` 比如: ```bash docker stop nginx #或者 docker stop c5f5d5125587 ``` ### 强制停止容器 ```bash docker kill $ContainerName(或者$ContainerId) ``` ### 启动已停止的容器 ```bash docker start $ContainerName(或者$ContainerId) ``` ### 进入容器 - 先查询出容器的pid: ```bash docker inspect --format "{{.State.Pid}}" $ContainerName(或者$ContainerId) ``` - 根据容器的pid进入容器: ```bash nsenter --target "$pid" --mount --uts --ipc --net --pid ``` ![](../images/refer_screen_57.png) ### 删除容器 - 删除指定容器: ```bash docker rm $ContainerName(或者$ContainerId) ``` - 按名称删除容器 ```bash docker rm `docker ps -a | grep mall-* | awk '{print $1}'` ``` - 强制删除所有容器; ```bash docker rm -f $(docker ps -a -q) ``` ### 查看容器的日志 - 查看当前全部日志 ```bash docker logs $ContainerName(或者$ContainerId) ``` - 动态查看日志 ```bash docker logs $ContainerName(或者$ContainerId) -f ``` ![](../images/refer_screen_58.png) ### 查看容器的IP地址 ```bash docker inspect --format '{{ .NetworkSettings.IPAddress }}' $ContainerName(或者$ContainerId) ``` ![](../images/refer_screen_59.png) ### 修改容器的启动方式 ```bash docker container update --restart=always $ContainerName ``` ### 同步宿主机时间到容器 ```bash docker cp /etc/localtime $ContainerName(或者$ContainerId):/etc/ ``` ### 指定容器时区 ```bash docker run -p 80:80 --name nginx \ -e TZ="Asia/Shanghai" \ -d nginx:1.17.0 ``` ### 在宿主机查看docker使用cpu、内存、网络、io情况 - 查看指定容器情况: ```bash docker stats $ContainerName(或者$ContainerId) ``` ![](../images/refer_screen_60.png) - 查看所有容器情况: ```bash docker stats -a ``` ![](../images/refer_screen_61.png) ### 查看Docker磁盘使用情况 ```bash docker system df ``` ![](../images/refer_screen_108.png) ### 进入Docker容器内部的bash ```bash docker exec -it $ContainerName /bin/bash ``` ![](../images/refer_screen_62.png) ### 使用root帐号进入Docker容器内部 ```bash docker exec -it --user root $ContainerName /bin/bash ``` ### Docker创建外部网络 ```bash docker network create -d bridge my-bridge-network ``` ## 修改Docker镜像的存放位置 - 查看Docker镜像的存放位置: ```bash docker info | grep "Docker Root Dir" ``` ![](../images/refer_screen_63.png) - 关闭Docker服务: ```bash systemctl stop docker ``` - 移动目录到目标路径: ```bash mv /var/lib/docker /mydata/docker ``` - 建立软连接: ```bash ln -s /mydata/docker /var/lib/docker ``` ![](../images/refer_screen_64.png) ![](../images/refer_screen_65.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/docker_command.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 还在百度Docker命令?推荐一套我用起来特顺手的命令! > 平时经常使用Docker来搭建各种环境,简单又好用!但是有时候往往会忘记命令,总结了一套非常实用的Docker命令,对于Java开发来说基本上够用了,希望对大家有所帮助! ## Docker简介 Docker是一个开源的应用容器引擎,让开发者可以打包应用及依赖包到一个可移植的镜像中,然后发布到任何流行的Linux或Windows机器上。使用Docker可以更方便地打包、测试以及部署应用程序。 ## Docker环境安装 - 安装`yum-utils`; ```bash yum install -y yum-utils device-mapper-persistent-data lvm2 ``` - 为yum源添加docker仓库位置; ```bash yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo ``` - 安装docker服务; ```bash yum install docker-ce ``` - 启动docker服务。 ```bash systemctl start docker ``` ## Docker镜像常用命令 ### 搜索镜像 ```bash docker search java ``` ![](../images/docker_command_01.png) ### 下载镜像 ```bash docker pull java:8 ``` ### 查看镜像版本 > 由于`docker search`命令只能查找出是否有该镜像,不能找到该镜像支持的版本,所以我们需要通过`Docker Hub`来搜索支持的版本。 - 进入`Docker Hub`的官网,地址:https://hub.docker.com - 然后搜索需要的镜像: ![](../images/docker_command_02.png) - 查看镜像支持的版本: ![](../images/docker_command_03.png) - 进行镜像的下载操作: ```bash docker pull nginx:1.17.0 ``` ### 列出镜像 ```bash docker images ``` ![](../images/docker_command_04.png) ### 删除镜像 - 指定名称删除镜像: ```bash docker rmi java:8 ``` - 指定名称删除镜像(强制): ```bash docker rmi -f java:8 ``` - 删除所有没有引用的镜像: ```bash docker rmi `docker images | grep none | awk '{print $3}'` ``` - 强制删除所有镜像: ```bash docker rmi -f $(docker images) ``` ### 打包镜像 ```bash # -t 表示指定镜像仓库名称/镜像名称:镜像标签 .表示使用当前目录下的Dockerfile文件 docker build -t mall/mall-admin:1.0-SNAPSHOT . ``` ### 推送镜像 ```bash # 登录Docker Hub docker login # 给本地镜像打标签为远程仓库名称 docker tag mall/mall-admin:1.0-SNAPSHOT macrodocker/mall-admin:1.0-SNAPSHOT # 推送到远程仓库 docker push macrodocker/mall-admin:1.0-SNAPSHOT ``` ## Docker容器常用命令 ### 新建并启动容器 ```bash docker run -p 80:80 --name nginx \ -e TZ="Asia/Shanghai" \ -v /mydata/nginx/html:/usr/share/nginx/html \ -d nginx:1.17.0 ``` - -p:将宿主机和容器端口进行映射,格式为:宿主机端口:容器端口; - --name:指定容器名称,之后可以通过容器名称来操作容器; - -e:设置容器的环境变量,这里设置的是时区; - -v:将宿主机上的文件挂载到宿主机上,格式为:宿主机文件目录:容器文件目录; - -d:表示容器以后台方式运行。 ### 列出容器 - 列出运行中的容器: ```bash docker ps ``` ![](../images/docker_command_05.png) - 列出所有容器: ```bash docker ps -a ``` ![](../images/docker_command_06.png) ### 停止容器 注意:`$ContainerName`表示容器名称,`$ContainerId`表示容器ID,可以使用容器名称的命令,基本也支持使用容器ID,比如下面的停止容器命令。 ```bash docker stop $ContainerName(or $ContainerId) ``` 例如: ```bash docker stop nginx #或者 docker stop c5f5d5125587 ``` ### 强制停止容器 ```bash docker kill $ContainerName ``` ### 启动容器 ```bash docker start $ContainerName ``` ### 进入容器 - 先查询出容器的`pid`: ```bash docker inspect --format "{{.State.Pid}}" $ContainerName ``` - 根据容器的pid进入容器: ```bash nsenter --target "$pid" --mount --uts --ipc --net --pid ``` ![](../images/docker_command_07.png) ### 删除容器 - 删除指定容器: ```bash docker rm $ContainerName ``` - 按名称通配符删除容器,比如删除以名称`mall-`开头的容器: ```bash docker rm `docker ps -a | grep mall-* | awk '{print $1}'` ``` - 强制删除所有容器; ```bash docker rm -f $(docker ps -a -q) ``` ### 查看容器的日志 - 查看容器产生的全部日志: ```bash docker logs $ContainerName ``` ![](../images/docker_command_08.png) - 动态查看容器产生的日志: ```bash docker logs -f $ContainerName ``` ### 查看容器的IP地址 ```bash docker inspect --format '{{ .NetworkSettings.IPAddress }}' $ContainerName ``` ![](../images/docker_command_09.png) ### 修改容器的启动方式 ```bash # 将容器启动方式改为always docker container update --restart=always $ContainerName ``` ### 同步宿主机时间到容器 ```bash docker cp /etc/localtime $ContainerName:/etc/ ``` ### 指定容器时区 ```bash docker run -p 80:80 --name nginx \ -e TZ="Asia/Shanghai" \ -d nginx:1.17.0 ``` ### 查看容器资源占用状况 - 查看指定容器资源占用状况,比如cpu、内存、网络、io状态: ```bash docker stats $ContainerName ``` ![](../images/docker_command_10.png) - 查看所有容器资源占用情况: ```bash docker stats -a ``` ![](../images/docker_command_11.png) ### 查看容器磁盘使用情况 ```bash docker system df ``` ![](../images/docker_command_16.png) ### 执行容器内部命令 ```bash docker exec -it $ContainerName /bin/bash ``` ![](../images/docker_command_12.png) ### 指定账号进入容器内部 ```bash # 使用root账号进入容器内部 docker exec -it --user root $ContainerName /bin/bash ``` ### 查看所有网络 ```bash docker network ls ``` ```bash [root@local-linux ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 59b309a5c12f bridge bridge local ef34fe69992b host host local a65be030c632 none ``` ### 创建外部网络 ```bash docker network create -d bridge my-bridge-network ``` ### 指定容器网络 ```bash docker run -p 80:80 --name nginx \ --network my-bridge-network \ -d nginx:1.17.0 ``` ## 修改镜像的存放位置 - 查看Docker镜像的存放位置: ```bash docker info | grep "Docker Root Dir" ``` ![](../images/docker_command_13.png) - 关闭Docker服务: ```bash systemctl stop docker ``` - 先将原镜像目录移动到目标目录: ```bash mv /var/lib/docker /mydata/docker ``` - 建立软连接: ```bash ln -s /mydata/docker /var/lib/docker ``` ![](../images/docker_command_14.png) - 再次查看可以发现镜像存放位置已经更改。 ![](../images/docker_command_15.png) ## Docker容器清理 - 查看Docker占用的磁盘空间情况: ```bash docker system df ``` - 删除所有关闭的容器: ```bash docker ps -a | grep Exit | cut -d ' ' -f 1 | xargs docker rm ``` - 删除所有`dangling`镜像(没有Tag的镜像): ```bash docker rmi $(docker images | grep "^" | awk "{print $3}") ``` - 删除所有`dangling`数据卷(即无用的 volume): ```bash docker volume rm $(docker volume ls -qf dangling=true) ``` ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/docker_compose.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 使用Docker Compose部署SpringBoot应用 > Docker Compose是一个用于定义和运行多个docker容器应用的工具。使用Compose你可以用YAML文件来配置你的应用服务,然后使用一个命令,你就可以部署你配置的所有服务了。 ## 安装 ### 下载Docker Compose ```bash curl -L https://get.daocloud.io/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose ``` ### 修改该文件的权限为可执行 ```bash chmod +x /usr/local/bin/docker-compose ``` ### 查看是否已经安装成功 ```bash docker-compose --version ``` ![](../images/refer_screen_96.png) ## 使用Docker Compose的步骤 - 使用Dockerfile定义应用程序环境,一般需要修改初始镜像行为时才需要使用; - 使用docker-compose.yml定义需要部署的应用程序服务,以便执行脚本一次性部署; - 使用docker-compose up命令将所有应用服务一次性部署起来。 ## docker-compose.yml常用命令 ### image 指定运行的镜像名称 ```yaml # 运行的是mysql5.7的镜像 image: mysql:5.7 ``` ### container_name 配置容器名称 ```yaml # 容器名称为mysql container_name: mysql ``` ### ports 指定宿主机和容器的端口映射(HOST:CONTAINER) ```yaml # 将宿主机的3306端口映射到容器的3306端口 ports: - 3306:3306 ``` ### volumes 将宿主机的文件或目录挂载到容器中(HOST:CONTAINER) ```yaml # 将外部文件挂载到myql容器中 volumes: - /mydata/mysql/log:/var/log/mysql - /mydata/mysql/data:/var/lib/mysql - /mydata/mysql/conf:/etc/mysql ``` ### environment 配置环境变量 ```yaml # 设置mysqlroot帐号密码的环境变量 environment: - MYSQL_ROOT_PASSWORD=root ``` ### links 连接其他容器的服务(SERVICE:ALIAS) ```yaml # 可以以database为域名访问服务名称为db的容器 links: - db:database ``` ## Docker Compose常用命令 ### 构建、创建、启动相关容器 ```bash # -d表示在后台运行 docker-compose up -d ``` ### 指定文件启动 ```bash docker-compose -f docker-compose.yml up -d ``` ### 停止所有相关容器 ```bash docker-compose stop ``` ### 列出所有容器信息 ```bash docker-compose ps ``` ## 使用Docker Compose 部署应用 ### 编写docker-compose.yml文件 > Docker Compose将所管理的容器分为三层,工程、服务及容器。docker-compose.yml中定义所有服务组成了一个工程,services节点下即为服务,服务之下为容器。容器与容器直之间可以以服务名称为域名进行访问,比如在mall-tiny-docker-compose服务中可以通过jdbc:mysql://db:3306这个地址来访问db这个mysql服务。 ```yaml version: '3' services: # 指定服务名称 db: # 指定服务使用的镜像 image: mysql:5.7 # 指定容器名称 container_name: mysql # 指定服务运行的端口 ports: - 3306:3306 # 指定容器中需要挂载的文件 volumes: - /mydata/mysql/log:/var/log/mysql - /mydata/mysql/data:/var/lib/mysql - /mydata/mysql/conf:/etc/mysql # 指定容器的环境变量 environment: - MYSQL_ROOT_PASSWORD=root # 指定服务名称 mall-tiny-docker-compose: # 指定服务使用的镜像 image: mall-tiny/mall-tiny-docker-compose:0.0.1-SNAPSHOT # 指定容器名称 container_name: mall-tiny-docker-compose # 指定服务运行的端口 ports: - 8080:8080 # 指定容器中需要挂载的文件 volumes: - /etc/localtime:/etc/localtime - /mydata/app/mall-tiny-docker-compose/logs:/var/logs ``` **注意:如果遇到mall-tiny-docker-compose服务无法连接到mysql,需要在mysql中建立mall数据库,同时导入mall.sql脚本。具体参考[使用Dockerfile为SpringBoot应用构建Docker镜像](https://mp.weixin.qq.com/s/U_OcNMpLAJJum_s9jbZLGg)中的运行mysql服务并设置部分。** ### 使用maven插件构建mall-tiny-docker-compose镜像 ![](../images/refer_screen_97.png) **注意:构建有问题的可以参考[使用Maven插件为SpringBoot应用构建Docker镜像](https://mp.weixin.qq.com/s/q2KDzHbPkf3Q0EY8qYjYgw)** ### 运行Docker Compose命令启动所有服务 先将docker-compose.yml上传至Linux服务器,再在当前目录下运行如下命令: ```bash docker-compose up -d ``` ![](../images/refer_screen_98.png) 访问接口文档地址http://192.168.3.101:8080/swagger-ui.html: ![](../images/refer_screen_94.png) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-docker-compose](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-docker-compose) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/docker_file.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 使用Dockerfile为SpringBoot应用构建Docker镜像 > 上次写过一篇[使用Maven插件构建Docker镜像](https://mp.weixin.qq.com/s/q2KDzHbPkf3Q0EY8qYjYgw) ,讲述了通过docker-maven-plugin来构建docker镜像的方式,此种方式需要依赖自建的Registry镜像仓库。本文将讲述另一种方式,使用Dockerfile来构建docker镜像,此种方式不需要依赖自建的镜像仓库,只需要应用的jar包和一个Dockerfile文件即可。 ## Dockerfile常用指令 ### ADD 用于复制文件,格式: ``` ADD ``` 示例: ```shell # 将当前目录下的mall-tiny-docker-file.jar包复制到docker容器的/目录下 ADD mall-tiny-docker-file.jar /mall-tiny-docker-file.jar ``` ### ENTRYPOINT 指定docker容器启动时执行的命令,格式: ``` ENTRYPOINT ["executable", "param1","param2"...] ``` 示例: ```shell # 指定docker容器启动时运行jar包 ENTRYPOINT ["java", "-jar","/mall-tiny-docker-file.jar"] ``` ### ENV 用于设置环境变量,格式: ``` ENV ``` 示例: ```shell # mysql运行时设置root密码 ENV MYSQL_ROOT_PASSWORD root ``` ### EXPOSE 声明需要暴露的端口(只声明不会打开端口),格式: ``` EXPOSE ... ``` 示例: ```shell # 声明服务运行在8080端口 EXPOSE 8080 ``` ### FROM 指定所需依赖的基础镜像,格式: ``` FROM : ``` 示例: ```shell # 该镜像需要依赖的java8的镜像 FROM java:8 ``` ### MAINTAINER 指定维护者的名字,格式: ``` MAINTAINER ``` 示例: ```shell MAINTAINER macrozheng ``` ### RUN 在容器构建过程中执行的命令,我们可以用该命令自定义容器的行为,比如安装一些软件,创建一些文件等,格式: ``` RUN RUN ["executable", "param1","param2"...] ``` 示例: ```shell # 在容器构建过程中需要在/目录下创建一个mall-tiny-docker-file.jar文件 RUN bash -c 'touch /mall-tiny-docker-file.jar' ``` ## 使用Dockerfile构建SpringBoot应用镜像 ### 编写Dockerfile文件 ```shell # 该镜像需要依赖的基础镜像 FROM java:8 # 将当前目录下的jar包复制到docker容器的/目录下 ADD mall-tiny-docker-file-0.0.1-SNAPSHOT.jar /mall-tiny-docker-file.jar # 运行过程中创建一个mall-tiny-docker-file.jar文件 RUN bash -c 'touch /mall-tiny-docker-file.jar' # 声明服务运行在8080端口 EXPOSE 8080 # 指定docker容器启动时运行jar包 ENTRYPOINT ["java", "-jar","/mall-tiny-docker-file.jar"] # 指定维护者的名字 MAINTAINER macrozheng ``` ### 使用maven打包应用 在IDEA中双击package命令进行打包: ![](../images/refer_screen_91.png) 打包成功后展示: ```shell [INFO] --- spring-boot-maven-plugin:2.1.3.RELEASE:repackage (repackage) @ mall-tiny-docker-file --- [INFO] Replacing main artifact with repackaged archive [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 30.749 s [INFO] Finished at: 2019-06-16T14:11:07+08:00 [INFO] Final Memory: 43M/306M [INFO] ------------------------------------------------------------------------ ``` 将应用jar包及Dockerfile文件上传到linux服务器: ![](../images/refer_screen_92.png) ![](../images/refer_screen_95.png) ### 在Linux上构建docker镜像 在Dockerfile所在目录执行以下命令: ```shell # -t 表示指定镜像仓库名称/镜像名称:镜像标签 .表示使用当前目录下的Dockerfile docker build -t mall-tiny/mall-tiny-docker-file:0.0.1-SNAPSHOT . ``` 输出如下信息: ```shell Sending build context to Docker daemon 36.37MB Step 1/5 : FROM java:8 ---> d23bdf5b1b1b Step 2/5 : ADD mall-tiny-docker-file-0.0.1-SNAPSHOT.jar /mall-tiny-docker-file.jar ---> c920c9e9d045 Step 3/5 : RUN bash -c 'touch /mall-tiny-docker-file.jar' ---> Running in 55506f517f19 Removing intermediate container 55506f517f19 ---> 0727eded66dc Step 4/5 : EXPOSE 8080 ---> Running in d67a5f50aa7d Removing intermediate container d67a5f50aa7d ---> 1b8b4506eb2d Step 5/5 : ENTRYPOINT ["java", "-jar","/mall-tiny-docker-file.jar"] ---> Running in 0c5bf61a0032 Removing intermediate container 0c5bf61a0032 ---> c3614dad21b7 Successfully built c3614dad21b7 Successfully tagged mall-tiny/mall-tiny-docker-file:0.0.1-SNAPSHOT ``` 查看docker镜像: ![](../images/refer_screen_93.png) ### 运行mysql服务并设置 #### 1.使用docker命令启动: ```shell docker run -p 3306:3306 --name mysql \ -v /mydata/mysql/log:/var/log/mysql \ -v /mydata/mysql/data:/var/lib/mysql \ -v /mydata/mysql/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD=root \ -d mysql:5.7 ``` #### 2.进入运行mysql的docker容器: ```shell docker exec -it mysql /bin/bash ``` #### 3.使用mysql命令打开客户端: ```shell mysql -uroot -proot --default-character-set=utf8 ``` #### 4.修改root帐号的权限,使得任何ip都能访问: ```sql grant all privileges on *.* to 'root'@'%' ``` #### 5.创建mall数据库: ```sql create database mall character set utf8 ``` #### 6.将mall.sql文件拷贝到mysql容器的/目录下: ```shell docker cp /mydata/mall.sql mysql:/ ``` #### 7.将sql文件导入到数据库: ```shell use mall; source /mall.sql; ``` ### 运行mall-tiny-docker-file应用 ```shell docker run -p 8080:8080 --name mall-tiny-docker-file \ --link mysql:db \ -v /etc/localtime:/etc/localtime \ -v /mydata/app/mall-tiny-docker-file/logs:/var/logs \ -d mall-tiny/mall-tiny-docker-file:0.0.1-SNAPSHOT ``` 访问接口文档地址http://192.168.3.101:8080/swagger-ui.html: ![](../images/refer_screen_94.png) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-docker-file](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-docker-file) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/docker_maven.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 使用Maven插件为SpringBoot应用构建Docker镜像 >本文主要介绍如何使用Maven插件将SpringBoot应用打包为Docker镜像,并上传到私有镜像仓库Docker Registry的过程。 ## Docker Registry ### Docker Registry 2.0搭建 ```shell docker run -d -p 5000:5000 --restart=always --name registry2 registry:2 ``` 如果遇到镜像下载不下来的情况,需要修改 /etc/docker/daemon.json 文件并添加上 registry-mirrors 键值,然后重启docker服务: ```json { "registry-mirrors": ["https://registry.docker-cn.com"] } ``` ### Docker开启远程API #### 用vim编辑器修改docker.service文件 ``` vi /usr/lib/systemd/system/docker.service ``` 需要修改的部分: ```shell ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock ``` 修改后的部分: ```shell ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock ``` #### 让Docker支持http上传镜像 ```shell echo '{ "insecure-registries":["192.168.3.101:5000"] }' > /etc/docker/daemon.json ``` #### 修改配置后需要使用如下命令使配置生效 ```shell systemctl daemon-reload ``` #### 重新启动Docker服务 ```shell systemctl stop docker systemctl start docker ``` #### 开启防火墙的Docker构建端口 ```shell firewall-cmd --zone=public --add-port=2375/tcp --permanent firewall-cmd --reload ``` ## 使用Maven构建Docker镜像 > 该代码是在mall-tiny-02的基础上修改的。 ### 在应用的pom.xml文件中添加docker-maven-plugin的依赖 ```xml com.spotify docker-maven-plugin 1.1.0 build-image package build mall-tiny/${project.artifactId}:${project.version} http://192.168.3.101:2375 java:8 ["java", "-jar","/${project.build.finalName}.jar"] / ${project.build.directory} ${project.build.finalName}.jar ``` 相关配置说明: - executions.execution.phase:此处配置了在maven打包应用时构建docker镜像; - imageName:用于指定镜像名称,mall-tiny是仓库名称,`${project.artifactId}`为镜像名称,`${project.version}`为仓库名称; - dockerHost:打包后上传到的docker服务器地址; - baseImage:该应用所依赖的基础镜像,此处为java; - entryPoint:docker容器启动时执行的命令; - resources.resource.targetPath:将打包后的资源文件复制到该目录; - resources.resource.directory:需要复制的文件所在目录,maven打包的应用jar包保存在target目录下面; - resources.resource.include:需要复制的文件,打包好的应用jar包。 ### 修改application.yml,将localhost改为db > 可以把docker中的容器看作独立的虚拟机,mall-tiny-docker访问localhost自然会访问不到mysql,docker容器之间可以通过指定好的服务名称db进行访问,至于db这个名称可以在运行mall-tiny-docker容器的时候指定。 ```yml spring: datasource: url: jdbc:mysql://db:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root ``` ### 使用IDEA打包项目并构建镜像 >注意:依赖的基础镜像需要先行下载,否则会出现构建镜像超时的情况,比如我本地并没有java8的镜像,就需要先把镜像pull下来,再用maven插件进行构建。 - 执行maven的package命令: ![](../images/refer_screen_68.png) - 构建成功: ![](../images/refer_screen_66.png) - 镜像仓库已有该镜像: ![](../images/refer_screen_67.png) ## 运行mall-tiny-docker项目 ### 启动mysql服务 - 使用docker命令启动: ```shell docker run -p 3306:3306 --name mysql \ -v /mydata/mysql/log:/var/log/mysql \ -v /mydata/mysql/data:/var/lib/mysql \ -v /mydata/mysql/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD=root \ -d mysql:5.7 ``` - 进入运行mysql的docker容器: ```shell docker exec -it mysql /bin/bash ``` - 使用mysql命令打开客户端: ```shell mysql -uroot -proot --default-character-set=utf8 ``` ![](../images/refer_screen_69.png) - 修改root帐号的权限,使得任何ip都能访问: ```sql grant all privileges on *.* to 'root'@'%' ``` ![](../images/refer_screen_70.png) - 创建mall数据库: ```sql create database mall character set utf8 ``` - 将[mall.sql](https://github.com/macrozheng/mall-learning/blob/master/document/sql/mall.sql)文件拷贝到mysql容器的/目录下: ```shell docker cp /mydata/mall.sql mysql:/ ``` - 将sql文件导入到数据库: ```shell use mall; source /mall.sql; ``` ![](../images/refer_screen_71.png) ### 启动mall-tiny-docker应用服务 - 使用docker命令启动(--link表示应用可以用db这个域名访问mysql服务): ```shell docker run -p 8080:8080 --name mall-tiny-docker \ --link mysql:db \ -v /etc/localtime:/etc/localtime \ -v /mydata/app/mall-tiny-docker/logs:/var/logs \ -d mall-tiny/mall-tiny-docker:0.0.1-SNAPSHOT ``` ![](../images/refer_screen_72.png) - 开启8080端口: ```shell firewall-cmd --zone=public --add-port=8080/tcp --permanent firewall-cmd --reload ``` - 进行访问测试,地址:[http://192.168.3.101:8080/swagger-ui.html](http://192.168.3.101:8080/swagger-ui.html) ![](../images/refer_screen_73.png) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-docker](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-docker) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/docker_protect_socket.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Docker服务开放了这个端口,服务器分分钟变肉机! > 之前有很多朋友提过,当使用`docker-maven-plugin`打包SpringBoot应用的Docker镜像时,服务器需要开放`2375`端口。由于开放了端口没有做任何安全保护,会引起安全漏洞,被人入侵、挖矿、CPU飙升这些情况都有发生,今天我们来聊聊如何解决这个问题。 ## 问题产生的原因 首先我们要明白问题产生的原因,才能更好地解决问题! Docker为了实现集群管理,提供了远程管理的端口。Docker Daemon作为守护进程运行在后台,可以执行发送到管理端口上的Docker命令。 当我们修改`docker.service`文件,修改启动命令,加入`-H tcp://0.0.0.0:2375`时,就会开放`2375`端口,且没有任何加密和认证过程,这种方式一般用在内网测试环境。如果你的服务器部署在公网上,任何知道你IP的人,都可以管理这台主机上的容器和镜像,想想就觉得可怕。 ## 解决思路 开放远程管理端口后,没有做任何安全保护导致了这个问题。我们只要使用安全传输层协议(TLS)进行传输并使用CA认证即可。 ## 制作证书及秘钥 > 我们需要使用OpenSSL制作CA机构证书、服务端证书和客户端证书,以下操作均在安装Docker的Linux服务器上进行。 - 首先创建一个目录用于存储生成的证书和秘钥; ```bash mkdir /mydata/docker-ca && cd /mydata/docker-ca ``` - 创建CA证书私钥,期间需要输入两次用户名和密码,生成文件为`ca-key.pem`; ```bash openssl genrsa -aes256 -out ca-key.pem 4096 ``` - 根据私钥创建CA证书,期间需要输入上一步设置的私钥密码,生成文件为`ca.pem`; ```bash openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -subj "/CN=*" -out ca.pem ``` - 创建服务端私钥,生成文件为`server-key.pem`; ```bash openssl genrsa -out server-key.pem 4096 ``` - 创建服务端证书签名请求文件,用于CA证书给服务端证书签名,生成文件`server.csr`; ```bash openssl req -subj "/CN=*" -sha256 -new -key server-key.pem -out server.csr ``` - 创建CA证书签名好的服务端证书,期间需要输入CA证书私钥密码,生成文件为`server-cert.pem`; ```bash openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem ``` - 创建客户端私钥,生成文件为`key.pem`; ```bash openssl genrsa -out key.pem 4096 ``` - 创建客户端证书签名请求文件,用于CA证书给客户证书签名,生成文件`client.csr`; ```bash openssl req -subj "/CN=client" -new -key key.pem -out client.csr ``` - 为了让秘钥适合客户端认证,创建一个扩展配置文件`extfile-client.cnf`; ```bash echo extendedKeyUsage = clientAuth > extfile-client.cnf ``` - 创建CA证书签名好的客户端证书,期间需要输入CA证书私钥密码,生成文件为`cert.pem`; ```bash openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile-client.cnf ``` - 删除创建过程中多余的文件; ```bash rm -rf ca.srl server.csr client.csr extfile-client.cnf ``` - 最终生成文件如下,有了它们我们就可以进行基于TLS的安全访问了。 ``` ca.pem CA证书 ca-key.pem CA证书私钥 server-cert.pem 服务端证书 server-key.pem 服务端证书私钥 cert.pem 客户端证书 key.pem 客户端证书私钥 ``` ## 配置Docker支持TLS - 用vim编辑器修改docker.service文件; ```bash vi /usr/lib/systemd/system/docker.service ``` - 修改以`ExecStart`开头的配置,开启TLS认证,并配置好CA证书、服务端证书和服务端私钥,修改内容如下; ```bash ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 --tlsverify --tlscacert=/mydata/docker-ca/ca.pem --tlscert=/mydata/docker-ca/server-cert.pem --tlskey=/mydata/docker-ca/server-key.pem ``` - 重启Docker服务,这样我们的Docker服务就支持使用TLS进行远程访问了! ```bash systemctl daemon-reload && systemctl restart docker ``` ## 客户端访问 > 接下来我们将使用`docker-maven-plugin`来打包Docker镜像,使用的代码为原来的`mall-tiny-docker`例子。 - 直接使用`docker-maven-plugin`打包试试,由于我们的插件版本有点低,使用新一点版本的Docker会出现如下问题,升级到`1.2.2`版本解决该问题; ``` [ERROR] Failed to execute goal com.spotify:docker-maven-plugin:1.1.0:build (build-image) on project mall-tiny-docker: Exception caught: com.spotify.docker.client.shaded.com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.spotify.docker.client.messages.RegistryAuth` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('desktop') [ERROR] at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: java.util.LinkedHashMap["credsStore"]) [ERROR] -> [Help 1] ``` - 修改完版本后打包,发现TLS不再支持`http`了,需要改用`https`,修改``配置为`https`; ``` [ERROR] Failed to execute goal com.spotify:docker-maven-plugin:1.2.2:build (build-image) on project mall-tiny-docker: Exception caught: Request error: GET http://192.168.3.101:2375/version: 400, body: Client sent an HTTP request to an HTTPS server. HTTP 400 Bad Request -> [Help 1] ``` - 修改完成后再次打包,继续失败,需要添加对应的客户端证书才能访问; ``` [ERROR] Failed to execute goal com.spotify:docker-maven-plugin:1.2.2:build (build-image) on project mall-tiny-docker: Exception caught: java.util.concurrent.ExecutionException: com.spotify.docker.client.shaded.javax.ws.rs.ProcessingException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target -> [Help 1] ``` - 将如下文件复制到指定目录,这里复制到了`I:\developer\env\docker-ca`; ``` ca.pem CA证书 cert.pem 客户端证书 key.pem 客户端证书私钥 ``` - 然后将该目录配置在插件的``节点下,最终插件配置如下; ```xml com.spotify docker-maven-plugin 1.2.2 build-image package build mall-tiny/${project.artifactId}:${project.version} https://192.168.3.101:2375 java:8 ["java", "-jar","/${project.build.finalName}.jar"] I:\developer\env\docker-ca / ${project.build.directory} ${project.build.finalName}.jar ``` - 再次打包镜像,发现已经可以成功打包镜像,从此我们的`2375`端口终于可以安全使用了! ``` [INFO] Building image mall-tiny/mall-tiny-docker:0.0.1-SNAPSHOT Step 1/3 : FROM java:8 ---> d23bdf5b1b1b Step 2/3 : ADD /mall-tiny-docker-0.0.1-SNAPSHOT.jar // ---> 5cb5a64ccedd Step 3/3 : ENTRYPOINT ["java", "-jar","/mall-tiny-docker-0.0.1-SNAPSHOT.jar"] ---> Running in 5f3ceefdd974 Removing intermediate container 5f3ceefdd974 ---> ee9d0e2b0114 ProgressMessage{id=null, status=null, stream=null, error=null, progress=null, progressDetail=null} Successfully built ee9d0e2b0114 Successfully tagged mall-tiny/mall-tiny-docker:0.0.1-SNAPSHOT [INFO] Built mall-tiny/mall-tiny-docker:0.0.1-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 20.550 s [INFO] Finished at: 2020-07-31T15:02:15+08:00 [INFO] Final Memory: 50M/490M [INFO] ------------------------------------------------------------------------ ``` ## 参考资料 官方文档:https://docs.docker.com/engine/security/https/ ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-docker ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/efk_fluent.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 性能优越的轻量级日志收集工具,微软、亚马逊都在用! > `ELK`日志收集系统大家都知道,但是还有一种日志收集系统`EFK`,肯定有很多朋友不知道!这里的`F`指的是`Fluentd`,它具有Logstash类似的日志收集功能,但是内存占用连Logstash的十分之一都不到,性能优越、非常轻巧。本文将详细介绍Fluentd的使用,主要用来收集SpringBoot应用的日志,希望对大家有所帮助! ## Fluentd 简介 Fluentd是一款开源的日志收集功能,致力于为用户搭建统一的日志收集层,和Elasticsearch、Kibana一起使用可以搭建EFK日志收集系统。什么是统一的日志收集层呢?看下下面这张图就清楚了! ![来自Fluentd官网](../images/efk_fluent_01.png) ## 安装 > 在[《你居然还去服务器上捞日志,搭个日志收集系统难道不香么!》](https://mp.weixin.qq.com/s/8nUunL02Y5AfXTCscYg54w)中已经介绍了ELK日志收集系统的搭建,这里就不再介绍Elasticsearch和Kibana的安装了,直接介绍Fluentd在Docker环境下的安装。 - 下载Fluentd的Docker镜像; ```bash docker pull fluent/fluentd:v1.10 ``` - 将默认配置`fluent.conf`文件复制到`/mydata/fluentd/`目录下,配置信息如下: ``` @type forward @id input1 @label @mainstream port 24224 @type stdout ``` - 运行Fluentd服务,需要开放`24221~24224`四个端口用于接收不同类型的日志; ```bash docker run -p 24221:24221 -p 24222:24222 -p 24223:24223 -p 24224:24224 --name efk-fluentd \ -v /mydata/fluentd/log:/fluentd/log \ -v /mydata/fluentd/fluent.conf:/fluentd/etc/fluent.conf \ -d fluent/fluentd:v1.10 ``` - 第一次启动可能会失败,修改目录权限后重新启动即可; ```bash chmod 777 /mydata/fluentd/log/ ``` - 使用`root`用户进入Fluentd容器内部; ```bash docker exec -it --user root efk-fluentd /bin/sh ``` - 安装Fluentd的Elasticsearch插件; ```bash fluent-gem install fluent-plugin-elasticsearch ``` - 如果你依然想使用`docker-compose`一次性安装EFK的话,可以使用如下脚本,`注意`使用`user:root`启动就不需要再修改目录权限了! ```yaml version: '3' services: elasticsearch: image: elasticsearch:6.4.0 container_name: efk-elasticsearch user: root environment: - "cluster.name=elasticsearch" #设置集群名称为elasticsearch - "discovery.type=single-node" #以单一节点模式启动 - "ES_JAVA_OPTS=-Xms512m -Xmx512m" #设置使用jvm内存大小 - TZ=Asia/Shanghai volumes: - /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins #插件文件挂载 - /mydata/elasticsearch/data:/usr/share/elasticsearch/data #数据文件挂载 ports: - 9200:9200 - 9300:9300 kibana: image: kibana:6.4.0 container_name: efk-kibana links: - elasticsearch:es #可以用es这个域名访问elasticsearch服务 depends_on: - elasticsearch #kibana在elasticsearch启动之后再启动 environment: - "elasticsearch.hosts=http://es:9200" #设置访问elasticsearch的地址 - TZ=Asia/Shanghai ports: - 5601:5601 fluentd: image: fluent/fluentd:v1.10 container_name: efk-fluentd user: root environment: - TZ=Asia/Shanghai volumes: - /mydata/fluentd/log:/fluentd/log - /mydata/fluentd/fluent.conf:/fluentd/etc/fluent.conf depends_on: - elasticsearch #kibana在elasticsearch启动之后再启动 links: - elasticsearch:es #可以用es这个域名访问elasticsearch服务 ports: - 24221:24221 - 24222:24222 - 24223:24223 - 24224:24224 ``` - 使用新的配置文件`fluent.conf`替换原来的配置文件,然后重新启动Fluentd服务,新的配置文件会在下面给出。 ## Fluentd配置详解 > 接下来我们来介绍下Fluentd配置文件如何配置,先放出完全配置,然后我们对里面的一些配置要点进行详细说明。 ### 完全配置 ``` @type tcp @id debug-input port 24221 tag debug @type json @type tcp @id error-input port 24222 tag error @type json @type tcp @id business-input port 24223 tag business @type json @type tcp @id record-input port 24224 tag record @type json @type parser key_name message reserve_data true remove_key_name_field true @type json @type stdout output_type json @type elasticsearch host 192.168.3.101 port 9200 type_name docker logstash_format true logstash_prefix docker-${tag}-logs logstash_dateformat %Y-%m-%d flush_interval 5s include_tag_key true ``` ### 配置要点解析 #### `` > 定义了日志收集的来源,可以有tcp、udp、tail(文件)、forward(tcp+udp)、http等方式。 这里我们从tcp请求收集日志,端口为`24221`,并且设置了tag为`debug`。 ``` @type tcp @id debug-input port 24221 tag debug @type json ``` #### `` > 定义对原始数据的解析方式,可以将日志转化为JSON。 比如我们将debug日志转化为JSON可以进行如下配置。 ``` @type tcp @id debug-input port 24221 tag debug @type json ``` #### `` > 可以对收集的日志进行一系列的处理,比如说将日志打印到控制台或者对日志进行解析。 将所有日志打印到控制台的配置: ``` @type stdout ``` 对于tag为`record`来源的日志,我们将其中的`message`属性转化为JSON格式,如果不进行转化的话,`message`属性将会是一个字符串。 ``` @type parser key_name message reserve_data true remove_key_name_field true @type json ``` #### `` > 定义了收集到的日志最后输出到哪里,可以输出到stdout(控制台)、file、elasticsearch、mongo等里面。 这里我们使用`elasticsearch`来存储日志信息,`logstash_format`、`logstash_prefix`、`logstash_dateformat`主要用来控制日志索引名称的生成,当前配置生成debug日志的索引格式为`docker-debug-logs-2020-06-03`,`flush_interval`用来控制日志输出到elasticsearch的时间间隔。 ``` @type elasticsearch host 192.168.3.101 port 9200 type_name docker logstash_format true logstash_prefix docker-${tag}-logs logstash_dateformat %Y-%m-%d flush_interval 5s include_tag_key true ``` ### 替换配置文件 替换掉原来的`/mydata/fluentd/fluent.conf`配置文件,然后再重新启动服务,我们的Fluentd服务就可以开始收集日志了。 ```bash docekr restart efk-fluentd ``` ## 结合SpringBoot使用 > 其实Fluentd收集日志的原理和Logstash一样,都是通过tcp端口来收集日志,所以我们只要把logback配置文件中原来Logstash日志收集地址端口改为Fluentd的即可。 - 修改`logback-spring.xml`配置文件; ```xml ${LOG_STASH_HOST}:24221 ${LOG_STASH_HOST}:24222 ${LOG_STASH_HOST}:24223 ${LOG_STASH_HOST}:24224 ``` - 如果你的Fluentd不是部署在原来Logstash的服务器上,还需要修改`application-dev.yml`配置中的`logstash.host`属性。 ```yaml logstash: host: localhost ``` - 启动并运行我们的SpringBoot应用。 ## Kibana中查看日志 > 至此我们的EFK日志收集系统搭建完成了,只需在Kibana中使用即可。 - 在`Management->Kibana->Index Patterns`中可以创建`Index Patterns`,Kibana服务访问地址:http://192.168.3.101:5601 ![](../images/efk_fluent_02.png) - 创建完成后查看日志,可以看出该日志收集功能和我们之前搭建的ELK系统完全相同。 ![](../images/efk_fluent_03.png) ## Logstash vs Fluentd > 接下来我们来对这两个日志收集工具的各个方面做个对比。 | 对比方面 | Logstash | Fluentd | | -------------- | ------------------------------ | -------------------------- | | 内存占用 | 启动1G左右 | 启动60M左右 | | CPU占用 | 较高 | 较低 | | 支持插件 | 丰富 | 丰富 | | 通用日志解析 | 支持grok(基于正则表达式)解析 | 支持正则表达式解析 | | 特定日志类型 | 支持JSON等主流格式 | 支持JSON等主流格式 | | 数据过滤 | 支持 | 支持 | | 数据buffer发送 | 插件支持 | 插件支持 | | 运行环境 | JRuby实现,依赖JVM环境 | CRuby、C实现,依赖Ruby环境 | | 线程支持 | 支持多线程 | 多线程受GIL限制 | ## 参考资料 官方文档:https://docs.fluentd.org/ ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-log ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/elastic_apm_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 微服务应用性能如何?APM监控工具来告诉你! > 当微服务系统越来越庞大,各个服务间的调用关系也变得越来越复杂,需要一个工具来帮忙理清请求调用的服务链路。之前在[《Spring Cloud Sleuth:分布式请求链路跟踪》](https://mp.weixin.qq.com/s/FEBeiU7PHrkXa8RrosXqlw)一文中使用的是Sleuth+Zipkin的解决方案,最近发现应用性能监控(Application Performance Monitoring,APM)也可以很好地解决该问题。对比SkyWalking和Elastic APM之后,发现Elastic APM更胜一筹,今天我们来一波Elastic APM的使用实践! ## Elastic APM 简介 Elastic APM是基于Elastic Stack构建的应用性能监控(APM)系统。它主要有如下用途: - 用来实时监控应用性能信息,包括HTTP请求调用时长、数据库查询信息、缓存调用信息和外部的HTTP请求调用信息。有助于我们快速找出并解决性能问题。 - 自动收集应用中未处理的错误和异常,显示异常的堆栈信息,有助于快速定位异常和了解出现频率。 - 度量指标是调试生产系统时的另一个重要信息来源。Elastic APM Agent 会自动收集主机级别的度量指标(比如Java JVM和Go Runtime的指标)。 - 支持分布式请求链路追踪,使你能够在一个视图中分析整个服务架构的性能问题。 ## 相关组件 Elastic APM 包括四大组件: APM Agent, APM Server, Elasticsearch, Kibana。 ![](../images/elastic_apm_start_01.png) - APM Agent:以应用程序库的形式提供,负责收集应用运行时的性能监控数据和错误数据,短时间缓存后发送APM Server。 - APM Server:一个独立的组件,负责接收APM Agent中发送的性能监控数据。验证并处理完数据后,会转存储到Elasticsearch中,之后就可以在Kibana APM 应用中查看性能监控数据了。 - Elasticsearch:用于存储应用性能监控数据并提供聚合功能。 - Kibana APM app:可视化查看APM性能监控数据,有助于找到性能瓶颈。 ## 数据模型 Elastic APM Agent 从其检测的应用程序中捕获不同类型的信息。这些操作被称为事件,可以是Span, Transaction, Error, or Metric。 - Span(跨度):Span包含一次操作过程中代码执行路径的信息。它从操作的开始到结束进行度量,并且可以与其他Span具有父/子关系。 - Transaction(事务):Transaction是一种特殊的Span,具有与之关联的其他属性。它描述了Elastic APM Agent捕获的最高级别事件,比如一次请求、一次批处理任务等。 - Error(错误):Error事件至少包含错误发生的原始异常或创建的日志的信息。 - Metric(度量):APM Agent 自动获取基本的主机级别指标,包括系统和进程级别的CPU和内存指标。也可以获取特定于代理的指标,例如Java Agent中的JVM指标和Go Agent中的Go运行时指标。 ## 使用实践 > 学习了上面的基本概念之后,是时候来波实践了,接下来我们将使用Elastic APM来监控SpringBoot应用的性能信息。 ### 安装Elasticsearch和Kibana 安装Elastic APM之前,我们需要先安装好Elasticsearch和Kibana,具体参考[《你居然还去服务器上捞日志,搭个日志收集系统难道不香么!》](https://mp.weixin.qq.com/s/8nUunL02Y5AfXTCscYg54w),注意使用7.6.2版本。 ### 安装APM Server - 下载APM Server的安装包,下载地址:https://www.elastic.co/cn/downloads/past-releases/apm-server-7-6-2 ![](../images/elastic_apm_start_02.png) - 下载完成后解压到指定目录; ![](../images/elastic_apm_start_03.png) - 修改配置文件`apm-server.yml`,修改下Elasticsearch的连接地址即可; ```yaml output.elasticsearch: hosts: ["localhost:9200"] ``` - 使用如下命令启动APM Server即可,启动成功APM Server将在`8200`端口运行; ```bash apm-sever -e ``` - 在Kibana中检测APM Server是否启动成功,访问地址:http://localhost:5601/app/kibana#/home/tutorial/apm ![](../images/elastic_apm_start_04.png) ### SpringBoot集成APM Agent > Java应用集成APM Agent的方式有三种,我们使用最简单的方式,直接在应用中集成。 - 在`pom.xml`中添加相关依赖; ```xml co.elastic.apm apm-agent-attach 1.17.0 ``` - 在应用启动类的`main`方法中添加Elastic APM的Attach API; ```java @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { ElasticApmAttacher.attach(); SpringApplication.run(MallTinyApplication.class, args); } } ``` - 在`resource`目录下添加Elastic APM的配置文件`elasticapm.properties`; ```properties # 配置服务名称 service_name=mall-tiny-apm # 配置应用所在基础包 application_packages=com.macro.mall.tiny # 配置APM Server的访问地址 server_urls=http://localhost:8200 ``` - 在Kibana中检测APM Agent是否启动成功,访问地址:http://localhost:5601/app/kibana#/home/tutorial/apm ![](../images/elastic_apm_start_05.png) ### 查看性能监控信息 - 打开监控面板以后,可以发现我们的`mall-tiny-apm`服务已经存在了; ![](../images/elastic_apm_start_06.png) - 多次调用应用接口,即可查看到应用性能信息; ![](../images/elastic_apm_start_07.png) - 打开某个`Transaction`查看详情,我们可以看到连SQL执行耗时信息都给我们统计好了; ![](../images/elastic_apm_start_08.png) - 不仅如此,打开执行查询的`Span`查看详情,连SQL语句都给我们收集好了; ![](../images/elastic_apm_start_09.png) - 在项目中添加一个有远程调用接口,看看能不能收集到请求调用链路; ```java /** * 品牌管理Controller * Created by macro on 2019/4/19. */ @Api(tags = "PmsBrandController", description = "商品品牌管理") @Controller @RequestMapping("/brand") public class PmsBrandController { @ApiOperation("远程调用获取所有品牌信息") @RequestMapping(value = "/remoteListAll", method = RequestMethod.GET) @ResponseBody public CommonResult> remoteListAll() { //模拟耗时操作 ThreadUtil.sleep(1, TimeUnit.SECONDS); //远程调用获取数据 String response = HttpUtil.get("http://localhost:8088/brand/listAll"); JSONObject jsonObject = new JSONObject(response); JSONArray data = jsonObject.getJSONArray("data"); List brandList = data.toList(PmsBrand.class); return CommonResult.success(brandList); } } ``` - 发现完全可以,Elastic APM完全可以取代Sleuth+Zipkin来做微服务的请求链路跟踪了; ![](../images/elastic_apm_start_10.png) - 使用我们之前`springcloud-learning`中的微服务调用案例,也是可以进行请求链路跟踪的; ![](../images/elastic_apm_start_13.png) - 接下来我们人为制造一个异常,在方法中添加`int i=1/0;`即可,查看下收集到的异常信息; ![](../images/elastic_apm_start_11.png) - 再来看下应用主机的度量信息,非常全面,CPU、内存、JVM信息都有了,以后性能调优的时候可以看看! ![](../images/elastic_apm_start_12.png) ## 总结 Elastic APM 完全可以取代Sleuth+Zipkin来做分布式请求链路追踪,并且提供了数据库及缓存调用时长的统计,很好很强大!不止于此,它还可以用来实时监控应用性能信息及度量指标,连错误日志也收集好了,是一款很好的应用性能监控工具! ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-apm ## 参考资料 官方文档:https://www.elastic.co/guide/en/apm/index.html ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/elasticsearch_sql_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Elasticsearch官方已支持SQL查询,用起来贼方便! > 平时使用Elasticsearch的时候,偶尔会在Kibana中使用Query DSL来查询数据。每次要用到Query DSL时都基本忘光了,需要重新在回顾一遍,最近发现Elasticsearch已经支持SQL查询了(6.3版本以后),整理了下其用法,希望对大家有所帮助! ## 简介 Elasticsearch SQL是一个X-Pack组件,它允许针对Elasticsearch实时执行类似SQL的查询。无论使用REST接口,命令行还是JDBC,任何客户端都可以使用SQL对Elasticsearch中的数据进行原生搜索和聚合数据。可以将Elasticsearch SQL看作是一种翻译器,它可以将SQL翻译成Query DSL。 Elasticsearch SQL具有如下特性: - 原生支持:Elasticsearch SQL是专门为Elasticsearch打造的。 - 没有额外的零件:无需其他硬件,处理器,运行环境或依赖库即可查询Elasticsearch,Elasticsearch SQL直接在Elasticsearch内部运行。 - 轻巧高效:Elasticsearch SQL并未抽象化其搜索功能,相反的它拥抱并接受了SQL来实现全文搜索,以简洁的方式实时运行全文搜索。 ## 学前准备 学习之前我们需要先对Elasticsearch有所了解,并安装好Elasticsearch和Kibana,这里安装的是7.6.2版本,具体可以参考[《Elasticsearch快速入门,掌握这些刚刚好!》](https://mp.weixin.qq.com/s/cohWZy_eUOUqbmUxhXzzNA)。 安装完成后在Kibana中导入测试数据,数据地址: https://github.com/macrozheng/mall-learning/blob/master/document/json/accounts.json 直接在Kibana的Dev Tools中运行如下命令即可: ![](../images/es_sql_start_01.png) ## 第一个SQL查询 我们使用SQL来查询下前10条记录,可以通过`format`参数控制返回结果的格式,txt表示文本格式,看起来更直观点,默认为json格式。 在Kibana的Console中输入如下命令: ``` POST /_sql?format=txt { "query": "SELECT account_number,address,age,balance FROM account LIMIT 10" } ``` 查询结果显示如下。 ![](../images/es_sql_start_02.png) ## 将SQL转化为DSL 当我们需要使用Query DSL时,也可以先使用SQL来查询,然后通过Translate API转换即可。 例如我们翻译以下查询语句: ``` POST /_sql/translate { "query": "SELECT account_number,address,age,balance FROM account WHERE age>32 LIMIT 10" } ``` 最终获取到Query DSL结果如下。 ![](../images/es_sql_start_03.png) ## SQL和DSL混合使用 我们还可以将SQL和Query DSL混合使用,比如使用Query DSL来设置过滤条件。 例如查询age在30-35之间的记录,可以使用如下查询语句: ``` POST /_sql?format=txt { "query": "SELECT account_number,address,age,balance FROM account", "filter": { "range": { "age": { "gte" : 30, "lte" : 35 } } }, "fetch_size": 10 } ``` 查询结果展示如下: ![](../images/es_sql_start_04.png) ## SQL和ES对应关系 | SQL | ES | 描述 | | ------ | -------- | ---------------------------------- | | column | field | 数据库中表的字段与ES中的属性对应 | | row | document | 数据库表中的行记录与ES中的文档对应 | | table | index | 数据库中的表与ES中的索引对应 | ## 常用SQL操作 ### 语法 在ES中使用SQL查询的语法与在数据库中使用基本一致,具体格式如下: ```sql SELECT select_expr [, ...] [ FROM table_name ] [ WHERE condition ] [ GROUP BY grouping_element [, ...] ] [ HAVING condition] [ ORDER BY expression [ ASC | DESC ] [, ...] ] [ LIMIT [ count ] ] [ PIVOT ( aggregation_expr FOR column IN ( value [ [ AS ] alias ] [, ...] ) ) ] ``` ### WHERE 可以使用`WHERE`语句设置查询条件,比如查询state字段为VA的记录,查询语句如下。 ``` POST /_sql?format=txt { "query": "SELECT account_number,address,age,balance,state FROM account WHERE state='VA' LIMIT 10 " } ``` 查询结果展示如下: ![](../images/es_sql_start_05.png) ### GROUP BY 我们可以使用`GROUP BY`语句对数据进行分组,统计出分组记录数量,最大age和平均balance等信息,查询语句如下。 ``` POST /_sql?format=txt { "query": "SELECT state,COUNT(*),MAX(age),AVG(balance) FROM account GROUP BY state LIMIT 10" } ``` ![](../images/es_sql_start_06.png) ### HAVING 我们可以使用`HAVING`语句对分组数据进行二次筛选,比如筛选分组记录数量大于15的信息,查询语句如下。 ``` POST /_sql?format=txt { "query": "SELECT state,COUNT(*),MAX(age),AVG(balance) FROM account GROUP BY state HAVING COUNT(*)>15 LIMIT 10" } ``` ![](../images/es_sql_start_07.png) ### ORDER BY 我们可以使用`ORDER BY`语句对数据进行排序,比如按照balance字段从高到低排序,查询语句如下。 ``` POST /_sql?format=txt { "query": "SELECT account_number,address,age,balance,state FROM account ORDER BY balance DESC LIMIT 10 " } ``` ![](../images/es_sql_start_08.png) ### DESCRIBE 我们可以使用`DESCRIBE`语句查看表(ES中为索引)中有哪些字段,比如查看account表的字段,查询语句如下。 ``` POST /_sql?format=txt { "query": "DESCRIBE account" } ``` ![](../images/es_sql_start_09.png) ### SHOW TABLES 我们可以使用`SHOW TABLES`查看所有的表(ES中为索引)。 ``` POST /_sql?format=txt { "query": "SHOW TABLES" } ``` ![](../images/es_sql_start_10.png) ## 支持的函数 使用SQL查询ES中的数据,不仅可以使用一些SQL中的函数,还可以使用一些ES中特有的函数。 ### 查询支持的函数 我们可以使用`SHOW FUNCTIONS`语句查看所有支持的函数,比如搜索所有带有`DATE`字段的函数可以使用如下语句。 ``` POST /_sql?format=txt { "query": "SHOW FUNCTIONS LIKE '%DATE%'" } ``` ![](../images/es_sql_start_11.png) ### 全文搜索函数 全文搜索函数是ES中特有的,当使用`MATCH`或`QUERY`函数时,会启用全文搜索功能,`SCORE`函数可以用来统计搜索评分。 #### MATCH() 使用`MATCH`函数查询address中包含Street的记录。 ``` POST /_sql?format=txt { "query": "SELECT account_number,address,age,balance,SCORE() FROM account WHERE MATCH(address,'Street') LIMIT 10" } ``` ![](../images/es_sql_start_12.png) #### QUERY() 使用`QUERY`函数查询address中包含Street的记录。 ``` POST /_sql?format=txt { "query": "SELECT account_number,address,age,balance,SCORE() FROM account WHERE QUERY('address:Street') LIMIT 10" } ``` ![](../images/es_sql_start_13.png) ## SQL CLI 如果你不想使用Kibana来使用ES SQL的话,也可以使用ES自带的SQL CLI来查询,该命令位于ES的bin目录下。 使用如下命令启动SQL CLI: ```bash elasticsearch-sql-cli http://localhost:9200 ``` ![](../images/es_sql_start_14.png) 然后直接输入SQL命令即可查询了,注意要加分号。 ```sql SELECT account_number,address,age,balance FROM account LIMIT 10; ``` ![](../images/es_sql_start_15.png) ## 局限性 使用SQL查询ES有一定的局限性,没有原生的Query DSL那么强大,对于嵌套属性和某些函数的支持并不怎么好,但是平时用来查询下数据基本够用了。 ## 参考资料 官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.6/xpack-sql.html ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/elasticsearch_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Elasticsearch快速入门,掌握这些刚刚好! > 记得刚接触Elasticsearch的时候,没找啥资料,直接看了遍Elasticsearch的中文官方文档,中文文档很久没更新了,一直都是2.3的版本。最近又重新看了遍6.0的官方文档,由于官方文档介绍的内容比较多,每次看都很费力,所以这次整理了其中最常用部分,写下了这篇入门教程,希望对大家有所帮助。 ## 简介 Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式的全文搜索引擎,基于restful web接口。Elasticsearch是用Java语言开发的,基于Apache协议的开源项目,是目前最受欢迎的企业搜索引擎。Elasticsearch广泛运用于云计算中,能够达到实时搜索,具有稳定,可靠,快速的特点。 ## 安装 ### Windows下的安装 #### Elasticsearch - 下载Elasticsearch 6.2.2的zip包,并解压到指定目录,下载地址:https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2 ![](../images/elasticsearch_start_01.png) - 安装中文分词插件,在elasticsearch-6.2.2\bin目录下执行以下命令; ```bash elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.2/elasticsearch-analysis-ik-6.2.2.zip ``` ![](../images/elasticsearch_start_02.png) - 运行bin目录下的elasticsearch.bat启动Elasticsearch; ![](../images/elasticsearch_start_03.png) #### Kibana - 下载Kibana,作为访问Elasticsearch的客户端,请下载6.2.2版本的zip包,并解压到指定目录,下载地址:https://artifacts.elastic.co/downloads/kibana/kibana-6.2.2-windows-x86_64.zip ![](../images/elasticsearch_start_04.png) - 运行bin目录下的kibana.bat,启动Kibana的用户界面 ![](../images/elasticsearch_start_05.png) - 访问[http://localhost:5601](http://localhost:5601) 即可打开Kibana的用户界面: ![](../images/elasticsearch_start_06.png) ### Linux下的安装 #### Elasticsearch - 下载elasticsearch 6.4.0的docker镜像; ```bash docker pull elasticsearch:6.4.0 ``` - 修改虚拟内存区域大小,否则会因为过小而无法启动; ```bash sysctl -w vm.max_map_count=262144 ``` - 使用docker命令启动; ```bash docker run -p 9200:9200 -p 9300:9300 --name elasticsearch \ -e "discovery.type=single-node" \ -e "cluster.name=elasticsearch" \ -v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \ -v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \ -d elasticsearch:6.4.0 ``` - 启动时会发现`/usr/share/elasticsearch/data`目录没有访问权限,只需要修改该目录的权限,再重新启动即可; ```bash chmod 777 /mydata/elasticsearch/data/ ``` - 安装中文分词器IKAnalyzer,并重新启动; ```bash docker exec -it elasticsearch /bin/bash #此命令需要在容器中运行 elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.4.0/elasticsearch-analysis-ik-6.4.0.zip docker restart elasticsearch ``` - 访问会返回版本信息:[http://192.168.3.101:9200/](http://192.168.3.101:9200/) ![](../images/elasticsearch_start_07.png) #### Kibina - 下载kibana 6.4.0的docker镜像; ```bash docker pull kibana:6.4.0 ``` - 使用docker命令启动; ```bash docker run --name kibana -p 5601:5601 \ --link elasticsearch:es \ -e "elasticsearch.hosts=http://es:9200" \ -d kibana:6.4.0 ``` - 访问地址进行测试:[http://192.168.3.101:5601](http://192.168.3.101:5601) ![](../images/elasticsearch_start_08.png) ## 相关概念 - Near Realtime(近实时):Elasticsearch是一个近乎实时的搜索平台,这意味着从索引文档到可搜索文档之间只有一个轻微的延迟(通常是一秒钟)。 - Cluster(集群):群集是一个或多个节点的集合,它们一起保存整个数据,并提供跨所有节点的联合索引和搜索功能。每个群集都有自己的唯一群集名称,节点通过名称加入群集。 - Node(节点):节点是指属于集群的单个Elasticsearch实例,存储数据并参与集群的索引和搜索功能。可以将节点配置为按集群名称加入特定集群,默认情况下,每个节点都设置为加入一个名为`elasticsearch`的群集。 - Index(索引):索引是一些具有相似特征的文档集合,类似于MySql中数据库的概念。 - Type(类型):类型是索引的逻辑类别分区,通常,为具有一组公共字段的文档类型,类似MySql中表的概念。`注意`:在Elasticsearch 6.0.0及更高的版本中,一个索引只能包含一个类型。 - Document(文档):文档是可被索引的基本信息单位,以JSON形式表示,类似于MySql中行记录的概念。 - Shards(分片):当索引存储大量数据时,可能会超出单个节点的硬件限制,为了解决这个问题,Elasticsearch提供了将索引细分为分片的概念。分片机制赋予了索引水平扩容的能力、并允许跨分片分发和并行化操作,从而提高性能和吞吐量。 - Replicas(副本):在可能出现故障的网络环境中,需要有一个故障切换机制,Elasticsearch提供了将索引的分片复制为一个或多个副本的功能,副本在某些节点失效的情况下提供高可用性。 ## 集群状态查看 - 查看集群健康状态; ```bash GET /_cat/health?v ``` ```bash epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent 1585552862 15:21:02 elasticsearch yellow 1 1 27 27 0 0 25 0 - 51.9% ``` - 查看节点状态; ```bash GET /_cat/nodes?v ``` ```bash ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name 127.0.0.1 23 94 28 mdi * KFFjkpV ``` - 查看所有索引信息; ```bash GET /_cat/indices?v ``` ```bash health status index uuid pri rep docs.count docs.deleted store.size pri.store.size green open pms xlU0BjEoTrujDgeL6ENMPw 1 0 41 0 30.5kb 30.5kb green open .kibana ljKQtJdwT9CnLrxbujdfWg 1 0 2 1 10.7kb 10.7kb ``` ## 索引操作 - 创建索引并查看; ```bash PUT /customer GET /_cat/indices?v ``` ```bash health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open customer 9uPjf94gSq-SJS6eOuJrHQ 5 1 0 0 460b 460b green open pms xlU0BjEoTrujDgeL6ENMPw 1 0 41 0 30.5kb 30.5kb green open .kibana ljKQtJdwT9CnLrxbujdfWg 1 0 2 1 10.7kb 10.7kb ``` - 删除索引并查看; ```bash DELETE /customer GET /_cat/indices?v ``` ```bash health status index uuid pri rep docs.count docs.deleted store.size pri.store.size green open pms xlU0BjEoTrujDgeL6ENMPw 1 0 41 0 30.5kb 30.5kb green open .kibana ljKQtJdwT9CnLrxbujdfWg 1 0 2 1 10.7kb 10.7kb ``` ## 类型操作 - 查看文档的类型; ``` GET /bank/account/_mapping ``` ```json { "bank": { "mappings": { "account": { "properties": { "account_number": { "type": "long" }, "address": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "age": { "type": "long" }, "balance": { "type": "long" }, "city": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "email": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "employer": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "firstname": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "gender": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "lastname": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "state": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } } } } } ``` ## 文档操作 - 在索引中添加文档; ``` PUT /customer/doc/1 { "name": "John Doe" } ``` ```json { "_index": "customer", "_type": "doc", "_id": "1", "_version": 1, "result": "created", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 3, "_primary_term": 1 } ``` - 查看索引中的文档; ``` GET /customer/doc/1 ``` ```json { "_index": "customer", "_type": "doc", "_id": "1", "_version": 2, "found": true, "_source": { "name": "John Doe" } } ``` - 修改索引中的文档: ``` POST /customer/doc/1/_update { "doc": { "name": "Jane Doe" } } ``` ```json { "_index": "customer", "_type": "doc", "_id": "1", "_version": 2, "result": "updated", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 4, "_primary_term": 1 } ``` - 删除索引中的文档; ``` DELETE /customer/doc/1 ``` ```json { "_index": "customer", "_type": "doc", "_id": "1", "_version": 3, "result": "deleted", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 2, "_primary_term": 1 } ``` - 对索引中的文档执行批量操作; ``` POST /customer/doc/_bulk {"index":{"_id":"1"}} {"name": "John Doe" } {"index":{"_id":"2"}} {"name": "Jane Doe" } ``` ```json { "took": 45, "errors": false, "items": [ { "index": { "_index": "customer", "_type": "doc", "_id": "1", "_version": 3, "result": "updated", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 5, "_primary_term": 1, "status": 200 } }, { "index": { "_index": "customer", "_type": "doc", "_id": "2", "_version": 1, "result": "created", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 0, "_primary_term": 1, "status": 201 } } ] } ``` ## 数据搜索 > 查询表达式(Query DSL)是一种非常灵活又富有表现力的查询语言,Elasticsearch使用它可以以简单的JSON接口来实现丰富的搜索功能,下面的搜索操作都将使用它。 ### 数据准备 - 首先我们需要导入一定量的数据用于搜索,使用的是银行账户表的例子,数据结构如下: ```json { "account_number": 0, "balance": 16623, "firstname": "Bradshaw", "lastname": "Mckenzie", "age": 29, "gender": "F", "address": "244 Columbus Place", "employer": "Euron", "email": "bradshawmckenzie@euron.com", "city": "Hobucken", "state": "CO" } ``` - 我们先复制下需要导入的数据,数据地址: https://github.com/macrozheng/mall-learning/blob/master/document/json/accounts.json - 然后直接使用批量操作来导入数据,注意本文所有操作都在Kibana的Dev Tools中进行; ``` POST /bank/account/_bulk { "index": { "_id": "1" } } { "account_number": 1, "balance": 39225, "firstname": "Amber", "lastname": "Duke", "age": 32, "gender": "M", "address": "880 Holmes Lane", "employer": "Pyrami", "email": "amberduke@pyrami.com", "city": "Brogan", "state": "IL" } ......省略若干条数据 ``` ![](../images/elasticsearch_start_09.png) - 导入完成后查看索引信息,可以发现`bank`索引中已经创建了1000条文档。 ```bash GET /_cat/indices?v ``` ```bash health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open bank HFjxDLNLRA-NATPKUQgjBw 5 1 1000 0 474.6kb 474.6kb ``` ### 搜索入门 - 最简单的搜索,使用`match_all`来表示,例如搜索全部; ``` GET /bank/_search { "query": { "match_all": {} } } ``` ![](../images/elasticsearch_start_10.png) - 分页搜索,`from`表示偏移量,从0开始,`size`表示每页显示的数量; ``` GET /bank/_search { "query": { "match_all": {} }, "from": 0, "size": 10 } ``` ![](../images/elasticsearch_start_11.png) - 搜索排序,使用`sort`表示,例如按`balance`字段降序排列; ``` GET /bank/_search { "query": { "match_all": {} }, "sort": { "balance": { "order": "desc" } } } ``` ![](../images/elasticsearch_start_12.png) - 搜索并返回指定字段内容,使用`_source`表示,例如只返回`account_number`和`balance`两个字段内容: ``` GET /bank/_search { "query": { "match_all": {} }, "_source": ["account_number", "balance"] } ``` ![](../images/elasticsearch_start_13.png) ### 条件搜索 - 条件搜索,使用`match`表示匹配条件,例如搜索出`account_number`为`20`的文档: ``` GET /bank/_search { "query": { "match": { "account_number": 20 } } } ``` ![](../images/elasticsearch_start_14.png) - 文本类型字段的条件搜索,例如搜索`address`字段中包含`mill`的文档,对比上一条搜索可以发现,对于数值类型`match`操作使用的是精确匹配,对于文本类型使用的是模糊匹配; ``` GET /bank/_search { "query": { "match": { "address": "mill" } }, "_source": [ "address", "account_number" ] } ``` ![](../images/elasticsearch_start_15.png) - 短语匹配搜索,使用`match_phrase`表示,例如搜索`address`字段中同时包含`mill`和`lane`的文档: ``` GET /bank/_search { "query": { "match_phrase": { "address": "mill lane" } } } ``` ![](../images/elasticsearch_start_16.png) ### 组合搜索 - 组合搜索,使用`bool`来进行组合,`must`表示同时满足,例如搜索`address`字段中同时包含`mill`和`lane`的文档; ``` GET /bank/_search { "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } } ``` ![](../images/elasticsearch_start_16.png) - 组合搜索,`should`表示满足其中任意一个,搜索`address`字段中包含`mill`或者`lane`的文档; ``` GET /bank/_search { "query": { "bool": { "should": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } } ``` ![](../images/elasticsearch_start_17.png) - 组合搜索,`must_not`表示同时不满足,例如搜索`address`字段中不包含`mill`且不包含`lane`的文档; ``` GET /bank/_search { "query": { "bool": { "must_not": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } } ``` ![](../images/elasticsearch_start_18.png) - 组合搜索,组合`must`和`must_not`,例如搜索`age`字段等于`40`且`state`字段不包含`ID`的文档; ``` GET /bank/_search { "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } } } ``` ![](../images/elasticsearch_start_19.png) ### 过滤搜索 - 搜索过滤,使用`filter`来表示,例如过滤出`balance`字段在`20000~30000`的文档; ``` GET /bank/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "balance": { "gte": 20000, "lte": 30000 } } } } } } ``` ![](../images/elasticsearch_start_20.png) ### 搜索聚合 - 对搜索结果进行聚合,使用`aggs`来表示,类似于MySql中的`group by`,例如对`state`字段进行聚合,统计出相同`state`的文档数量; ``` GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" } } } } ``` ![](../images/elasticsearch_start_21.png) - 嵌套聚合,例如对`state`字段进行聚合,统计出相同`state`的文档数量,再统计出`balance`的平均值; ``` GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } } ``` ![](../images/elasticsearch_start_22.png) - 对聚合搜索的结果进行排序,例如按`balance`的平均值降序排列; ``` GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword", "order": { "average_balance": "desc" } }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } } ``` ![](../images/elasticsearch_start_23.png) - 按字段值的范围进行分段聚合,例如分段范围为`age`字段的`[20,30]` `[30,40]` `[40,50]`,之后按`gender`统计文档个数和`balance`的平均值; ``` GET /bank/_search { "size": 0, "aggs": { "group_by_age": { "range": { "field": "age", "ranges": [ { "from": 20, "to": 30 }, { "from": 30, "to": 40 }, { "from": 40, "to": 50 } ] }, "aggs": { "group_by_gender": { "terms": { "field": "gender.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } } } } ``` ![](../images/elasticsearch_start_24.png) ## 参考资料 https://www.elastic.co/guide/en/elasticsearch/reference/6.0/getting-started.html ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/elk_security.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 居然有人想白嫖我的日志,赶紧开启安全保护压压惊! > 在[《你居然还去服务器上捞日志,搭个日志收集系统难道不香么!》](https://mp.weixin.qq.com/s/8nUunL02Y5AfXTCscYg54w)一文中我们介绍过ELK日志收集系统的搭建,由于我们的Kibana没有任何安全保护机制,如果部署到公网上去的话,任何人都可以查看你的日志了。日志暴露在网络上可不是件好事情,今天教大家如何给Kibana设置登录认证来保护它。 ## 实现原理 由于Kibana的日志信息都存储在Elasticsearch中,所以只要给Elasticsearch开启`X-PACK`中的安全功能,并给预置的账号设置好密码即可。Elasticsearch设置好之后,就可以在Kibana中对用户、角色、权限进行管理了,本文使用的ELK组件版本均为`7.6.2`。 ## Elasticsearch设置密码 - 修改Elasticsearch的配置文件并开启`X-PACK`中的安全功能,该配置文件在安装目录的`config`文件夹下面,例如`elasticsearch-7.6.2\config\elasticsearch.yml`; ```yaml http.cors.enabled: true http.cors.allow-origin: "*" http.cors.allow-headers: Authorization xpack.security.enabled: true xpack.security.transport.ssl.enabled: true ``` - 启动Elasticsearch服务,启动命令在`bin`目录下,例如`elasticsearch-7.6.2\bin\elasticsearch.bat`; - 在`bin`目录下使用如下命令`elasticsearch-setup-passwords interactive`修改预置账号的密码,期间需要设置多个账号密码,我都设置成了`123456`; ![](../images/elk_security_01.png) - 期间设置了好几个账号,我们先来了解下这些账号都有啥作用吧; ``` elastic:超级管理员账号 kibana:Kibana访问专用账号 logstash_system:Logstash访问专用账号 beats_system:FileBeat访问专用账号 apm_system:APM系统专用账号 remote_monitoring_user:远程监控账号 ``` - 接下来我们需要在Kibana的配置文件中添加可以访问Elasticsearch的账号,该配置文件在安装目录的`config`文件夹下面,例如`kibana-7.6.2\config\kibana.yml`; ```yaml elasticsearch.username: "kibana" elasticsearch.password: "123456" ``` - 启动Kibana服务,启动命令在`bin`目录下,例如`kibana-7.6.2\bin\kibana.bat`; - 当Kibana启动完成后,我们访问的时就需要登录认证了,使用超级管理员账号`elastic:123456`可以进行登录,访问地址:http://localhost:5601 ![](../images/elk_security_02.png) - 登录成功后,在我们的`Management`选项中可以找到安全相关的配置,在此我们可以对用户、角色、权限进行设置。 ![](../images/elk_security_03.png) ## SpringBoot安全访问 > 由于Elasticsearch开启`X-PACK`中的安全功能,当我们的SpringBoot应用访问Elasticsearch时,也需要设置用户名和密码了! - 我们可以直接在SpringBoot中设置超级管理员账号,但这不是个好办法,我们还是自己建个角色和账号吧! - 首先在Kibana中创建一个应用访问专用的角色`app_user`; ![](../images/elk_security_04.png) - 创建一个用户并配置好该角色,账号密码为`app:123456`; ![](../images/elk_security_05.png) - 修改SpringBoot应用的配置文件`application.yml`,配置好账号密码即可正常访问了! ```yaml spring: elasticsearch: rest: uris: http://localhost:9200 username: app password: 123456 ``` ## Logstash安全访问 > 由于Elasticsearch开启`X-PACK`中的安全功能,向Elasticsearch输出日志的Logstash也需要设置用户名和密码了! - 首先修改我们原来的Logstash配置文件`logstash.conf`,在`output`节点下设置访问Elasticsearch的用户名和密码,直接使用我们创建的`app:123456`账号即可; ``` input { tcp { mode => "server" host => "0.0.0.0" port => 4560 codec => json_lines type => "debug" } tcp { mode => "server" host => "0.0.0.0" port => 4561 codec => json_lines type => "error" } tcp { mode => "server" host => "0.0.0.0" port => 4562 codec => json_lines type => "business" } tcp { mode => "server" host => "0.0.0.0" port => 4563 codec => json_lines type => "record" } } filter{ if [type] == "record" { mutate { remove_field => "port" remove_field => "host" remove_field => "@version" } json { source => "message" remove_field => ["message"] } } } output { elasticsearch { hosts => ["localhost:9200"] action => "index" codec => json index => "mall-tiny-%{type}-%{+YYYY.MM.dd}" template_name => "mall-tiny" user => app password => "123456" } } ``` - 使用指定配置文件启动Logstash服务,启动命令在`bin`目录下,例如`logstash-7.6.2\bin\logstash.bat`; ```bash logstash -f logstash.conf ``` - 接下来在Kibana中就可以查看到应用输出的日志了! ![](../images/elk_security_06.png) ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-log ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/filebeat_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 面对成百上千台服务器产生的日志,试试这款轻量级日志搬运神器! > 之前我们搭建的ELK日志收集系统,主要是用来收集SpringBoot应用的日志。其原理是应用通过Logstash插件,使用TCP向Logstash传输日志,从而存储到Elasticsearch中去。但是有很多中间件的日志都是直接存储在文件中的,比如`Nginx`、`Elasticsearch`和`MySQL`,此时我们就需要一个搬运工来把日志搬到Elasticsearch中去,Filebeat正是这样一个日志搬运工,本文将详细介绍它的使用方法,希望对大家有所帮助。 ## Filebeat简介 Filebeat是一款轻量级日志采集器,可用于转发和汇总日志与文件。Filebeat内置有多种模块(Nginx、MySQL、Redis、Elasticsearch、Logstash等),可针对常见格式的日志大大简化收集、解析和可视化过程,只需一条命令即可。 ## Filebeat安装及配置 > 安装Filebeat之前,我们需要先安装好Elasticsearch和Kibana,具体参考[《你居然还去服务器上捞日志,搭个日志收集系统难道不香么!》](https://mp.weixin.qq.com/s/8nUunL02Y5AfXTCscYg54w),注意使用7.6.2版本。 - 我们先下载Filebeat的安装包,下载地址:https://www.elastic.co/cn/downloads/past-releases/filebeat-7-6-2 ![](../images/filebeat_start_01.png) - 下载完成后解压到指定目录,注意图中所示的三个地方; ![](../images/filebeat_start_02.png) - Kibana中早就提供好了Filebeat收集各种日志的教程,我们先进入首页,访问地址:http://localhost:5601/app/kibana#/home ![](../images/filebeat_start_03.png) - 点击`Add log data`按钮,可以发现支持的中间件还是很丰富的,涵盖了常用的; ![](../images/filebeat_start_04.png) - 点开Nginx的日志收集教程看下,是不是很详细; ![](../images/filebeat_start_05.png) - 接下来我们就要开始配置Filebeat了,配置好Elasticsearch和Kibana的连接地址,修改Filebeat的安装目录下的配置文件`filebeat.yml`即可,修改内容如下。 ```yaml output.elasticsearch: hosts: ["localhost:9200"] setup.kibana: host: "localhost:5601" ``` ## 收集Nginx日志 > 我们先拿Nginx来练练手,体验下Filebeat的日志收集功能吧。 - 使用如下命令开启Filebeat的Nginx日志收集模块; ```bash filebeat modules enable nginx ``` - 开启完成后,进入`modules.d`目录下,你会发现`nginx.yml`的`disable`后缀没有了,说明已经被开启; ![](../images/filebeat_start_06.png) - 接下来修改`nginx.yml`文件,配置好Nginx的`access`和`error`日志路径; ```yaml # Module: nginx # Docs: https://www.elastic.co/guide/en/beats/filebeat/7.6/filebeat-module-nginx.html - module: nginx access: enabled: true var.paths: ["I:/developer/env/nginx-1.8.1/logs/access.log"] error: enabled: true var.paths: ["I:/developer/env/nginx-1.8.1/logs/error.log"] ``` - 由于启用了Nginx日志收集模块,我们需要通过如下命令对Filebeat进行设置; ```bash filebeat setup ``` ![](../images/filebeat_start_07.png) - 通过如下命令启动Filebeat服务; ```bash filebeat -e ``` - 我们可以通过之前使用的Kibana中的Nginx教程页面,检查下数据是否成功被收集了; ![](../images/filebeat_start_08.png) - 点击`Nginx logs dashboard`按钮可以查看收集到的Nginx日志,先看看概览日志; ![](../images/filebeat_start_09.png) - 再看看详细日志,从此不用再去服务器上捞Nginx日志了! ![](../images/filebeat_start_10.png) ## 收集Elasticsearch日志 > 接下来我们再试试收集Elasticsearch的日志。 - 使用如下命令开启Filebeat的Elasticsearch日志收集模块; ```bash filebeat modules enable elasticsearch ``` - 接下来修改`elasticsearch.yml`配置,配置好Elasticsearch的日志路径; ```yaml # Module: elasticsearch # Docs: https://www.elastic.co/guide/en/beats/filebeat/7.6/filebeat-module-elasticsearch.html - module: elasticsearch server: enabled: true var.paths: ["I:/developer/env/elasticsearch-7.6.2/logs/elasticsearch.log"] slowlog: enabled: true var.paths: ["I:/developer/env/elasticsearch-7.6.2/logs/elasticsearch_index_indexing_slowlog.log","I:/developer/env/elasticsearch-7.6.2/logs/elasticsearch_index_search_slowlog.log"] deprecation: enabled: true var.paths: ["I:/developer/env/elasticsearch-7.6.2/logs/elasticsearch_deprecation.log"] ``` - 设置并启动Filebeat服务; ```bash filebeat setup filebeat -e ``` - 在`Discover`页面中查看Elasticsearch日志。 ![](../images/filebeat_start_11.png) ## 收集MySQL日志 > 再来试试收集MySQL的日志。 - 使用如下命令开启Filebeat的MySQL日志收集模块; ```bash filebeat modules enable mysql ``` - 接下来修改`mysql.yml`配置,配置好MySQL的日志路径,主要是错误日志和慢查询日志; ```yaml # Module: mysql # Docs: https://www.elastic.co/guide/en/beats/filebeat/7.6/filebeat-module-mysql.html - module: mysql error: enabled: true var.paths: ["C:/ProgramData/MySQL/MySQL Server 5.7/Data/DESKTOP-5NIMJ19.err"] slowlog: enabled: true var.paths: ["C:/ProgramData/MySQL/MySQL Server 5.7/Data/DESKTOP-5NIMJ19-slow.log"] ``` - 设置并启动Filebeat服务; ```bash filebeat setup filebeat -e ``` - 点击`MySQL logs dashboard`按钮可以查看收集到的MySQL日志; ![](../images/filebeat_start_12.png) - 查看MySQL收集到的日志详情。 ![](../images/filebeat_start_13.png) ## 总结 本文主要介绍了使用Filebeat去收集Nginx、Elasticsearch和MySQL的文件日志,其他中间件的日志收集用法也基本相同,用Filebeat收集中间件日志是不是方便! ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/flyway_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 数据库迁移搞炸了!没用这款开源神器的锅? > 当我们的应用升级时往往会伴随着数据库表结构的升级,此时就需要迁移数据库的表结构。一般我们会使用工具或者脚本来实现,手动操作毕竟有一定风险,要是能在应用启动时自动升级数据库表结构就好了!Flyway正是这么一款工具,通过Flyway和SpringBoot结合使用,在应用启动时就可以自动升级数据库表结构,非常方便,推荐给大家! ## Flyway简介 Flyway是一款数据库迁移工具,它让数据库迁移变得更加简单。它能像Git一样对数据库进行版本控制,支持命令行工具、Maven插件、第三方工具(比如SpringBoot)等多种使用方式。 Flyway具有如下特点: - 简单:使用和学习简单,通过不同版本的SQL脚本实现数据库迁移。 - 专业:专注于数据库迁移功能,你无需担心有任何问题。 - 功能强大:支持多种数据库,拥有大量的第三方工具,支持CI/DI。 ## 相关概念 ### 工作原理 使用Flyway时我们需要编写好数据库迁移的SQL脚本,比如` V1__Initial_Setup.sql`中初始化了三种表,`V2__First_Changes.sql`中又新增了两种表。Flyway会创建`flyway_schema_history`表,用于存储这些SQL脚本的执行情况,从而对数据库进行版本控制。当我们使用Flyway进行数据库迁移时,Flyway会根据`flyway_schema_history`表中的记录,自行决定需要执行哪些SQL脚本,从而实现数据库迁移。 ![](../images/flyway_start_01.png) ### 脚本命名规范 在创建Flyway的SQL脚本时,有些命名规范需要遵守,这些命名规范决定了Flyway执行脚本的顺序和方式,可以先参考下面的示意图。 ![](../images/flyway_start_02.png) 为了能被Flyway正确执行,SQL迁移脚本需要遵循如下规范: - Prefix(前缀):`V`表示有版本号的数据库迁移,`U`表示一些数据库版本的回滚,`R`表示可重复执行的数据库迁移; - Version(版本号):Flyway会按照版本号的大小顺序来执行数据库迁移脚本; - Separator(分隔符):命名时使用双下划线分隔符; - Description(描述):用于描述该迁移脚本的具体操作说明; - Suffix(后缀):表示`.sql`文件。 ### 相关命令 - migrate:数据库迁移命令,会根据设置好的SQL脚本直接将数据库表升级至最新版本。 - clean:删除数据库中所有的表,千万别在生产环境上使用。 - info:打印所有关于数据库迁移的详细信息和状态信息。 - validate:验证数据库迁移是否可用。 - undo:对数据库迁移进行回滚操作。 - baseline:以现有数据库为基准,创建`flyway_schema_history`表,大于基准版本的数据库迁移才会被应用。 - repair:修复`flyway_schema_history`表。 ## 命令行工具 > 使用Flyway实现数据迁移有多种方式,我们先通过命令行工具的方法来体验下Flyway的使用。 - 首先需要下载Flyway的命令行工具,下载社区版即可,下载地址:https://flywaydb.org/download ![](../images/flyway_start_03.png) - 下载完成后进行解压,解压完成后目录结构如下; ![](../images/flyway_start_04.png) - 修改Flyway的配置文件`/conf/flyway.conf`,修改下数据库配置即可; ``` flyway.url=jdbc:mysql://localhost:3306/flyway?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai flyway.user=root flyway.password=root ``` - 在`/sql`目录下添加SQL执行脚本,这里添加创建`ums_admin`表的执行脚本`V1.0.1__Create_ums_admin_table.sql`; ```sql CREATE TABLE `ums_admin` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(64) DEFAULT NULL, `password` varchar(64) DEFAULT NULL, `icon` varchar(500) DEFAULT NULL COMMENT '头像', `email` varchar(100) DEFAULT NULL COMMENT '邮箱', `nick_name` varchar(200) DEFAULT NULL COMMENT '昵称', `note` varchar(500) DEFAULT NULL COMMENT '备注信息', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `login_time` datetime DEFAULT NULL COMMENT '最后登录时间', `status` int(1) DEFAULT '1' COMMENT '帐号启用状态:0->禁用;1->启用', PRIMARY KEY (`id`) ) ENGINE = InnoDB AUTO_INCREMENT = 8 DEFAULT CHARSET = utf8 COMMENT ='后台用户表'; ``` - 使用`flyway migrate`命令进行数据迁移,此时我们会发现需要先使用`flyway baseline`命令创建保存迁移记录的表`flyway_schema_history`才行; ![](../images/flyway_start_05.png) - 先使用`flyway baseline`命令,再使用`flyway migrate`命令,命令行会输出执行成功的信息; ![](../images/flyway_start_06.png) - 在`\sql`目录下添加SQL执行脚本,给`ums_admin`表添加一些数据,执行脚本为`V1.0.2__Add_ums_admin.sql`; ```sql INSERT INTO ums_admin (username, PASSWORD, email, nick_name, STATUS) VALUES ('test', '123456', 'test@qq.com', '测试账号', 1); INSERT INTO ums_admin (username, PASSWORD, email, nick_name, STATUS) VALUES ('macro', '123456', 'macro@qq.com', '普通账号', 1); INSERT INTO ums_admin (username, PASSWORD, email, nick_name, STATUS) VALUES ('andy', '123456', 'andy@qq.com', '普通账号', 1); ``` - 我们可以使用`flyway info`命令查看`flyway_schema_history`表中的数据迁移记录,可以发现`1.0.2`版本的更新还处于`Pending`状态,使用`flyway migrate`命令后变为`Success`; ![](../images/flyway_start_07.png) - 我们可以创建可重复执行的SQL脚本,通常可以用来创建视图、存储过程、函数等,比如基于`ums_admin`表创建一个视图,执行脚本为`R__Ums_admin_view.sql`; ```sql CREATE OR REPLACE VIEW ums_admin_view AS SELECT username, PASSWORD, email FROM ums_admin; ``` - 使用`flyway migrate`命令可以重复执行(当R开头的脚本有变更时),该脚本会在所有`V`开头的脚本执行完成后执行; ![](../images/flyway_start_08.png) - Flyway的回滚机制需要依赖SQL脚本,这里创建`U1.0.1__Create_ums_admin_table.sql`和`U1.0.2__Add_ums_admin.sql`两个回滚脚本; ```sql # U1.0.1__Create_ums_admin_table.sql DROP TABLE ums_admin ``` ```sql # U1.0.2__Add_ums_admin.sql DELETE FROM ums_admin; ``` - 使用`flyway undo`命令可以执行回滚,很遗憾的是社区版本不支持回滚,看样子数据库升级之前还是得通过工具做好备份才行! ![](../images/flyway_start_09.png) ## Maven插件 > Flyway也提供了Maven插件,插件所支持功能与命令行工具基本一致。 - 想要在Maven项目通过插件使用Flyway,首先需要在pom.xml中添加Flyway的插件并配置好数据库连接信息; ```xml org.flywaydb flyway-maven-plugin 7.3.2 jdbc:mysql://localhost:3306/flyway?serverTimezone=Asia/Shanghai root root mysql mysql-connector-java 8.0.15 ``` - 在resouce目录下创建`db\migration`目录,将数据库升级使用的SQL脚本放入进去; ![](../images/flyway_start_10.png) - Flyway的Maven插件支持如下几种命令; ![](../images/flyway_start_11.png) - 双击`flyway:info`命令使用,输出如下内容,此方式与命令行工具使用基本没啥区别。 ``` [INFO] --- flyway-maven-plugin:7.3.2:info (default-cli) @ mall-tiny-flyway --- [INFO] Flyway Community Edition 7.3.2 by Redgate [INFO] Database: jdbc:mysql://localhost:3306/flyway (MySQL 5.7) [INFO] Schema version: 1.0.2 [INFO] [INFO] +------------+---------+------------------------+----------+---------------------+----------+ | Category | Version | Description | Type | Installed On | State | +------------+---------+------------------------+----------+---------------------+----------+ | | 1 | << Flyway Baseline >> | BASELINE | 2020-12-24 11:17:35 | Baseline | | Versioned | 1.0.1 | Create ums admin table | SQL | 2020-12-24 11:17:42 | Success | | Versioned | 1.0.2 | Add ums admin | SQL | 2020-12-24 11:33:40 | Success | | Repeatable | | Ums admin view | SQL | 2020-12-24 11:33:40 | Success | +------------+---------+------------------------+----------+---------------------+----------+ [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 9.076 s [INFO] Finished at: 2020-12-24T14:28:16+08:00 [INFO] Final Memory: 28M/286M [INFO] ------------------------------------------------------------------------ Process finished with exit code 0 ``` ## 结合SpringBoot使用 > 由于SpringBoot官方已经支持了Flyway,所以Flyway结合SpringBoot使用非常简单! - 首先在pom.xml中添加Flyway相关依赖,注意无需添加Flyway的版本号: ```xml org.flywaydb flyway-core ``` - 修改配置文件`application.yml`,对数据源和Flyway进行配置; ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/flyway?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root flyway: # 启用Flyway功能 enabled: true # 禁用Flyway的clean命令,使用clean命令会删除schema下的所有表 clean-disabled: true # 设置Flyway的SQL脚本路径 locations: classpath:db/migration # 设置版本信息控制表名称,默认flyway_schema_history table: flyway_schema_history # 在执行migrate命令时需要有flyway_schema_history表,通过baseline命令可以生成该表 baseline-on-migrate: true # 指定baseline版本号,低于该版本的SQL脚本在migrate是不会执行 baseline-version: 1 # 设置字符编码 encoding: UTF-8 # 不允许不按顺序迁移 out-of-order: false # 设置Flyway管控的schema,不设置的话为datasourcel.url中指定的schema schemas: flyway # 执行migrate时开启校验 validate-on-migrate: true ``` - 最后直接运行SpringBoot应用,即可自动创建好对应的数据库,控制台会输出如下信息。 ``` 2020-12-24 14:38:15.659 INFO 10716 --- [ main] o.f.c.internal.license.VersionPrinter : Flyway Community Edition 6.4.1 by Redgate 2020-12-24 14:38:15.898 INFO 10716 --- [ main] o.f.c.internal.database.DatabaseFactory : Database: jdbc:mysql://localhost:3306/flyway (MySQL 5.7) 2020-12-24 14:38:15.972 INFO 10716 --- [ main] o.f.core.internal.command.DbValidate : Successfully validated 3 migrations (execution time 00:00.047s) 2020-12-24 14:38:15.988 INFO 10716 --- [ main] o.f.c.i.s.JdbcTableSchemaHistory : Creating Schema History table `flyway`.`flyway_schema_history` with baseline ... 2020-12-24 14:38:16.106 INFO 10716 --- [ main] o.f.core.internal.command.DbBaseline : Successfully baselined schema with version: 1 2020-12-24 14:38:16.122 INFO 10716 --- [ main] o.f.core.internal.command.DbMigrate : Current version of schema `flyway`: 1 2020-12-24 14:38:16.134 INFO 10716 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema `flyway` to version 1.0.1 - Create ums admin table 2020-12-24 14:38:16.248 INFO 10716 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema `flyway` to version 1.0.2 - Add ums admin 2020-12-24 14:38:16.281 INFO 10716 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema `flyway` with repeatable migration Ums admin view 2020-12-24 14:38:16.314 INFO 10716 --- [ main] o.f.core.internal.command.DbMigrate : Successfully applied 3 migrations to schema `flyway` (execution time 00:00.206s) ``` ## 总结 对比手动升级数据库表结构,使用Flyway自动升级更有优势。使用Flyway可以在我们升级应用时同时升级数据库,由于社区版本目前不支持数据库回滚,升级前做好备份是很有必要的。 ## 参考资料 官方文档:https://flywaydb.org/documentation/ ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-flyway ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/gaea.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 你还在代码里做读写分离么,试试这个中间件吧! > 传统的MySql读写分离方案是通过在代码中根据SQL语句的类型动态切换数据源来实现的,那么有没有什么中间件可以自动实现读写分离呢?小米开源的数据库中间件Gaea就可以实现,接下来我们将详细讲解如何使用Gaea来实现MySql的读写分离。 ## Gaea简介 Gaea是小米中国区电商研发部研发的基于MySql协议的数据库中间件,目前在小米商城大陆和海外得到广泛使用,包括订单、社区、活动等多个业务。Gaea支持分库分表、SQL路由、读写分离等基本特性,其中分库分表方案兼容了mycat和kingshard两个项目的路由方式。 ## MySql主从复制 使用Gaea需要依赖MySql的主从复制环境,关于MySql的主从复制可以参考:[MySql主从复制,从原理到实践!](https://mp.weixin.qq.com/s/W9G9_yo46zlttNwnz0DuQQ) ## 直接在Linux下安装 > 目前官方提供的是在Linux下直接安装的方式,我们先按此方法来安装Gaea。 ### 安装Go语言环境 > 由于Gaea是使用Go语言编写的,所以我们需要先安装Go语言的环境。 - 安装Go语言环境,下载地址:https://golang.org/dl/ ![](../images/gaea_use_01.png) - 下载完成后解压到`/mydata`目录下; ```bash tar -zxvf go1.13.5.linux-amd64.tar.gz -C /mydata/ ``` - 添加`/mydata/go/bin`目录到PATH变量中: ```bash # 编辑环境变量配置文件 vim /etc/profile # 在最后一行添加 export GOROOT=mydata/go export PATH=$PATH:$GOROOT/bin # 刷新配置文件 source /etc/profile ``` - 查看版本号,测试是否安装成功: ```bash go version ``` - 返回以下信息表示Go语言环境已经安装成功了: ```bash go version go1.13.5 linux/amd64 ``` ### 安装Gaea > 由于Gaea并没有提供安装包,所以我们需要自行编译源码获取可执行文件。 - 下载Gaea的源码,直接下载`zip`包即可,下载地址:https://github.com/XiaoMi/Gaea - 将下载好的压缩包进行解压操作,这里我们解压到`/mydata/gaea/`目录下: ```bash unzip Gaea-master.zip ``` - 进入`/mydata/gaea/`目录下,使用`make`命令对源码编译: ```bash make build ``` - `注意`:由于网络问题,某些Go的依赖会下载不下来导致编译失败,多尝试几次即可成功; - 编译完成后在`/mydata/gaea/bin`目录下会生成Gaea的执行文件`gaea`: ![](../images/gaea_use_02.png) - 由于我们没有搭建`etcd`配置中心,所以需要修改本地配置文件`/mydata/gaea/etc/gaea.ini`,将配置类型改为`file`: ```ini ; 配置类型,目前支持file/etcd两种方式,file方式不支持热加载 config_type=file ``` - 添加namespace配置文件,用于配置我们的主从数据库信息,配置文件地址:`/mydata/gaea/etc/file/namespace/mall_namespace_1.json` ![](../images/gaea_use_03.png) - 配置文件内容如下: ```json { "name": "mall_namespace_1", "online": true, "read_only": false, "allowed_dbs": { "mall": true }, "slow_sql_time": "1000", "black_sql": [ "" ], "allowed_ip": null, "slices": [ { "name": "slice-0", "user_name": "root", "password": "root", "master": "192.168.6.132:3307", "slaves": ["192.168.6.132:3308"], "statistic_slaves": null, "capacity": 12, "max_capacity": 24, "idle_timeout": 60 } ], "shard_rules": null, "users": [ { "user_name": "macro", "password": "123456", "namespace": "mall_namespace_1", "rw_flag": 2, "rw_split": 1, "other_property": 0 } ], "default_slice": "slice-0", "global_sequences": null } ``` ### namespace配置文件 > namespace的配置格式为json,包含分表、非分表、实例等配置信息,都可在运行时改变。 - 整体配置说明: | 字段名称 | 字段类型 | 字段含义 | | --------------- | ---------- | ------------------------------------------------------- | | name | string | namespace名称 | | online | bool | 是否在线,逻辑上下线使用 | | read_only | bool | 是否只读,namespace级别 | | allowed_dbs | map | 允许通过代理访问的数据库 | | default_phy_dbs | map | 默认数据库名, 与allowed_dbs一一对应 | | slow_sql_time | string | 慢sql时间,单位ms | | black_sql | string数组 | 黑名单sql | | allowed_ip | string数组 | 白名单IP | | slices | map数组 | 一主多从的物理实例,slice里map的具体字段可参照slice配置 | | shard_rules | map数组 | 分库、分表、特殊表的配置内容,具体字段可参照shard配置 | | users | map数组 | 应用端连接gaea所需要的用户配置,具体字段可参照users配置 | - slice配置: | 字段名称 | 字段类型 | 字段含义 | | ---------------- | ---------- | ---------------------------------------------- | | name | string | 分片名称,自动、有序生成 | | user_name | string | 数据库用户名 | | password | string | 数据库密码 | | master | string | 主实例地址 | | slaves | string数组 | 从数据库地址,可以配置多个 | | statistic_slaves | string数组 | 统计型从实例地址列表 | | capacity | int | gaea_proxy与每个实例的连接池大小 | | max_capacity | int | gaea_proxy与每个实例的连接池最大大小 | | idle_timeout | int | gaea_proxy与后端mysql空闲连接存活时间,单位:秒 | - users配置: | 字段名称 | 字段类型 | 字段含义 | | -------------- | -------- | ---------------------------------------------------- | | user_name | string | 数据库代理用户名,客户端通过该用户名访问 | | password | string | 数据库代理密码,客户端通过该用户名访问 | | namespace | string | 对应的命名空间 | | rw_flag | int | 读写标识, 只读=1, 读写=2 | | rw_split | int | 是否读写分离, 非读写分离=0, 读写分离=1 | | other_property | int | 目前用来标识是否走统计从实例, 普通用户=0, 统计用户=1 | ## 在Docker容器中运行 > 由于官方只提供了Linux下直接安装运行的方式,这里我们提供另一种运行方式,在Docker容器中作为服务运行。 ### 打包成Docker镜像 > Docker Hub 中并没有打包好的Gaea镜像,我们需要自行构建一个,下面详细介绍下如何构建Gaea的Docker镜像。 - 这里我们使用Dockerfile构建Docker镜像,Dockerfile中的内容如下: ```dockerfile # 该镜像需要依赖的基础镜像 FROM golang:latest # 将当前目录下的gaea源码包复制到docker容器的/go/Gaea-master目录下,对于.tar.gz文件会自动解压 ADD Gaea-master.tar.gz /go/Gaea-master # 将解压后的源码移动到/go/gaea目录中去 RUN bash -c 'mv /go/Gaea-master/Gaea-master /go/gaea' # 进入/go/gaea目录 WORKDIR /go/gaea # 将gaea源码进行打包编译 RUN bash -c 'make build' # 声明服务运行在13306端口 EXPOSE 13306 # 指定docker容器启动时执行的命令 ENTRYPOINT ["/go/gaea/bin/gaea"] # 指定维护者的名字 MAINTAINER macrozheng ``` - 在此之前我们需要把Gaea的源码压缩包转换为`.tar.gz`格式方便在Docker容器中的解压,可以使用`压缩软件`来实现: ![](../images/gaea_use_04.png) - 之后使用Docker命令构建Gaea的Docker镜像: ```bash docker build -t gaea:1.0.2 . ``` - 构建成功控制台输出: ![](../images/gaea_use_12.png) - 将本地安装的Gaea配置文件复制到`/mydata/gaea-docker/etc/`目录下: ```bash cp -r /mydata/gaea/etc/ /mydata/gaea-docker/etc/ ``` - 使用Docker命令启动Gaea容器: ```bash docker run -p 13306:13306 --name gaea \ -v /mydata/gaea-docker/etc:/go/gaea/etc \ -d gaea:1.0.2 ``` ## 测试读写分离 > 测试思路:首先我们关闭从实例的主从复制,然后通过Gaea代理来操作数据库,插入一条数据,如果主实例中有这条数据而从实例中没有,说明写操作是走的主库。然后再通过Gaea代理查询该表数据,如果没有这条数据,表示读操作走的是从库,证明读写分离成功。 - 通过Navicat连接到Gaea代理,注意此处账号密码为Gaea的namespace中配置的内容,端口为Gaea的服务端口; ![](../images/gaea_use_05.png) - 通过Navicat分别连接到主库和从库,用于查看数据,此时建立了以下三个数据库连接; ![](../images/gaea_use_06.png) - 通过`stop slave`命令关闭`mysql-slave`实例的主从复制功能: ![](../images/gaea_use_07.png) - 通过Gaea代理在`test`表中插入一条数据: ![](../images/gaea_use_08.png) - 在主库中查看`test`表的数据,发现已有该数据: ![](../images/gaea_use_09.png) - 在从库中查看`test`表的数据,发现没有该数据,证明写操作走的是主库: ![](../images/gaea_use_10.png) - 直接在代理中查看`test`表中的数据,发现没有该数据,证明读操作走的是从库。 ![](../images/gaea_use_11.png) ## 结合SpringBoot使用 在我们的SpringBoot应用中,我们只需要把Gaea的代理服务直接当做数据库服务来使用就可以实现读写分离了。这样就不用在代码中添加任何读写分离逻辑了,是不是很方便! ## 参考资料 更多资料请参考官方文档:https://github.com/XiaoMi/Gaea ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/gitlab.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 10分钟搭建自己的Git仓库 > GitLab是一款使用MIT许可证的基于网络的Git仓库管理工具,我们可以使用它来搭建自己的Git仓库,本文将介绍如何使用Gitlab在Linux下快速搭建Git仓库。 ## Gitlab服务端搭建 > 在Linux(CenterOS7.6)下我们会以Docker的方式来安装Gitlab,对Docker不了解的朋友可以参考:[开发者必备Docker命令](https://mp.weixin.qq.com/s/d_CuljDTJq680NTndAay8g)。 ### 下载Gitlab的Docker镜像 ```bash docker pull gitlab/gitlab-ce ``` ### 运行如下命令来启动Gitlab > 需要注意的是我们的Gitlab的http服务运行在宿主机的1080端口上,这里我们将Gitlab的配置,日志以及数据目录映射到了宿主机的指定文件夹下,防止我们在重新创建容器后丢失数据。 ```bash docker run --detach \ --publish 10443:443 --publish 1080:80 --publish 1022:22 \ --name gitlab \ --restart always \ --volume /mydata/gitlab/config:/etc/gitlab \ --volume /mydata/gitlab/logs:/var/log/gitlab \ --volume /mydata/gitlab/data:/var/opt/gitlab \ gitlab/gitlab-ce:latest ``` ### 开启防火墙的指定端口 > 由于Gitlab运行在1080端口上,所以我们需要开放该端口,注意千万不要直接关闭防火墙,否则Gitlab会无法启动。 ```bash # 开启1080端口 firewall-cmd --zone=public --add-port=1080/tcp --permanent # 重启防火墙才能生效 systemctl restart firewalld # 查看已经开放的端口 firewall-cmd --list-ports ``` ### 访问Gitlab - 访问地址:[http://192.168.3.101:1080/](http://192.168.3.101:1080/) - 由于Gitlab启动比较慢,需要耐心等待10分钟左右,如果Gitlab没有启动完成访问,会出现如下错误。 ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_04.png) - 可以通过docker命令动态查看容器启动日志来知道gitlab是否已经启动完成。 ```bash docker logs gitlab -f ``` ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_05.png) ## Gitlab的使用 ### Gitlab启动完成后第一次访问,会让你重置root帐号的密码 ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_06.png) ### 重置完成后输入帐号密码登录 ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_07.png) ### 选择创建项目、创建组织、创建帐号 ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_08.png) ### 创建组织 首先我们需要创建一个组织,然后在这个组织下分别创建用户和项目,这样同组织的用户就可以使用该组织下的项目了。 ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_09.png) ### 创建用户并修改密码 #### 找到添加用户的按钮 ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_10.png) #### 输入用户名密码添加用户 ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_11.png) #### 在编辑界面中修改用户密码 ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_12.png) ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_13.png) ### 创建项目并添加README文件 ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_14.png) ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_15.png) ### 将用户分配到组织 ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_16.png) ## Git客户端安装及使用 ### 下载Git客户端并安装 - 下载地址:https://github.com/git-for-windows/git/releases/download/v2.23.0.windows.1/Git-2.23.0-64-bit.exe - 下载完成后,一路点击Next安装即可。 ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_01.png) ### clone项目 - 找到项目clone的地址: ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_17.png) - 打开Git命令行工具: ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_18.png) - 执行以下命令clone项目到本地: ```bash git clone http://192.168.3.101:1080/macrozheng/hello.git ``` ### 提交代码 进入项目目录,修改一下README.md并提交: ```bash # 进入项目工程目录 cd hello/ # 将当前修改的文件添加到暂存区 git add . # 提交代码 git commit -m "first commit" ``` ### 推送到远程仓库 ```bash git push ``` ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_19.png) ### 拉取远程仓库代码 - 在Gitlab上修改readme中的文件内容: ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_20.png) - 拉取代码: ```bash git pull ``` ### 本地创建并提交分支 ```bash # 切换并从当前分支创建一个dev分支 git checkout -b dev # 将新创建的dev分支推送到远程仓库 git push origin dev ``` ![](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/blog/gitlab_screen_21.png) ### 其他常用命令 ```bash # 切换到dev分支 git checkout dev # 查看本地仓库文件状况 git status # 查看本地所有分支 git branch # 查看提交记录 git log ``` ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/gogs_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Github标星34K+Star,这款开源项目助你秒建Git服务! > 以前使用Gitlab搭建过Git服务,如果服务器配置低的话启动和访问都会特别慢。最近发现了Gogs这个开源项目,安装方便,特别轻量级,推荐给大家! ## 推荐语 一款极易搭建的自助Git服务,特别轻量级,推荐给大家! ## Gogs简介 Gogs是一款极易搭建的自助Git服务,使用Go语言开发,只要Go语言支持的平台它都支持,包括Linux、Mac OS X、Windows以及ARM平台。Gogs对系统硬件要求极低,你甚至可以在树莓派上搭建它。 项目地址:https://github.com/gogs/gogs ## 安装 > Gogs在Docker环境下的安装非常简单,只需要两个命令即可,推荐使用该方式来进行安装。 - 首先我们需要先下载Gogs的Docker镜像; ```bash docker pull gogs/gogs ``` - 下载完成后使用`docker run`命令即可运行服务; ```bash docker run -p 10022:22 -p 10080:3000 --name=gogs \ -v /mydata/gogs:/data \ -d gogs/gogs ``` - 这里我们说下命令中值得注意的地方,`10022`对应的是Gogs的SSH服务端口,`10080`对应的使用Gogs的HTTP服务端口,我们还将容器的数据目录挂载到了宿主机的`/mydata/gogs`目录下,这样就算我们重新创建容器数据也不会丢失。 ## 配置 - 安装完成后,我们第一次访问Gogs服务会显示一个设置页面,访问地址:http://192.168.5.19:10080/ - 数据库设置,这里我们直接使用内置的`SQLite3`数据库即可,使用其他的需要自行搭建数据库; ![](../images/gogs_start_01.png) - 应用基本设置,主要修改域名、SSH端口号和应用URL即可。 ![](../images/gogs_start_02.png) ## 使用 ### 注册 - 配置好以后会直接跳转到登录界面,首先注册一个帐户; ![](../images/gogs_start_03.png) - 注册完成后,登录即可进入控制面板页面。 ![](../images/gogs_start_04.png) ### 创建仓库 - 直接使用我的仓库右侧的加号即可创建仓库,简单设置下仓库名称和可见性来完成创建; ![](../images/gogs_start_05.png) - 创建成功后直接使用克隆地址即可克隆该仓库; ![](../images/gogs_start_06.png) - 我们可以直接使用IDEA的Git检出功能; ![](../images/gogs_start_07.png) - 检出完成后,加入我们的代码直接提交、推送,在Gogs里面就可以看到我们提交的代码了。 ![](../images/gogs_start_08.png) ### 工单管理 - 这里的工单管理,有点类似Github上面的Issue的功能,我们可以通过`创建工单`按钮来创建; ![](../images/gogs_start_09.png) - 输入我们的标题、内容和标签以后即可创建; ![](../images/gogs_start_10.png) - 创建完成后显示效果如下。 ![](../images/gogs_start_11.png) ### 添加用户 - 有时候管理员需要新建一些帐户来协作开发,此时我们只要点击`头像->管理面板->用户管理`即可打开用户管理界面; ![](../images/gogs_start_12.png) - 然后点击`创建新的帐户`,输入相关信息即可完成创建。 ![](../images/gogs_start_13.png) ### 管理协作者 - 创建完成后,我们可以在仓库页面的`仓库设置`中打开`管理协作者`功能; ![](../images/gogs_start_14.png) - 之后通过输入协作者账户,点击`增加新的协作者`并设置好权限即可,这样协作者就可以访问并向该仓库提交代码了。 ![](../images/gogs_start_15.png) ### 迁移外部仓库 - Gogs还提供了从外部仓库迁移代码的功能,通过头像左侧的加号,然后选择`迁移外部仓库`即可; ![](../images/gogs_start_16.png) - 这里以迁移Gitee上的`mall`项目为例,项目地址:http://gitee.com/macrozheng/mall ![](../images/gogs_start_17.png) - 迁移成功后就可以在Gogs里面看到`mall`项目了! ![](../images/gogs_start_18.png) ## Gogs VS Gitlab > 之前有写过一篇[《10分钟搭建自己的Git仓库》](https://mp.weixin.qq.com/s/6GyYlR9lpVcjgYmHMYLi0w),使用的是Gitlab,下面对比下Gogs和Gitlab在安装使用过程中的优缺点,仅代表个人观点。 | 比较方面 | Gogs | Gitlab | | -------------- | ------------------ | -------------------------- | | Docker镜像大小 | 44MB | 836MB | | 启动速度 | 很快,几秒 | 很慢,机器配置不好要10分钟 | | 配置要求 | 很低,树莓派都可以 | 很高,吃内存,吃CPU | | 访问速度 | 够快 | 机器配置好也还可以 | | 功能 | 功能较少 | 功能很丰富 | ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/harbor_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # DockerHub访问慢怎么破?自建个企业级镜像仓库试试! > 平时经常用Docker来部署各种环境,发现从DockerHub上下载镜像有时候比较慢。第三方的镜像还可以使用一些国内的镜像仓库来加速,如果我们自己构建的镜像那就不行了。这时候搭建一个私有的镜像仓库很有必要,最近发现Harbor这个企业级镜像仓库,非常好用且功能强大,推荐给大家! ## Harbor简介 Harbor是一款开源的Docker镜像仓库服务,在Github上目前有13.4k+Star。提供了基于角色的镜像访问机制,可以保护你的镜像安全。 ## 安装 > 学习开源项目的第一步,一般都是把它运行起来,我们先来把Harbor运行起来吧! - 下载Harbor安装包,这里下载的是`v1.10.6`离线版本,下载地址:https://github.com/goharbor/harbor/releases ![](../images/harbor_start_06.png) - 下载完成后上传到Linux服务器,使用如下命令解压; ```bash tar xvf harbor-offline-installer-v1.10.6.tgz ``` - 解压完成后,所有文件内容如下; ```bash [root@linux-local harbor]# ll total 700260 drwxr-xr-x. 3 root root 20 Dec 2 11:18 common -rw-r--r--. 1 root root 3398 Nov 17 11:58 common.sh -rw-r--r--. 1 root root 5348 Dec 2 14:41 docker-compose.yml -rw-r--r--. 1 root root 717021676 Nov 17 11:59 harbor.v1.10.6.tar.gz -rw-r--r--. 1 root root 5882 Dec 2 11:21 harbor.yml -rwxr-xr-x. 1 root root 2284 Nov 17 11:58 install.sh -rw-r--r--. 1 root root 11347 Nov 17 11:58 LICENSE -rwxr-xr-x. 1 root root 1749 Nov 17 11:58 prepare ``` - 修改Harbor的配置文件`harbor.yml`,修改`hostname`,并注释掉`https`配置,相关属性说明参考注释即可; ```yaml # 指定Harbor的管理界面及镜像仓库访问地址 hostname: 192.168.3.101 # http相关配置 http: # http端口,如果配置了https,默认使用https port: 80 # https相关配置 #https: # # https端口 # port: 443 # # 指定Habor中Nginx的https的证书和私钥地址 # certificate: /your/certificate/path # private_key: /your/private/key/path # Harbor默认管理员账号admin的密码 harbor_admin_password: Harbor12345 # Harbor内置PostgreSQL数据库配置 database: # root用户密码 password: root123 # 最大空闲连接数,小于等于0表示无空闲连接 max_idle_conns: 50 # 最大连接数,小于等于0表示无限制 max_open_conns: 100 # 默认数据目录 data_volume: /data # Clair configuration clair: # The interval of clair updaters, the unit is hour, set to 0 to disable the updaters. updaters_interval: 12 jobservice: # Maximum number of job workers in job service max_job_workers: 10 notification: # Maximum retry count for webhook job webhook_job_max_retry: 10 chart: # Change the value of absolute_url to enabled can enable absolute url in chart absolute_url: disabled # 日志配置 log: # 日志级别配置: debug, info, warning, error, fatal level: info # 日志本地存储策略 local: # 日志文件滚动数量,超过该数量会删除日志文件 rotate_count: 50 # 日志滚动大小,超过该大小会生成新的日志文件 rotate_size: 200M # 日志存储路径 location: /var/log/harbor # This attribute is for migrator to detect the version of the .cfg file, DO NOT MODIFY! _version: 1.10.0 # Configure proxies to be used by Clair, the replication jobservice, and Harbor. Leave blank if no proxies are required. proxy: http_proxy: https_proxy: # no_proxy endpoints will appended to 127.0.0.1,localhost,.local,.internal,log,db,redis,nginx,core,portal,postgresql,jobservice,registry,registryctl,clair,chartmuseum,notary-server no_proxy: components: - core - jobservice - clair ``` - 使用`install.sh`脚本安装Harbor: ```bash ./install.sh ``` - Harbor启动成功后会输出如下信息,这里需要注意的是Harbor会启动Nginx、Redis之类的容器,以前创建过的需要先删除掉,看到`started successfully`就表示启动成功了; ```bash [Step 0]: checking if docker is installed ... Note: docker version: 19.03.5 [Step 1]: checking docker-compose is installed ... Note: docker-compose version: 1.24.0 [Step 2]: loading Harbor images ... Loaded image: goharbor/harbor-migrator:v1.10.6 Loaded image: goharbor/harbor-core:v1.10.6 Loaded image: goharbor/harbor-db:v1.10.6 Loaded image: goharbor/harbor-registryctl:v1.10.6 Loaded image: goharbor/nginx-photon:v1.10.6 Loaded image: goharbor/clair-photon:v1.10.6 Loaded image: goharbor/clair-adapter-photon:v1.10.6 Loaded image: goharbor/harbor-portal:v1.10.6 Loaded image: goharbor/harbor-log:v1.10.6 Loaded image: goharbor/registry-photon:v1.10.6 Loaded image: goharbor/notary-signer-photon:v1.10.6 Loaded image: goharbor/harbor-jobservice:v1.10.6 Loaded image: goharbor/redis-photon:v1.10.6 Loaded image: goharbor/prepare:v1.10.6 Loaded image: goharbor/notary-server-photon:v1.10.6 Loaded image: goharbor/chartmuseum-photon:v1.10.6 [Step 3]: preparing environment ... [Step 4]: preparing harbor configs ... prepare base dir is set to /mydata/harbor/harbor WARNING:root:WARNING: HTTP protocol is insecure. Harbor will deprecate http protocol in the future. Please make sure to upgrade to https Clearing the configuration file: /config/log/logrotate.conf Clearing the configuration file: /config/log/rsyslog_docker.conf Clearing the configuration file: /config/nginx/nginx.conf Clearing the configuration file: /config/core/env Clearing the configuration file: /config/core/app.conf Clearing the configuration file: /config/registry/config.yml Clearing the configuration file: /config/registry/root.crt Clearing the configuration file: /config/registryctl/env Clearing the configuration file: /config/registryctl/config.yml Clearing the configuration file: /config/db/env Clearing the configuration file: /config/jobservice/env Clearing the configuration file: /config/jobservice/config.yml Generated configuration file: /config/log/logrotate.conf Generated configuration file: /config/log/rsyslog_docker.conf Generated configuration file: /config/nginx/nginx.conf Generated configuration file: /config/core/env Generated configuration file: /config/core/app.conf Generated configuration file: /config/registry/config.yml Generated configuration file: /config/registryctl/env Generated configuration file: /config/db/env Generated configuration file: /config/jobservice/env Generated configuration file: /config/jobservice/config.yml loaded secret from file: /secret/keys/secretkey Generated configuration file: /compose_location/docker-compose.yml Clean up the input dir Note: stopping existing Harbor instance ... Stopping harbor-jobservice ... done Stopping harbor-core ... done Stopping redis ... done Stopping registryctl ... done Stopping registry ... done Stopping harbor-db ... done Stopping harbor-portal ... done Stopping harbor-log ... done Removing harbor-jobservice ... done Removing harbor-core ... done Removing redis ... done Removing registryctl ... done Removing registry ... done Removing harbor-db ... done Removing harbor-portal ... done Removing harbor-log ... done Removing network harbor_harbor [Step 5]: starting Harbor ... Creating network "harbor_harbor" with the default driver Creating harbor-log ... done Creating harbor-portal ... done Creating registry ... done Creating harbor-db ... done Creating registryctl ... done Creating redis ... done Creating harbor-core ... done Creating harbor-jobservice ... done Creating nginx ... done ✔ ----Harbor has been installed and started successfully.---- ``` - 我们可以使用`docker images`命令查看下安装Harbor安装的Docker镜像,还挺多的; ```bash REPOSITORY TAG IMAGE ID CREATED SIZE latest dc3bacd8b5ea 8 days ago 1.23MB goharbor/chartmuseum-photon v1.10.6 01b70eccaf71 2 weeks ago 178MB goharbor/harbor-migrator v1.10.6 a5d4a4ee44e4 2 weeks ago 356MB goharbor/redis-photon v1.10.6 99e25b65195c 2 weeks ago 132MB goharbor/clair-adapter-photon v1.10.6 aa72598ecc12 2 weeks ago 61.3MB goharbor/clair-photon v1.10.6 da1b03030e34 2 weeks ago 171MB goharbor/notary-server-photon v1.10.6 37c8bed3e255 2 weeks ago 142MB goharbor/notary-signer-photon v1.10.6 c56d82220929 2 weeks ago 139MB goharbor/harbor-registryctl v1.10.6 1d3986d90c65 2 weeks ago 101MB goharbor/registry-photon v1.10.6 3e669c8204ed 2 weeks ago 83.7MB goharbor/nginx-photon v1.10.6 a39d8dd46060 2 weeks ago 43.7MB goharbor/harbor-log v1.10.6 1085d3865a57 2 weeks ago 106MB goharbor/harbor-jobservice v1.10.6 aa05538acecf 2 weeks ago 143MB goharbor/harbor-core v1.10.6 193e76e6be5d 2 weeks ago 129MB goharbor/harbor-portal v1.10.6 942a9c448850 2 weeks ago 51.8MB goharbor/harbor-db v1.10.6 37da2e5414ae 2 weeks ago 170MB goharbor/prepare v1.10.6 35f073e33ec5 2 weeks ago 177MB ``` - 访问Harbor的管理界面,输入账号密码`admin:Harbor12345`登录即可,访问地址:http://192.168.3.101/ ![](../images/harbor_start_01.png) ## 使用 > 接下来我们就可以使用Harbor来管理我们的镜像了。 - 首先点击`新建项目`按钮,新建一个项目: ![](../images/harbor_start_02.png) - 这里新建一个叫做`test`的私有项目; ![](../images/harbor_start_03.png) - 由于`docker login`命令默认不支持http访问,所以我们需要手动开启,使用Vim编辑器修改docker的配置文件`daemon.json`; ```bash vi /etc/docker/daemon.json ``` - 添加一行`insecure-registries`配置即可,允许使用非安全方式访问Harbor镜像仓库,注意不要少了端口号`80`; ```json { "registry-mirrors":["https://xxx.aliyuncs.com"], "insecure-registries":["192.168.3.101:80"] } ``` - 再次重新启动docker服务; ```bash systemctl restart docker ``` - 再次使用`install.sh`启动Harbor服务; ```bash ./install.sh ``` - 使用`docker login`命令访问Harbor镜像仓库,注意加上端口号为`80`; ```bash [root@linux-local harbor]# docker login 192.168.3.101:80 Username: admin Password: WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded ``` - 编写Dockerfile脚本,用于构建Docker镜像,一个最简单的busybox脚本如下; ```dockerfile FROM busybox:latest ``` - 使用如下命令构建一个自己的busybox镜像; ```bash docker build -t 192.168.3.101:80/test/busybox . ``` - 将自己构建的busybox镜像推送到Harbor镜像仓库; ```bash docker push 192.168.3.101:80/test/busybox ``` - 推送成功后在Harbor的管理界面中就可以查看到busybox镜像了; ![](../images/harbor_start_04.png) - 由于Harbor是用Docker Compose部署的,可以直接使用Docker Compose的命令来停止和启动。 ```bash # 停止Harbor docker-compose stop # 启动Harbor docker-compose start ``` ## 结合SpringBoot使用 > 这里使用之前的`mall-tiny-fabric`项目来演示下,如何使用Maven插件一键打包并推送到Harbor镜像仓库。 - 首先修改项目的`pom.xml`文件,修改推送的`镜像仓库地址`,并添加`认证信息`即可; ```xml io.fabric8 docker-maven-plugin 0.33.0 build-image package build http://192.168.3.101:2375 http://192.168.3.101:80 admin Harbor12345 192.168.3.101:80/mall-tiny/${project.name}:${project.version} java:8 ${project.build.finalName}.jar / artifact ["java", "-jar","/${project.build.finalName}.jar"] macrozheng ${project.artifactId} 8080:8080 mysql:db /etc/localtime:/etc/localtime /mydata/app/${project.artifactId}/logs:/var/logs ``` - 推送镜像之前需要在Harbor中创建好`mall-tiny`项目,否则会无法推送镜像; ![](../images/harbor_start_07.png) - 之后使用Maven插件打包镜像并推送到Harbor仓库,具体可以参考[《还在手动部署SpringBoot应用?试试这个自动化插件!》](https://mp.weixin.qq.com/s/3X6vVdWmjmWCyiLm35jpVw),推送过程中输出信息如下; ```bash [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building mall-tiny-fabric 0.0.1-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- docker-maven-plugin:0.33.0:push (default-cli) @ mall-tiny-fabric --- [INFO] DOCKER> The push refers to repository [192.168.3.101:80/mall-tiny/mall-tiny-fabric] ############### [INFO] DOCKER> 0.0.1-SNAPSHOT: digest: sha256:3a54682fd3b04526f6da0916e98f3d0d5ba4193a8ad6aafbe6c05a1badf6c13b size: 2212 [INFO] DOCKER> Temporary image tag skipped. Target image '192.168.3.101:80/mall-tiny/mall-tiny-fabric:0.0.1-SNAPSHOT' already has registry set or no registry is available [INFO] DOCKER> Pushed 192.168.3.101:80/mall-tiny/mall-tiny-fabric:0.0.1-SNAPSHOT in 2 minutes and 8 seconds [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 02:11 min [INFO] Finished at: 2020-12-02T15:11:10+08:00 [INFO] Final Memory: 19M/219M [INFO] ------------------------------------------------------------------------ Process finished with exit code 0 ``` - 打开Harbor管理页面,发现`mall-tiny-fabric`镜像已经存在了。 ![](../images/harbor_start_05.png) ## 总结 Harbor提供了管理界面让我们可以更方便地管理Docker镜像,同时添加了基于角色的权限管理功能来保护镜像的安全。之前我们为了安全地使用镜像,需要使用繁琐的TLS来控制远程Docker服务打包镜像,具体参考[《Docker服务开放了这个端口,服务器分分钟变肉机!》](https://mp.weixin.qq.com/s/3d5IlLcyklVsGDOqeipIYw)。现在我们只要搭建一个Harbor镜像仓库,然后本地打包好镜像上传到Harbor,需要使用镜像的时候直接从Harbor下载即可! ## 参考资料 官方文档:https://goharbor.io/docs/2.1.0/install-config/ ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-fabric ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/hutool.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Hutool中那些常用的工具类和方法 > Hutool是一个Java工具包,它帮助我们简化每一行代码,避免重复造轮子。如果你有需要用到某些工具方法的时候,不妨在Hutool里面找找,可能就有。本文将对Hutool中的常用工具类和方法进行介绍。 ## 安装 maven项目在pom.xml添加以下依赖即可: ```xml cn.hutool hutool-all 4.6.3 ``` ## 常用工具类 ### Convert 类型转换工具类,用于各种类型数据的转换。 ```java //转换为字符串 int a = 1; String aStr = Convert.toStr(a); //转换为指定类型数组 String[] b = {"1", "2", "3", "4"}; Integer[] bArr = Convert.toIntArray(b); //转换为日期对象 String dateStr = "2017-05-06"; Date date = Convert.toDate(dateStr); //转换为列表 String[] strArr = {"a", "b", "c", "d"}; List strList = Convert.toList(String.class, strArr); ``` ### DateUtil 日期时间工具类,定义了一些常用的日期时间操作方法。 ```java //Date、long、Calendar之间的相互转换 //当前时间 Date date = DateUtil.date(); //Calendar转Date date = DateUtil.date(Calendar.getInstance()); //时间戳转Date date = DateUtil.date(System.currentTimeMillis()); //自动识别格式转换 String dateStr = "2017-03-01"; date = DateUtil.parse(dateStr); //自定义格式化转换 date = DateUtil.parse(dateStr, "yyyy-MM-dd"); //格式化输出日期 String format = DateUtil.format(date, "yyyy-MM-dd"); //获得年的部分 int year = DateUtil.year(date); //获得月份,从0开始计数 int month = DateUtil.month(date); //获取某天的开始、结束时间 Date beginOfDay = DateUtil.beginOfDay(date); Date endOfDay = DateUtil.endOfDay(date); //计算偏移后的日期时间 Date newDate = DateUtil.offset(date, DateField.DAY_OF_MONTH, 2); //计算日期时间之间的偏移量 long betweenDay = DateUtil.between(date, newDate, DateUnit.DAY); ``` ### StrUtil 字符串工具类,定义了一些常用的字符串操作方法。 ```java //判断是否为空字符串 String str = "test"; StrUtil.isEmpty(str); StrUtil.isNotEmpty(str); //去除字符串的前后缀 StrUtil.removeSuffix("a.jpg", ".jpg"); StrUtil.removePrefix("a.jpg", "a."); //格式化字符串 String template = "这只是个占位符:{}"; String str2 = StrUtil.format(template, "我是占位符"); LOGGER.info("/strUtil format:{}", str2); ``` ### ClassPathResource 获取classPath下的文件,在Tomcat等容器下,classPath一般是WEB-INF/classes。 ```java //获取定义在src/main/resources文件夹中的配置文件 ClassPathResource resource = new ClassPathResource("generator.properties"); Properties properties = new Properties(); properties.load(resource.getStream()); LOGGER.info("/classPath:{}", properties); ``` ### ReflectUtil Java反射工具类,可用于反射获取类的方法及创建对象。 ```java //获取某个类的所有方法 Method[] methods = ReflectUtil.getMethods(PmsBrand.class); //获取某个类的指定方法 Method method = ReflectUtil.getMethod(PmsBrand.class, "getId"); //使用反射来创建对象 PmsBrand pmsBrand = ReflectUtil.newInstance(PmsBrand.class); //反射执行对象的方法 ReflectUtil.invoke(pmsBrand, "setId", 1); ``` ### NumberUtil 数字处理工具类,可用于各种类型数字的加减乘除操作及判断类型。 ```java double n1 = 1.234; double n2 = 1.234; double result; //对float、double、BigDecimal做加减乘除操作 result = NumberUtil.add(n1, n2); result = NumberUtil.sub(n1, n2); result = NumberUtil.mul(n1, n2); result = NumberUtil.div(n1, n2); //保留两位小数 BigDecimal roundNum = NumberUtil.round(n1, 2); String n3 = "1.234"; //判断是否为数字、整数、浮点数 NumberUtil.isNumber(n3); NumberUtil.isInteger(n3); NumberUtil.isDouble(n3); ``` ### BeanUtil JavaBean的工具类,可用于Map与JavaBean对象的互相转换以及对象属性的拷贝。 ```java PmsBrand brand = new PmsBrand(); brand.setId(1L); brand.setName("小米"); brand.setShowStatus(0); //Bean转Map Map map = BeanUtil.beanToMap(brand); LOGGER.info("beanUtil bean to map:{}", map); //Map转Bean PmsBrand mapBrand = BeanUtil.mapToBean(map, PmsBrand.class, false); LOGGER.info("beanUtil map to bean:{}", mapBrand); //Bean属性拷贝 PmsBrand copyBrand = new PmsBrand(); BeanUtil.copyProperties(brand, copyBrand); LOGGER.info("beanUtil copy properties:{}", copyBrand); ``` ### CollUtil 集合操作的工具类,定义了一些常用的集合操作。 ```java //数组转换为列表 String[] array = new String[]{"a", "b", "c", "d", "e"}; List list = CollUtil.newArrayList(array); //join:数组转字符串时添加连接符号 String joinStr = CollUtil.join(list, ","); LOGGER.info("collUtil join:{}", joinStr); //将以连接符号分隔的字符串再转换为列表 List splitList = StrUtil.split(joinStr, ','); LOGGER.info("collUtil split:{}", splitList); //创建新的Map、Set、List HashMap newMap = CollUtil.newHashMap(); HashSet newHashSet = CollUtil.newHashSet(); ArrayList newList = CollUtil.newArrayList(); //判断列表是否为空 CollUtil.isEmpty(list); ``` ### MapUtil Map操作工具类,可用于创建Map对象及判断Map是否为空。 ```java //将多个键值对加入到Map中 Map map = MapUtil.of(new String[][]{ {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"} }); //判断Map是否为空 MapUtil.isEmpty(map); MapUtil.isNotEmpty(map); ``` ### AnnotationUtil 注解工具类,可用于获取注解与注解中指定的值。 ```java //获取指定类、方法、字段、构造器上的注解列表 Annotation[] annotationList = AnnotationUtil.getAnnotations(HutoolController.class, false); LOGGER.info("annotationUtil annotations:{}", annotationList); //获取指定类型注解 Api api = AnnotationUtil.getAnnotation(HutoolController.class, Api.class); LOGGER.info("annotationUtil api value:{}", api.description()); //获取指定类型注解的值 Object annotationValue = AnnotationUtil.getAnnotationValue(HutoolController.class, RequestMapping.class); ``` ### SecureUtil 加密解密工具类,可用于MD5加密。 ```java //MD5加密 String str = "123456"; String md5Str = SecureUtil.md5(str); LOGGER.info("secureUtil md5:{}", md5Str); ``` ### CaptchaUtil 验证码工具类,可用于生成图形验证码。 ```java //生成验证码图片 LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100); try { request.getSession().setAttribute("CAPTCHA_KEY", lineCaptcha.getCode()); response.setContentType("image/png");//告诉浏览器输出内容为图片 response.setHeader("Pragma", "No-cache");//禁止浏览器缓存 response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expire", 0); lineCaptcha.write(response.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } ``` ### 其他工具类 Hutool中的工具类很多,可以参考:[https://www.hutool.cn/](https://www.hutool.cn/) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-hutool](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-hutool) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/hutool_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 不要再重复造轮子了,这款开源工具类库贼好使! > Hutool是一个小而全的Java工具类库,它帮助我们简化每一行代码,避免重复造轮子。如果你有需要用到某些工具类的时候,不妨在Hutool里面找找。本文总结了平时常用的16个工具类,希望对大家有所帮助! ## 安装 Hutool的安装非常简单,Maven项目中只需在`pom.xml`添加以下依赖即可。 ```xml cn.hutool hutool-all 5.4.0 ``` ## 常用工具类 > 使用一个工具方法代替一段复杂代码,避免`复制粘贴`代码,可以极大的提高我们的开发效率,下面介绍下我常用的工具方法! ### Convert 类型转换工具类,用于各种类型数据的转换。平时我们转换类型经常会面临类型转换失败的问题,要写`try catch`代码,有了它,就不用写了! ```java //转换为字符串 int a = 1; String aStr = Convert.toStr(a); //转换为指定类型数组 String[] b = {"1", "2", "3", "4"}; Integer[] bArr = Convert.toIntArray(b); //转换为日期对象 String dateStr = "2017-05-06"; Date date = Convert.toDate(dateStr); //转换为列表 String[] strArr = {"a", "b", "c", "d"}; List strList = Convert.toList(String.class, strArr); ``` ### DateUtil 日期时间工具类,定义了一些常用的日期时间操作方法。JDK自带的Date和Calendar对象真心不好用,有了它操作日期时间就简单多了! ```java //Date、long、Calendar之间的相互转换 //当前时间 Date date = DateUtil.date(); //Calendar转Date date = DateUtil.date(Calendar.getInstance()); //时间戳转Date date = DateUtil.date(System.currentTimeMillis()); //自动识别格式转换 String dateStr = "2017-03-01"; date = DateUtil.parse(dateStr); //自定义格式化转换 date = DateUtil.parse(dateStr, "yyyy-MM-dd"); //格式化输出日期 String format = DateUtil.format(date, "yyyy-MM-dd"); //获得年的部分 int year = DateUtil.year(date); //获得月份,从0开始计数 int month = DateUtil.month(date); //获取某天的开始、结束时间 Date beginOfDay = DateUtil.beginOfDay(date); Date endOfDay = DateUtil.endOfDay(date); //计算偏移后的日期时间 Date newDate = DateUtil.offset(date, DateField.DAY_OF_MONTH, 2); //计算日期时间之间的偏移量 long betweenDay = DateUtil.between(date, newDate, DateUnit.DAY); ``` ### JSONUtil JSON解析工具类,可用于对象与JSON之间的互相转化。 ```java PmsBrand brand = new PmsBrand(); brand.setId(1L); brand.setName("小米"); brand.setShowStatus(1); //对象转化为JSON字符串 String jsonStr = JSONUtil.parse(brand).toString(); LOGGER.info("jsonUtil parse:{}", jsonStr); //JSON字符串转化为对象 PmsBrand brandBean = JSONUtil.toBean(jsonStr, PmsBrand.class); LOGGER.info("jsonUtil toBean:{}", brandBean); List brandList = new ArrayList<>(); brandList.add(brand); String jsonListStr = JSONUtil.parse(brandList).toString(); //JSON字符串转化为列表 brandList = JSONUtil.toList(new JSONArray(jsonListStr), PmsBrand.class); LOGGER.info("jsonUtil toList:{}", brandList); ``` ### StrUtil 字符串工具类,定义了一些常用的字符串操作方法。`StrUtil`比`StringUtil`名称更短,用起来也更方便! ```java //判断是否为空字符串 String str = "test"; StrUtil.isEmpty(str); StrUtil.isNotEmpty(str); //去除字符串的前后缀 StrUtil.removeSuffix("a.jpg", ".jpg"); StrUtil.removePrefix("a.jpg", "a."); //格式化字符串 String template = "这只是个占位符:{}"; String str2 = StrUtil.format(template, "我是占位符"); LOGGER.info("/strUtil format:{}", str2); ``` ### ClassPathResource ClassPath单一资源访问类,可以获取classPath下的文件,在Tomcat等容器下,classPath一般是WEB-INF/classes。 ```java //获取定义在src/main/resources文件夹中的配置文件 ClassPathResource resource = new ClassPathResource("generator.properties"); Properties properties = new Properties(); properties.load(resource.getStream()); LOGGER.info("/classPath:{}", properties); ``` ### ReflectUtil Java反射工具类,可用于反射获取类的方法及创建对象。 ```java //获取某个类的所有方法 Method[] methods = ReflectUtil.getMethods(PmsBrand.class); //获取某个类的指定方法 Method method = ReflectUtil.getMethod(PmsBrand.class, "getId"); //使用反射来创建对象 PmsBrand pmsBrand = ReflectUtil.newInstance(PmsBrand.class); //反射执行对象的方法 ReflectUtil.invoke(pmsBrand, "setId", 1); ``` ### NumberUtil 数字处理工具类,可用于各种类型数字的加减乘除操作及类型判断。 ```java double n1 = 1.234; double n2 = 1.234; double result; //对float、double、BigDecimal做加减乘除操作 result = NumberUtil.add(n1, n2); result = NumberUtil.sub(n1, n2); result = NumberUtil.mul(n1, n2); result = NumberUtil.div(n1, n2); //保留两位小数 BigDecimal roundNum = NumberUtil.round(n1, 2); String n3 = "1.234"; //判断是否为数字、整数、浮点数 NumberUtil.isNumber(n3); NumberUtil.isInteger(n3); NumberUtil.isDouble(n3); ``` ### BeanUtil JavaBean工具类,可用于Map与JavaBean对象的互相转换以及对象属性的拷贝。 ```java PmsBrand brand = new PmsBrand(); brand.setId(1L); brand.setName("小米"); brand.setShowStatus(0); //Bean转Map Map map = BeanUtil.beanToMap(brand); LOGGER.info("beanUtil bean to map:{}", map); //Map转Bean PmsBrand mapBrand = BeanUtil.mapToBean(map, PmsBrand.class, false); LOGGER.info("beanUtil map to bean:{}", mapBrand); //Bean属性拷贝 PmsBrand copyBrand = new PmsBrand(); BeanUtil.copyProperties(brand, copyBrand); LOGGER.info("beanUtil copy properties:{}", copyBrand); ``` ### CollUtil 集合操作的工具类,定义了一些常用的集合操作。 ```java //数组转换为列表 String[] array = new String[]{"a", "b", "c", "d", "e"}; List list = CollUtil.newArrayList(array); //join:数组转字符串时添加连接符号 String joinStr = CollUtil.join(list, ","); LOGGER.info("collUtil join:{}", joinStr); //将以连接符号分隔的字符串再转换为列表 List splitList = StrUtil.split(joinStr, ','); LOGGER.info("collUtil split:{}", splitList); //创建新的Map、Set、List HashMap newMap = CollUtil.newHashMap(); HashSet newHashSet = CollUtil.newHashSet(); ArrayList newList = CollUtil.newArrayList(); //判断列表是否为空 CollUtil.isEmpty(list); ``` ### MapUtil Map操作工具类,可用于创建Map对象及判断Map是否为空。 ```java //将多个键值对加入到Map中 Map map = MapUtil.of(new String[][]{ {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"} }); //判断Map是否为空 MapUtil.isEmpty(map); MapUtil.isNotEmpty(map); ``` ### AnnotationUtil 注解工具类,可用于获取注解与注解中指定的值。 ```java //获取指定类、方法、字段、构造器上的注解列表 Annotation[] annotationList = AnnotationUtil.getAnnotations(HutoolController.class, false); LOGGER.info("annotationUtil annotations:{}", annotationList); //获取指定类型注解 Api api = AnnotationUtil.getAnnotation(HutoolController.class, Api.class); LOGGER.info("annotationUtil api value:{}", api.description()); //获取指定类型注解的值 Object annotationValue = AnnotationUtil.getAnnotationValue(HutoolController.class, RequestMapping.class); ``` ### SecureUtil 加密解密工具类,可用于MD5加密。 ```java //MD5加密 String str = "123456"; String md5Str = SecureUtil.md5(str); LOGGER.info("secureUtil md5:{}", md5Str); ``` ### CaptchaUtil 验证码工具类,可用于生成图形验证码。 ```java //生成验证码图片 LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100); try { request.getSession().setAttribute("CAPTCHA_KEY", lineCaptcha.getCode()); response.setContentType("image/png");//告诉浏览器输出内容为图片 response.setHeader("Pragma", "No-cache");//禁止浏览器缓存 response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expire", 0); lineCaptcha.write(response.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } ``` ### Validator 字段验证器,可以对不同格式的字符串进行验证,比如邮箱、手机号、IP等格式。 ```java //判断是否为邮箱地址 boolean result = Validator.isEmail("macro@qq.com"); LOGGER.info("Validator isEmail:{}", result); //判断是否为手机号码 result = Validator.isMobile("18911111111"); LOGGER.info("Validator isMobile:{}", result); //判断是否为IPV4地址 result = Validator.isIpv4("192.168.3.101"); LOGGER.info("Validator isIpv4:{}", result); //判断是否为汉字 result = Validator.isChinese("你好"); LOGGER.info("Validator isChinese:{}", result); //判断是否为身份证号码(18位中国) result = Validator.isCitizenId("123456"); LOGGER.info("Validator isCitizenId:{}", result); //判断是否为URL result = Validator.isUrl("http://www.baidu.com"); LOGGER.info("Validator isUrl:{}", result); //判断是否为生日 result = Validator.isBirthday("2020-02-01"); LOGGER.info("Validator isBirthday:{}", result); ``` ### DigestUtil 摘要算法工具类,支持MD5、SHA-256、Bcrypt等算法。 ```java String password = "123456"; //计算MD5摘要值,并转为16进制字符串 String result = DigestUtil.md5Hex(password); LOGGER.info("DigestUtil md5Hex:{}", result); //计算SHA-256摘要值,并转为16进制字符串 result = DigestUtil.sha256Hex(password); LOGGER.info("DigestUtil sha256Hex:{}", result); //生成Bcrypt加密后的密文,并校验 String hashPwd = DigestUtil.bcrypt(password); boolean check = DigestUtil.bcryptCheck(password,hashPwd); LOGGER.info("DigestUtil bcryptCheck:{}", check); ``` ### HttpUtil Http请求工具类,可以发起GET/POST等请求。 ```java String response = HttpUtil.get("http://localhost:8080/hutool/covert"); LOGGER.info("HttpUtil get:{}", response); ``` ### 其他工具类 Hutool中的工具类还有很多,可以参考:https://www.hutool.cn/ ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-hutool ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/idea.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # IDEA常用设置及推荐插件 >本文主要记录IDEA的一些常用设置,IDEA与Eclipse的常用快捷键对比及推荐一些好用的插件。 ## 基本设置 ### 设置界面风格及修改外部UI尺寸大小 ![](../images/idea_screen_1.png) ### 打开IDEA时设置不重新打开最近的项目 > IDEA默认会打开最近的项目,有时候我们需要自己选择要打开的项目,不勾选该选项可以实现。 ![](../images/idea_screen_2.png) ### 设置IDEA的快捷键 ![](../images/idea_screen_3.png) ### 设置代码字体大小 ![](../images/idea_screen_4.png) ### 设置项目文件编码格式 ![](../images/idea_screen_5.png) ### 设置代码提示的匹配模式 ![](../images/idea_screen_6.png) ### 设置新建类文件的类注释模版 ![](../images/idea_screen_7.png) ## IDEA和Eclipse常用快捷键对比 > 友情提示:IDEA可以设置为Eclipse风格的快捷键,在File->Settings->Keymap处,如需更改部分快捷键可按如下表格中的英文描述进行搜索,并改为相应快捷键。 | Eclipse | IDEA | 英文描述 | 中文描述 | | ------------- | ------------------- | ----------------------------- | ----------------------------------- | | ctrl+shift+r | ctrl+shift+n | Navigate->File | 找工作空间的文件 | | ctrl+shift+t | ctrl+n | Navigate->Class | 找类定义 | | ctrl+shift+g | alt+f7 | Edit->Find->Find Usages | 查找方法在哪里调用.变量在哪里被使用 | | ctrl+t | ctrl+t | Other->Hierarchy Class | 看类继承结构 | | ctrl+o | ctrl+f12 | Navigate->File Structure | 搜索一个类里面的方法 | | shift+alt+z | ctrl+alt+t | Code->Surround With | 生成常见的代码块 | | shift+alt+l | ctrl+alt+v | Refactor->Extract->Variable | 抽取变量 | | shift+alt+m | ctrl+alt+m | Refactor->Extract->Method | 抽取方法 | | alt+左箭头 | ctrl+alt+左箭头 | Navigate->Back | 回退上一个操作位置 | | alt+右箭头 | ctrl+alt+右键头 | Navigate->Forward | 前进上一个操作位置 | | ctrl+home | ctrl+home | Move Caret to Text Start | 回到类最前面 | | ctrl+end | ctrl+end | Move Caret to Text End | 回到类最后面 | | ctrl+e | ctrl+e | View->Recent Files | 最近打开的文件 | | alt+/ | ctrl+space | Code->Completion->Basic | 提示变量生成 | | ctrl+1 | alt+enter | Other->Show Intention Actions | 提示可能的操作 | | ctrl+h | ctrl+shift+f | Find in Path | 全局搜索 | | alt+上/下箭头 | alt+shift+上/下箭头 | Code->Move Line Up/Down | 移动一行代码 | | ctrl+/ | ctrl+/ | Other->Fix doc comment | 方法注释 | | ctrl+alt+s | alt+insert | Generate | 生成getter,setter,tostring等 | ## 推荐插件 > 由于IDEA本身就自带很多插件,可以完成大部分需求,这里就推荐两个本人常用的插件。 ### Free MyBatis plugin > 非常好用的MyBatis插件,对MyBatis的xml具有强大的提示功能,同时可以关联mapper接口和mapper.xml中的sql实现。 #### 可以从mapper接口和mapper.xml文件中相互跳转 ![](../images/idea_screen_8.png) #### mapper.xml中的各种提示 ![](../images/idea_screen_9.png) ![](../images/idea_screen_10.png) ### Lombok plugin > Lombok为Java语言添加了非常有趣的附加功能,你可以不用再为实体类手写getter,setter等方法,通过一个注解即可拥有。 一个没有getter,setter方法的类通过添加@Getter和@Setter注解拥有了getter,setter方法。 ![](../images/idea_screen_11.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/idea_git.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # IDEA中的Git操作,看这一篇就够了! > 大家在使用Git时,都会选择一种Git客户端,在IDEA中内置了这种客户端,可以让你不需要使用Git命令就可以方便地进行操作,本文将讲述IDEA中的一些常用Git操作。 ## 环境准备 - 使用前需要安装一个远程的Git仓库和本地的Git客户端,具体参考:[10分钟搭建自己的Git仓库](https://mp.weixin.qq.com/s/6GyYlR9lpVcjgYmHMYLi0w)。 - 由于IDEA中的Git插件需要依赖本地Git客户端,所以需要进行如下配置: ![](../images/gitlab_screen_02.png) ## 操作流程 > 我们这里使用mall-tiny项目的源代码来演示,尽可能还原一个正式的操作流程。 ### 在Gitlab中创建一个项目并添加README文件 ![](../images/gitlab_screen_22.png) ### clone项目到本地 - 打开从Git检出项目的界面: ![](../images/gitlab_screen_23.png) - 输入Git地址进行检出: ![](../images/gitlab_screen_24.png) - 暂时不生成IDEA项目,因为项目还没初始化: ![](../images/gitlab_screen_25.png) ### 初始化项目并提交代码 - 将mall-tiny的代码复制到该目录中: ![](../images/gitlab_screen_26.png) - 这里我们需要一个.gitignore文件来防止一些IDEA自动生成的代码被提交到Git仓库去: ``` # Maven # target/ # IDEA # .idea/ *.iml # Eclipse # .settings/ .classpath .project ``` - 使用IDEA打开项目: ![](../images/gitlab_screen_27.png) - 右键项目打开菜单,将所有文件添加到暂存区中: ![](../images/gitlab_screen_28.png) - 添加注释并提交代码: ![](../images/gitlab_screen_29.png) ### 将代码推送到远程仓库 - 点击push按钮推送代码: ![](../images/gitlab_screen_30.png) - 确认推送内容: ![](../images/gitlab_screen_31.png) - 查看远程仓库发现已经提交完成: ![](../images/gitlab_screen_32.png) ### 从远程仓库拉取代码 - 在远程仓库添加一个README-TEST.md文件: ![](../images/gitlab_screen_33.png) - 从远程仓库拉取代码: ![](../images/gitlab_screen_34.png) - 确认拉取分支信息: ![](../images/gitlab_screen_35.png) ### 从本地创建分支并推送到远程 - 在本地创建dev分支,点击右下角的Git:master按钮: ![](../images/gitlab_screen_36.png) - 使用push将本地dev分支推送到远程: ![](../images/gitlab_screen_30.png) - 确认推送内容: ![](../images/gitlab_screen_37.png) - 查看远程仓库发现已经创建了dev分支: ![](../images/gitlab_screen_38.png) ### 分支切换 - 从dev分支切换回master分支: ![](../images/gitlab_screen_39.png) ### Git文件冲突问题解决 - 修改远程仓库代码: ![](../images/gitlab_screen_40.png) - 修改本地仓库代码: ![](../images/gitlab_screen_41.png) - 提交本地仓库代码并拉取,发现代码产生冲突,点击Merge进行合并: ![](../images/gitlab_screen_42.png) - 点击箭头将左右两侧代码合并到中间区域: ![](../images/gitlab_screen_43.png) - 冲突合并完成后,点击Apply生效: ![](../images/gitlab_screen_44.png) - 提交代码并推送到远程。 ### 从dev分支合并代码到master - 在远程仓库修改dev分支代码: ![](../images/gitlab_screen_45.png) - 在本地仓库拉取代码,选择从dev分支拉取并进行合并: ![](../images/gitlab_screen_46.png) - 发现产生冲突,解决后提交并推送到远程仓库即可。 ![](../images/gitlab_screen_47.png) ### 查看Git仓库提交历史记录 ![](../images/gitlab_screen_48.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/idea_plugins.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 我常用的IDEA插件大公开,个个是精品! > 总结了平时工作中常用的12款IDEA插件,后端和前端的都有了,附上我的使用技巧,看完之后开发效率杠杠的! ## Lombok > Lombok为Java项目提供了非常有趣的附加功能,使用它的注解可以有效的地解决那些繁琐又重复的代码,例如 Setter、Getter、toString、equals、hashCode 以及非空判断等。 - 举个例子,我们给一个类添加@Getter和@Setter注解: ```java /** * 修改订单费用信息参数 * Created by macro on 2018/10/29. */ @Getter @Setter public class OmsMoneyInfoParam { private Long orderId; private BigDecimal freightAmount; private BigDecimal discountAmount; private Integer status; } ``` - Lombok就会为我们自动生成所有属性的Getter和Setter方法。 ![](../images/idea_plugins_01.png) ## Free MyBatis Plugin > MyBatis扩展插件,可以在Mapper接口的方法和xml实现之间自由跳转,也可以用来一键生成某些xml实现。 - 我们可以通过Mapper接口中方法左侧的箭头直接跳转到对应的xml实现中去; ![](../images/idea_plugins_02.png) - 也可以从xml中Statement左侧的箭头直接跳转到对应的Mapper接口方法中去; ![](../images/idea_plugins_03.png) - 还可以通过`Alt+Enter`键组合直接生成新方法的xml实现,使用起来是不是很方便! ![](../images/idea_plugins_04.png) ## MyBatis Log Plugin > 有时候我们需要运行过程中产生的SQL语句来帮助我们排查某些问题,这款插件可以把Mybatis输出的SQL日志还原成完整的SQL语句,就不需要我们去手动转换了。 - 首先我们需要打开这款插件的窗口; ![](../images/idea_plugins_05.png) - 当我们调用方法,控制台输出Mybatis的SQL日志时; ``` 2020-04-28 15:52:20.455 DEBUG 13960 --- [nio-8081-exec-1] c.m.m.m.UmsAdminMapper.selectByExample : ==> Preparing: select id, username, password, icon, email, nick_name, note, create_time, login_time, status from ums_admin WHERE ( username = ? ) 2020-04-28 15:52:20.456 DEBUG 13960 --- [nio-8081-exec-1] c.m.m.m.UmsAdminMapper.selectByExample : ==> Parameters: admin(String) 2020-04-28 15:52:20.463 DEBUG 13960 --- [nio-8081-exec-1] c.m.m.m.UmsAdminMapper.selectByExample : <== Total: 1 ``` - 该插件会自动帮我们转换成对应的SQL语句; ``` 1 2020-04-28 15:50:40.487 DEBUG 9512 --- [nio-8081-exec-9] c.m.m.m.UmsAdminMapper.selectByExample : ==> select id, username, password, icon, email, nick_name, note, create_time, login_time, status FROM ums_admin WHERE ( username = 'admin' ); ``` - 有的时候我们需要转换的日志并不在自己的控制台上,这时可以使用插件的`SQL Text`功能: ![](../images/idea_plugins_06.png) - 直接复制我们需要转换的日志,然后点击`Restore Sql`按钮即可。 ![](../images/idea_plugins_07.png) ## RestfulToolkit > 一套Restful服务开发辅助工具集,提供了项目中的接口概览信息,可以根据URL跳转到对应的接口方法中去,内置了HTTP请求工具,对请求方法做了一些增强功能,总之功能很强大! - 可以通过右上角的`RestServices`按钮显示项目中接口的概览信息; ![](../images/idea_plugins_08.png) - 可以通过搜索按钮,根据URL搜索对应接口; ![](../images/idea_plugins_09.png) - 可以通过底部的HTTP请求工具来发起接口测试请求; ![](../images/idea_plugins_10.png) - 通过在接口方法上右键可以生成查询参数、请求参数、请求URL; ![](../images/idea_plugins_11.png) - 通过在实体类上右键可以直接生成实体类对应的JSON; ![](../images/idea_plugins_12.png) ## Translation > 一款翻译插件,支持Google、有道、百度翻译,对我们看源码时看注释很有帮助! - 直接选中需要翻译的内容,点击右键即可找到翻译按钮; ![](../images/idea_plugins_13.png) - 直接使用`翻译文档`可以将整个文档都进行翻译; ![](../images/idea_plugins_14.png) - 还可以通过右上角的翻译按钮直接翻译指定内容。 ![](../images/idea_plugins_15.png) ## GsonFormat > 这款插件可以把JSON格式的字符串转化为实体类,当我们要根据JSON字符串来创建实体类的时候用起来很方便。 - 首先我们需要先创建一个实体类,然后在类名上右键`Generate`,之后选择`GsonFormat`; ![](../images/idea_plugins_16.png) - 输入我们需要转换的JSON字符串: ![](../images/idea_plugins_17.png) - 选择性更改属性名称和类型: ![](../images/idea_plugins_18.png) - 点击确定后直接生成实体类。 ![](../images/idea_plugins_19.png) ## Grep Console > 一款帮你分析控制台日志的插件,可以对不同级别的日志进行不同颜色的高亮显示,还可以用来按关键字搜索日志内容。 - 当项目打印日志的时候,可以发现不同日志级别的日志会以不同颜色来显示; ![](../images/idea_plugins_20.png) - 如果你需要修改配色方案的话,可以通过`Tools`打开该插件的配置菜单; ![](../images/idea_plugins_21.png) - 然后通过配置菜单修改配色方案; ![](../images/idea_plugins_22.png) - 可以通过在控制台右键并使用`Grep`按钮来调出日志分析的窗口: ![](../images/idea_plugins_23.png) - 然后直接通过关键字来搜索即可。 ![](../images/idea_plugins_24.png) ## Alibaba Java Coding Guidelines > 阿里巴巴《Java 开发手册》配套插件,可以实时检测代码中不符合手册规约的地方,助你码出高效,码出质量。 - 比如说手册里有这么一条; ![](../images/idea_plugins_25.png) - 当我们违反手册规约时,该插件会自动检测并进行提示; ![](../images/idea_plugins_26.png) - 同时提供了一键检测所有代码规约情况和切换语言的功能; ![](../images/idea_plugins_27.png) - 如果你想修改某条规约的检测规则的话,可以通过设置的`Editor->Inspections`进行修改。 ![](../images/idea_plugins_28.png) ## Maven Helper > 解决Maven依赖冲突的好帮手,可以快速查找项目中的依赖冲突,并予以解决! - 我们可以通过`pom.xml`文件底部的`依赖分析`标签页查看当前项目中的所有依赖; ![](../images/idea_plugins_29.png) - 通过`冲突`按钮我们可以筛选出所有冲突的依赖,当前项目`guava`依赖有冲突,目前使用的是`18.0`版本; ![](../images/idea_plugins_30.png) - 选中有冲突的依赖,点击`Exclude`按钮可以直接排除该依赖; ![](../images/idea_plugins_31.png) - 同时`pom.xml`中也会对该依赖添加``标签,是不是很方便啊! ![](../images/idea_plugins_32.png) ## Statistic > 一款代码统计工具,可以用来统计当前项目中代码的行数和大小。 - 我们可以通过顶部菜单中的`View->Tool Windows->Statistic`按钮开启该功能; ![](../images/idea_plugins_33.png) - 此时就可以看到我们项目代码的统计情况了,比如我的开源项目`mall`中`java`代码大小为`2818kB`,行数为`85645`。 ![](../images/idea_plugins_34.png) ## Vue.js > Vue.js支持插件,写过前端的朋友肯定用过,可以根据模板创建`.vue`文件,也可以对Vue相关代码进行智能提示。 - 启用该插件后,可以根据模板新建`.vue`文件; ![](../images/idea_plugins_35.png) - 当我们在标签中写入以`v-`开头的代码时,会提示Vue中的相关指令。 ![](../images/idea_plugins_36.png) ## element > Element-UI支持插件,可以对Element-UI中的标签进行智能提示,有了它就不用盲写相关代码了! - 当我们写入以`el-`开头的标签时,会提示Element-UI相关组件。 ![](../images/idea_plugins_37.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/idea_springboot.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # IDEA中创建和启动SpringBoot应用的正确姿势 > 本文主要讲解如何在IDEA中创建、启动SpringBoot应用以及查看应用暴露监控端点的正确方式。 ## 创建SpringBoot应用 > 由于SpringCloud应用也属于SpringBoot应用的一种,这里我们以创建Eureka注册中心为例来看看在IDEA中创建并运行SpringBoot应用的正确姿势。 ### 使用IDEA来创建SpringBoot应用 - 创建一个Eureka注册中心模块,并使用Spring Initializer初始化一个SpringBoot项目: ![](../images/springcloud_eureka_01.png) - 填写应用信息: ![](../images/springcloud_eureka_02.png) - 选择你需要的SpringBoot或SpringCloud组件进行创建: ![](../images/springcloud_eureka_03.png) - 创建完成后会发现pom.xml文件中已经有了eureka-server的依赖: ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-server ``` - 当然你也可以使用其他组件来创建SpringBoot应用,IDEA中提供了很多SpringBoot中常用的组件,比如: ![](../images/springcloud_idea_01.png) ## 启动SpringBoot应用 > 创建完成的SpringBoot应用,直接运行启动类的main方法就可以运行了。但是有时候我们会启动很多应用,为了便于管理,我们使用IDEA的Run Dashboard来启动。 ### 打开Run Dashboard的方式 默认情况下,当IDEA检查到你的项目中有SpringBoot应用时,会提示你开启,如果你没开启,可以用以下方法开启。 ![](../images/springcloud_eureka_04.png) ### 通过默认配置启动 > 直接在Run Dashboard中右键应用,点击run即可启动。 ![](../images/springcloud_eureka_05.png) ### 使用指定配置运行 > 我们曾经搭建过一个用于注册到注册中心的eureka-client服务,只使用默认配置启动的话,我们直接使用上面的启动方式即可,但是如果我们要用其他配置来启动,如果我们没使用IDEA的话,可能会把eureka-client打成jar包,然后使用java命令指定不同的配置来启动,接下来我们试试使用IDEA怎么用不同的配置启动同一个SpringBoot应用。 - 此时我们的eureka-client有三个不同的配置,默认配置为application.yml,我们将采用application-replica.yml来启动它; ![](../images/springcloud_idea_02.png) - 首先我们复制eureka-client原来的启动配置: ![](../images/springcloud_idea_03.png) - 复制完后设置启动文件为application-replica.yml: ![](../images/springcloud_idea_04.png) - 直接在Run Dashboard中运行即可。 ### 覆盖配置中的某个属性运行 > 如果我们启动只是需要覆盖某个SpringBoot的配置,比如说是运行的端口号,可以采用以下方式,还是以eureka-client为例。 复制eureka-client原来的启动配置,然后直接修改运行端口号即可: ![](../images/springcloud_idea_05.png) ## 查看SpringBoot应用暴露的监控端点 > 我们可以从IDEA的Run Dashboard中查看到所有暴露的Actuator监控端点,这里以hystrix-dashboard(断路器仪表盘服务)为例。 ![](../images/springcloud_idea_06.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/jenkins.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 使用Jenkins一键打包部署SpringBoot应用,就是这么6! > 任何简单操作的背后,都有一套相当复杂的机制。本文将以SpringBoot应用的在Docker环境下的打包部署为例,详细讲解如何使用Jenkins一键打包部署SpringBoot应用。 ## Jenkins简介 Jenkins是开源CI&CD软件领导者,提供超过1000个插件来支持构建、部署、自动化,满足任何项目的需要。我们可以用Jenkins来构建和部署我们的项目,比如说从我们的代码仓库获取代码,然后将我们的代码打包成可执行的文件,之后通过远程的ssh工具执行脚本来运行我们的项目。 ## Jenkins的安装及配置 ### Docker环境下的安装 - 下载Jenkins的Docker镜像: ```bash docker pull jenkins/jenkins:lts ``` - 在Docker容器中运行Jenkins: ```bash docker run -p 8080:8080 -p 50000:5000 --name jenkins \ -u root \ -v /mydata/jenkins_home:/var/jenkins_home \ -d jenkins/jenkins:lts ``` ### Jenkins的配置 - 运行成功后访问该地址登录Jenkins,第一次登录需要输入管理员密码:http://192.168.6.132:8080/ ![](../images/jenkins_use_23.png) - 使用管理员密码进行登录,可以使用以下命令从容器启动日志中获取管理密码: ```bash docker logs jenkins ``` - 从日志中获取管理员密码: ![](../images/jenkins_use_24.png) - 选择安装插件方式,这里我们直接安装推荐的插件: ![](../images/jenkins_use_25.png) - 进入插件安装界面,联网等待插件安装: ![](../images/jenkins_use_26.png) - 安装完成后,创建管理员账号: ![](../images/jenkins_use_27.png) - 进行实例配置,配置Jenkins的URL: ![](../images/jenkins_use_28.png) - 点击系统管理->插件管理,进行一些自定义的插件安装: ![](../images/jenkins_use_14.png) - 确保以下插件被正确安装: - 根据角色管理权限的插件:Role-based Authorization Strategy - 远程使用ssh的插件:SSH plugin - 通过系统管理->全局工具配置来进行全局工具的配置,比如maven的配置: ![](../images/jenkins_use_15.png) - 新增maven的安装配置: ![](../images/jenkins_use_16.png) - 在系统管理->系统配置中添加全局ssh的配置,这样Jenkins使用ssh就可以执行远程的linux脚本了: ![](../images/jenkins_use_17.png) ### 角色权限管理 > 我们可以使用Jenkins的角色管理插件来管理Jenkins的用户,比如我们可以给管理员赋予所有权限,运维人员赋予执行任务的相关权限,其他人员只赋予查看权限。 - 在系统管理->全局安全配置中启用基于角色的权限管理: ![](../images/jenkins_use_18.png) - 进入系统管理->Manage and Assign Roles界面: ![](../images/jenkins_use_19.png) - 添加角色与权限的关系: ![](../images/jenkins_use_20.png) - 给用户分配角色: ![](../images/jenkins_use_21.png) ## 打包部署SpringBoot应用 > 这里我们使用`mall-learning`项目中的`mall-tiny-jenkins`模块代码来演示下如何使Jenkins一键打包部署SpringBoot应用。 ### 将代码上传到Git仓库 - 首先我们需要安装Gitlab(当然你也可以使用Github或者Gitee),然后将`mall-tiny-jenkins`中的代码上传到Gitlab中去,Gitlab的使用请参考:[10分钟搭建自己的Git仓库](https://mp.weixin.qq.com/s/6GyYlR9lpVcjgYmHMYLi0w) - `mall-tiny-jenkins`项目源码地址:https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-jenkins - 上传完成后Gitlab中的展示效果如下: ![](../images/jenkins_use_01.png) - 有一点需要`注意`,要将pom.xml中的dockerHost地址改成你自己的Docker镜像仓库地址: ![](../images/jenkins_use_22.png) ### 执行脚本准备 - 将`mall-tiny-jenkins.sh`脚本文件上传到`/mydata/sh`目录下,脚本内容如下: ```bash #!/usr/bin/env bash app_name='mall-tiny-jenkins' docker stop ${app_name} echo '----stop container----' docker rm ${app_name} echo '----rm container----' docker run -p 8088:8088 --name ${app_name} \ --link mysql:db \ -v /etc/localtime:/etc/localtime \ -v /mydata/app/${app_name}/logs:/var/logs \ -d mall-tiny/${app_name}:1.0-SNAPSHOT echo '----start container----' ``` - 给.sh脚本添加可执行权限: ```bash chmod +x ./mall-tiny-jenkins.sh ``` - windows下的.sh脚本上传到linux上使用,需要修改文件格式,否则会因为有特殊格式存在而无法执行: ```bash #使用vim编辑器来修改 vi mall-tiny-jenkins.sh # 查看文件格式,windows上传上来的默认为dos :set ff #修改文件格式为unix :set ff=unix #保存并退出 :wq ``` - 执行.sh脚本,测试使用,可以不执行: ```bash ./mall-tiny-jenkins.sh ``` ### 在Jenkins中创建执行任务 - 首先我们需要新建一个任务: ![](../images/jenkins_use_02.png) - 设置任务名称后选择构建一个自由风格的软件项目: ![](../images/jenkins_use_03.png) - 然后在源码管理中添加我们的git仓库地址:http://192.168.6.132:1080/macrozheng/mall-tiny-jenkins ![](../images/jenkins_use_04.png) - 此时需要添加一个凭据,也就是我们git仓库的账号密码: ![](../images/jenkins_use_05.png) - 填写完成后选择该凭据,就可以正常连接git仓库了; ![](../images/jenkins_use_06.png) - 之后我们需要添加一个构建,选择调用顶层maven目标,该构建主要用于把我们的源码打包成Docker镜像并上传到我们的Docker镜像仓库去: ![](../images/jenkins_use_07.png) - 选择我们的maven版本,然后设置maven命令和指定pom文件位置: ![](../images/jenkins_use_08.png) - 之后添加一个执行远程shell脚本的构建,用于在我们的镜像打包完成后执行启动Docker容器的.sh脚本: ![](../images/jenkins_use_09.png) - 需要设置执行的shell命令如下:/mydata/sh/mall-tiny-jenkins.sh ![](../images/jenkins_use_10.png) - 之后点击保存操作,我们的任务就创建完成了,在任务列表中我们可以点击运行来执行该任务; ![](../images/jenkins_use_11.png) - 我们可以通过控制台输出来查看整个任务的执行过程: ![](../images/jenkins_use_12.png) - 运行成功后,访问该地址即可查看API文档:http://192.168.6.132:8088/swagger-ui.html ![](../images/jenkins_use_13.png) ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-jenkins ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/jenkins_vue.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 使用Jenkins一键打包部署前端应用,就是这么6! > 上一次我们讲到了使用Jenkins一键打包部署SpringBoot应用,这一次我们来讲下如何一键打包部署前端应用,以Vue前端应用为例,这里我们使用`mall-admin-web`中的代码来进行演示。 ## 学前准备 > 学习本文需要一些Jenkins和Nginx的知识,对这些不熟悉的小伙伴可以参考以下文章。 - [使用Jenkins一键打包部署SpringBoot应用,就是这么6!](https://mp.weixin.qq.com/s/tQqvgSc9cHBtnqRQSbI4aw) - [Nginx的这些妙用,你肯定有不知道的!](https://mp.weixin.qq.com/s/9VZi2suAlomu1IRGy-qdCA) ## Jenkins中的自动化部署 > Vue前端应用的打包需要依赖NodeJS插件,所以我们先安装并配置该插件,然后创建任务来打包部署。 ### 安装NodeJS插件 - 在系统设置->插件管理中选择安装插件; ![](../images/jenkins_vue_01.png) - 搜索`NodeJS`插件并进行安装; ![](../images/jenkins_vue_02.png) ### 配置NodeJS插件 - 在系统设置->全局工具配置中进行插件配置; ![](../images/jenkins_vue_03.png) - 选择`新增NodeJS`,配置好版本号以后,点击保存即可完成设置; ![](../images/jenkins_vue_04.png) ### 创建任务 > 我们需要创建一个任务来打包部署我们的前端应用,这里以我的`mall-admin-web`项目为例。 - 任务执行流程如下: ![](../images/jenkins_vue_11.png) - 构建一个自由风格的软件项目: ![](../images/jenkins_vue_05.png) - 在源码管理中添加Git代码仓库相关配置,这里我使用的Gitee上面的代码,地址为:https://gitee.com/macrozheng/mall-admin-web ![](../images/jenkins_vue_06.png) - 在构建环境中把我们的`node`环境添加进去: ![](../images/jenkins_vue_07.png) - 添加一个`执行shell`的构建,用于将我们的前端代码进行编译打包: ![](../images/jenkins_vue_08.png) - 构建脚本如下: ```bash # 查看版本信息 npm -v # 解决存放在Github上的sass无法下载的问题 SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/ npm install node-sass # 将镜像源替换为淘宝的加速访问 npm config set registry https://registry.npm.taobao.org # 安装项目依赖 npm install # 项目打包 npm run build ``` - 添加一个`使用ssh执行远程脚本`的构建,用于将我们打包后的代码发布到Nginx中去: ![](../images/jenkins_vue_09.png) - 远程执行脚本如下: ```bash docker stop nginx echo '----stop nginx----' rm -rf /mydata/nginx/html echo '----rm html dir----' cp -r /mydata/jenkins_home/workspace/mall-admin-web/dist /mydata/nginx/html echo '----cp dist dir to html dir----' docker start nginx echo '----start nginx----' ``` - 点击保存后,直接在任务列表中点击运行即可完成自动化部署: ![](../images/jenkins_vue_10.png) ## 遇到的坑 ### node-sass无法下载导致构建失败 由于node-sass的源使用的是Github上面的,经常无法访问,我们构建的时候需要单独设置node-sass的下载地址。 ```bash # linux SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/ npm install node-sass # window set SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass&& npm install node-sass ``` ### 有些依赖无法下载导致构建失败 由于npm源访问慢的问题,有些源可能会无法下载,改用淘宝的npm源即可解决。 ```bash # 设置为淘宝的镜像源 npm config set registry https://registry.npm.taobao.org # 设置为官方镜像源 npm config set registry https://registry.npmjs.org ``` ## 项目地址 [https://github.com/macrozheng/mall-admin-web](https://github.com/macrozheng/mall-admin-web) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/jose_jwt_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 听说你的JWT库用起来特别扭,推荐一款贼好用的! > 以前一直使用的是`jjwt`这个JWT库,虽然小巧够用, 但对JWT的一些细节封装的不是很好。最近发现了一个更好用的JWT库`nimbus-jose-jwt`,简单易用,API非常易于理解,对称加密和非对称加密算法都支持,推荐给大家! ## 简介 `nimbus-jose-jwt`是最受欢迎的JWT开源库,基于Apache 2.0开源协议,支持所有标准的签名(JWS)和加密(JWE)算法。 ## JWT概念关系 这里我们需要了解下JWT、JWS、JWE三者之间的关系,其实JWT(JSON Web Token)指的是一种规范,这种规范允许我们使用JWT在两个组织之间传递安全可靠的信息。而JWS(JSON Web Signature)和JWE(JSON Web Encryption)是JWT规范的两种不同实现,我们平时最常使用的实现就是JWS。 ## 使用 > 接下来我们将介绍下`nimbus-jose-jwt`库的使用,主要使用对称加密(HMAC)和非对称加密(RSA)两种算法来生成和解析JWT令牌。 ### 对称加密(HMAC) > 对称加密指的是使用`相同`的秘钥来进行加密和解密,如果你的秘钥不想暴露给解密方,考虑使用非对称加密。 - 要使用`nimbus-jose-jwt`库,首先在`pom.xml`添加相关依赖; ```xml com.nimbusds nimbus-jose-jwt 8.16 ``` - 创建`JwtTokenServiceImpl`作为JWT处理的业务类,添加根据`HMAC`算法生成和解析JWT令牌的方法,可以发现`nimbus-jose-jwt`库操作JWT的API非常易于理解; ```java /** * Created by macro on 2020/6/22. */ @Service public class JwtTokenServiceImpl implements JwtTokenService { @Override public String generateTokenByHMAC(String payloadStr, String secret) throws JOSEException { //创建JWS头,设置签名算法和类型 JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.HS256). type(JOSEObjectType.JWT) .build(); //将负载信息封装到Payload中 Payload payload = new Payload(payloadStr); //创建JWS对象 JWSObject jwsObject = new JWSObject(jwsHeader, payload); //创建HMAC签名器 JWSSigner jwsSigner = new MACSigner(secret); //签名 jwsObject.sign(jwsSigner); return jwsObject.serialize(); } @Override public PayloadDto verifyTokenByHMAC(String token, String secret) throws ParseException, JOSEException { //从token中解析JWS对象 JWSObject jwsObject = JWSObject.parse(token); //创建HMAC验证器 JWSVerifier jwsVerifier = new MACVerifier(secret); if (!jwsObject.verify(jwsVerifier)) { throw new JwtInvalidException("token签名不合法!"); } String payload = jwsObject.getPayload().toString(); PayloadDto payloadDto = JSONUtil.toBean(payload, PayloadDto.class); if (payloadDto.getExp() < new Date().getTime()) { throw new JwtExpiredException("token已过期!"); } return payloadDto; } } ``` - 创建`PayloadDto`实体类,用于封装JWT中存储的信息; ```java /** * Created by macro on 2020/6/22. */ @Data @EqualsAndHashCode(callSuper = false) @Builder public class PayloadDto { @ApiModelProperty("主题") private String sub; @ApiModelProperty("签发时间") private Long iat; @ApiModelProperty("过期时间") private Long exp; @ApiModelProperty("JWT的ID") private String jti; @ApiModelProperty("用户名称") private String username; @ApiModelProperty("用户拥有的权限") private List authorities; } ``` - 在`JwtTokenServiceImpl`类中添加获取默认的PayloadDto的方法,JWT过期时间设置为`60min`; ```java /** * Created by macro on 2020/6/22. */ @Service public class JwtTokenServiceImpl implements JwtTokenService { @Override public PayloadDto getDefaultPayloadDto() { Date now = new Date(); Date exp = DateUtil.offsetSecond(now, 60*60); return PayloadDto.builder() .sub("macro") .iat(now.getTime()) .exp(exp.getTime()) .jti(UUID.randomUUID().toString()) .username("macro") .authorities(CollUtil.toList("ADMIN")) .build(); } } ``` - 创建`JwtTokenController`类,添加根据HMAC算法生成和解析JWT令牌的接口,由于HMAC算法需要长度至少为`32个字节`的秘钥,所以我们使用MD5加密下; ```java /** * JWT令牌管理Controller * Created by macro on 2020/6/22. */ @Api(tags = "JwtTokenController", description = "JWT令牌管理") @Controller @RequestMapping("/token") public class JwtTokenController { @Autowired private JwtTokenService jwtTokenService; @ApiOperation("使用对称加密(HMAC)算法生成token") @RequestMapping(value = "/hmac/generate", method = RequestMethod.GET) @ResponseBody public CommonResult generateTokenByHMAC() { try { PayloadDto payloadDto = jwtTokenService.getDefaultPayloadDto(); String token = jwtTokenService.generateTokenByHMAC(JSONUtil.toJsonStr(payloadDto), SecureUtil.md5("test")); return CommonResult.success(token); } catch (JOSEException e) { e.printStackTrace(); } return CommonResult.failed(); } @ApiOperation("使用对称加密(HMAC)算法验证token") @RequestMapping(value = "/hmac/verify", method = RequestMethod.GET) @ResponseBody public CommonResult verifyTokenByHMAC(String token) { try { PayloadDto payloadDto = jwtTokenService.verifyTokenByHMAC(token, SecureUtil.md5("test")); return CommonResult.success(payloadDto); } catch (ParseException | JOSEException e) { e.printStackTrace(); } return CommonResult.failed(); } } ``` - 调用使用HMAC算法生成JWT令牌的接口进行测试; ![](../images/jose_jwt_01.png) - 调用使用HMAC算法解析JWT令牌的接口进行测试。 ![](../images/jose_jwt_02.png) ### 非对称加密(RSA) > 非对称加密指的是使用公钥和私钥来进行加密解密操作。对于`加密`操作,公钥负责加密,私钥负责解密,对于`签名`操作,私钥负责签名,公钥负责验证。非对称加密在JWT中的使用显然属于`签名`操作。 - 如果我们需要使用固定的公钥和私钥来进行签名和验证的话,我们需要生成一个证书文件,这里将使用Java自带的`keytool`工具来生成`jks`证书文件,该工具在JDK的`bin`目录下; ![](../images/jose_jwt_03.png) - 打开CMD命令界面,使用如下命令生成证书文件,设置别名为`jwt`,文件名为`jwt.jks`; ```bash keytool -genkey -alias jwt -keyalg RSA -keystore jwt.jks ``` - 输入密码为`123456`,然后输入各种信息之后就可以生成证书`jwt.jks`文件了; ![](../images/jose_jwt_04.png) - 将证书文件`jwt.jks`复制到项目的`resource`目录下,然后需要从证书文件中读取`RSAKey`,这里我们需要在`pom.xml`中添加一个Spring Security的RSA依赖; ```xml org.springframework.security spring-security-rsa 1.0.7.RELEASE ``` - 然后在`JwtTokenServiceImpl`类中添加方法,从类路径下读取证书文件并转换为`RSAKey`对象; ```java /** * Created by macro on 2020/6/22. */ @Service public class JwtTokenServiceImpl implements JwtTokenService { @Override public RSAKey getDefaultRSAKey() { //从classpath下获取RSA秘钥对 KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray()); KeyPair keyPair = keyStoreKeyFactory.getKeyPair("jwt", "123456".toCharArray()); //获取RSA公钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); //获取RSA私钥 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); return new RSAKey.Builder(publicKey).privateKey(privateKey).build(); } } ``` - 我们可以在`JwtTokenController`中添加一个接口,用于获取证书中的公钥; ```java /** * JWT令牌管理Controller * Created by macro on 2020/6/22. */ @Api(tags = "JwtTokenController", description = "JWT令牌管理") @Controller @RequestMapping("/token") public class JwtTokenController { @Autowired private JwtTokenService jwtTokenService; @ApiOperation("获取非对称加密(RSA)算法公钥") @RequestMapping(value = "/rsa/publicKey", method = RequestMethod.GET) @ResponseBody public Object getRSAPublicKey() { RSAKey key = jwtTokenService.getDefaultRSAKey(); return new JWKSet(key).toJSONObject(); } } ``` - 调用该接口,查看公钥信息,公钥是可以公开访问的; ![](../images/jose_jwt_05.png) - 在`JwtTokenServiceImpl`中添加根据`RSA`算法生成和解析JWT令牌的方法,可以发现和上面的`HMAC`算法操作基本一致; ```java /** * Created by macro on 2020/6/22. */ @Service public class JwtTokenServiceImpl implements JwtTokenService { @Override public String generateTokenByRSA(String payloadStr, RSAKey rsaKey) throws JOSEException { //创建JWS头,设置签名算法和类型 JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256) .type(JOSEObjectType.JWT) .build(); //将负载信息封装到Payload中 Payload payload = new Payload(payloadStr); //创建JWS对象 JWSObject jwsObject = new JWSObject(jwsHeader, payload); //创建RSA签名器 JWSSigner jwsSigner = new RSASSASigner(rsaKey, true); //签名 jwsObject.sign(jwsSigner); return jwsObject.serialize(); } @Override public PayloadDto verifyTokenByRSA(String token, RSAKey rsaKey) throws ParseException, JOSEException { //从token中解析JWS对象 JWSObject jwsObject = JWSObject.parse(token); RSAKey publicRsaKey = rsaKey.toPublicJWK(); //使用RSA公钥创建RSA验证器 JWSVerifier jwsVerifier = new RSASSAVerifier(publicRsaKey); if (!jwsObject.verify(jwsVerifier)) { throw new JwtInvalidException("token签名不合法!"); } String payload = jwsObject.getPayload().toString(); PayloadDto payloadDto = JSONUtil.toBean(payload, PayloadDto.class); if (payloadDto.getExp() < new Date().getTime()) { throw new JwtExpiredException("token已过期!"); } return payloadDto; } } ``` - 在`JwtTokenController`类,添加根据RSA算法生成和解析JWT令牌的接口,使用默认的RSA钥匙对; ```java /** * JWT令牌管理Controller * Created by macro on 2020/6/22. */ @Api(tags = "JwtTokenController", description = "JWT令牌管理") @Controller @RequestMapping("/token") public class JwtTokenController { @Autowired private JwtTokenService jwtTokenService; @ApiOperation("使用非对称加密(RSA)算法生成token") @RequestMapping(value = "/rsa/generate", method = RequestMethod.GET) @ResponseBody public CommonResult generateTokenByRSA() { try { PayloadDto payloadDto = jwtTokenService.getDefaultPayloadDto(); String token = jwtTokenService.generateTokenByRSA(JSONUtil.toJsonStr(payloadDto),jwtTokenService.getDefaultRSAKey()); return CommonResult.success(token); } catch (JOSEException e) { e.printStackTrace(); } return CommonResult.failed(); } @ApiOperation("使用非对称加密(RSA)算法验证token") @RequestMapping(value = "/rsa/verify", method = RequestMethod.GET) @ResponseBody public CommonResult verifyTokenByRSA(String token) { try { PayloadDto payloadDto = jwtTokenService.verifyTokenByRSA(token, jwtTokenService.getDefaultRSAKey()); return CommonResult.success(payloadDto); } catch (ParseException | JOSEException e) { e.printStackTrace(); } return CommonResult.failed(); } } ``` - 调用使用RSA算法生成JWT令牌的接口进行测试; ![](../images/jose_jwt_06.png) - 调用使用RSA算法解析JWT令牌的接口进行测试。 ![](../images/jose_jwt_07.png) ## 参考资料 官方文档:https://connect2id.com/products/nimbus-jose-jwt ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-jwt ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/knife4j_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 给Swagger换了个新皮肤,瞬间高大上了! > Swagger作为一款API文档生成工具,虽然功能已经很完善了,但是还是有些不足的地方。偶然发现knife4j弥补了这些不足,赋予了Swagger更多的功能,今天我们来讲下它的使用方法。 ## knife4j简介 knife4j是springfox-swagger的增强UI实现,为Java开发者在使用Swagger的时候,提供了简洁、强大的接口文档体验。knife4j完全遵循了springfox-swagger中的使用方式,并在此基础上做了增强功能,如果你用过Swagger,你就可以无缝切换到knife4j。 ## 快速开始 > 接下来我们来介绍下如何在SpringBoot中使用knife4j,仅需两步即可! - 在pom.xml中增加knife4j的相关依赖; ```xml com.github.xiaoymin knife4j-spring-boot-starter 2.0.4 ``` - 在Swagger2Config中增加一个@EnableKnife4j注解,该注解可以开启knife4j的增强功能; ```java /** * Swagger2API文档的配置 */ @Configuration @EnableSwagger2 @EnableKnife4j public class Swagger2Config { } ``` - 运行我们的SpringBoot应用,访问API文档地址即可查看:http://localhost:8088/doc.html ![](../images/knife4j_start_01.png) ## 功能特点 > 接下来我们对比下Swagger,看看使用knife4j和它有啥不同之处! ### JSON功能增强 > 平时一直使用Swagger,但是Swagger的JSON支持一直不是很好,JSON不能折叠,太长没法看,传JSON格式参数时,没有参数校验功能。这些痛点,在knife4j上都得到了解决。 - 返回结果集支持折叠,方便查看; ![](../images/knife4j_start_02.png) - 请求参数有JSON校验功能。 ![](../images/knife4j_start_03.png) ### 登录认证 > knife4j也支持在头部添加Token,用于登录认证使用。 - 首先在`Authorize`功能中添加登录返回的Token; ![](../images/knife4j_start_04.png) - 之后在每个接口中就可以看到已经在请求头中携带了Token信息。 ![](../images/knife4j_start_05.png) ### 离线文档 > knife4j支持导出离线文档,方便发送给别人,支持Markdown格式。 - 直接选择`文档管理->离线文档`功能,然后选择`下载Markdown`即可; ![](../images/knife4j_start_06.png) - 我们来查看下导出的Markdown离线文档,还是很详细的。 ![](../images/knife4j_start_07.png) ### 全局参数 > knife4j支持临时设置全局参数,支持两种类型query(表单)、header(请求头)。 - 比如我们想要在所有请求头中加入一个参数appType来区分是android还是ios调用,可以在全局参数中添加; ![](../images/knife4j_start_08.png) - 此时再调用接口时,就会包含`appType`这个请求头了。 ![](../images/knife4j_start_09.png) ### 忽略参数属性 > 有时候我们创建和修改的接口会使用同一个对象作为请求参数,但是我们创建的时候并不需要id,而修改的时候会需要id,此时我们可以忽略id这个属性。 - 比如这里的创建商品接口,id、商品数量、商品评论数量都可以让后台接口生成无需传递,可以使用knife4j提供的`@ApiOperationSupport`注解来忽略这些属性; ```java /** * 品牌管理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("添加品牌") @ApiOperationSupport(ignoreParameters = {"id","productCount","productCommentCount"}) @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; } } ``` - 此时查看接口文档时,发现这三个属性已经消失,这样前端开发查看接口文档时就不会觉得你定义无用参数了,是不很很好的功能! ![](../images/knife4j_start_10.png) ## 参考资料 官方文档:https://doc.xiaominfo.com/guide/ ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-knife4j ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/linux.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 开发者必备Linux命令 > 开发者必备Linux常用命令,掌握这些命令绝对够了,基于CenterOS7.6。 ## 系统服务管理 ### systemctl - 输出系统中各个服务的状态: ```shell systemctl list-units --type=service ``` ![](../images/refer_screen_01.png) - 查看服务的运行状态: ```shell systemctl status firewalld ``` ![](../images/refer_screen_02.png) - 关闭服务: ```shell systemctl stop firewalld ``` ![](../images/refer_screen_03.png) - 启动服务: ```shell systemctl start firewalld ``` ![](../images/refer_screen_04.png) - 重新启动服务(不管当前服务是启动还是关闭): ```shell systemctl restart firewalld ``` - 重新载入配置信息而不中断服务: ```shell systemctl reload firewalld ``` - 禁止服务开机自启动: ```shell systemctl disable firewalld ``` ![](../images/refer_screen_05.png) - 设置服务开机自启动: ```shell systemctl enable firewalld ``` ![](../images/refer_screen_06.png) ## 文件管理 ### ls 列出当前目录(/)下的所有文件: ```shell ls -l / ``` ![](../images/refer_screen_07.png) ### pwd 获取目前所在工作目录的绝对路径 ![](../images/refer_screen_08.png) ### cd 改变当前工作目录: ```shell cd /usr/local ``` ![](../images/refer_screen_09.png) ### date 显示或修改系统时间与日期; ```shell date '+%Y-%m-%d %H:%M:%S' ``` ![](../images/refer_screen_10.png) ### passwd 用于设置用户密码: ```shell passwd root ``` ![](../images/refer_screen_11.png) ### su 改变用户身份(切换到超级用户): ```shell su - ``` ### clear 用于清除屏幕信息 ### man 显示指定命令的帮助信息: ```shell man ls ``` ### who - 查询系统处于什么运行级别: ```shell who -r ``` ![](../images/refer_screen_12.png) - 显示目前登录到系统的用户: ```shell who -buT ``` ![](../images/refer_screen_13.png) ### free 显示系统内存状态(单位MB): ```shell free -m ``` ![](../images/refer_screen_14.png) ### ps 显示系统进程运行动态: ```shell ps -ef ``` 查看sshd进程的运行动态: ```shell ps -ef | grep sshd ``` ![](../images/refer_screen_15.png) ### top 查看即时活跃的进程,类似Windows的任务管理器 ![](../images/refer_screen_16.png) ### mkdir 创建目录 ![](../images/refer_screen_17.png) ### more 用于文件过长时分页查看文件内容 每页10行查看boot.log文件 ```shell more -c -10 /var/log/boot.log ``` ![](../images/refer_screen_18.png) ### cat 查看Linux启动日志文件文件,并标明行号: ```shell cat -Ab /var/log/boot.log ``` ![](../images/refer_screen_19.png) ### touch 创建text.txt文件: ```shell touch text.txt ``` ![](../images/refer_screen_20.png) ### rm - 删除文件: ```shell rm text.txt ``` - 强制删除某个目录及其子目录: ```shell rm -rf testdir/ ``` ![](../images/refer_screen_21.png) ### cp 将test1目录复制到test2目录 ```shell cp -r /mydata/tes1 /mydata/test2 ``` ### mv 移动或覆盖文件: ```shell mv text.txt text2.txt ``` ## 压缩与解压 ### tar - 将/etc文件夹中的文件归档到文件etc.tar(并不会进行压缩): ```shell tar -cvf /mydata/etc.tar /etc ``` - 用gzip压缩文件夹/etc中的文件到文件etc.tar.gz: ```shell tar -zcvf /mydata/etc.tar.gz /etc ``` - 用bzip2压缩文件夹/etc到文件/etc.tar.bz2: ```shell tar -jcvf /mydata/etc.tar.bz2 /etc ``` ![](../images/refer_screen_22.png) - 分页查看压缩包中内容(gzip): ```shell tar -ztvf /mydata/etc.tar.gz |more -c -10 ``` ![](../images/refer_screen_24.png) - 解压文件到当前目录(gzip): ```shell tar -zxvf /mydata/etc.tar.gz ``` - 解压文件到指定目录(gzip): ```shell tar -zxvf /mydata/etc.tar.gz -C /mydata/etc ``` ## 磁盘和网络管理 ### df 查看磁盘空间占用情况: ```shell df -hT ``` ![](../images/refer_screen_25.png) ### dh 查看当前目录下的文件及文件夹所占大小: ```shell du -h --max-depth=1 ./* ``` ![](../images/refer_screen_26.png) ### ifconfig 显示当前网络接口状态 ![](../images/refer_screen_27.png) ### netstat - 查看当前路由信息: ```shell netstat -rn ``` ![](../images/refer_screen_28.png) - 查看所有有效TCP连接: ```shell netstat -an ``` - 查看系统中启动的监听服务: ```shell netstat -tulnp ``` ![](../images/refer_screen_29.png) - 查看处于连接状态的系统资源信息: ```shell netstat -atunp ``` ### wget 从网络上下载文件 ![](../images/refer_screen_30.png) ## 文件上传下载 ### 安装上传下载工具 ```bash yum install -y lrzsz ``` ### 上传文件 ```bash rz ``` ### 下载文件 ```bash sz fileName ``` ## 软件的安装与管理 ### rpm - 安装软件包:rpm -ivh nginx-1.12.2-2.el7.x86_64.rpm - 模糊搜索软件包:rpm -qa | grep nginx - 精确查找软件包:rpm -qa nginx - 查询软件包的安装路径:rpm -ql nginx-1.12.2-2.el7.x86_64 - 查看软件包的概要信息:rpm -qi nginx-1.12.2-2.el7.x86_64 - 验证软件包内容和安装文件是否一致:rpm -V nginx-1.12.2-2.el7.x86_64 - 更新软件包:rpm -Uvh nginx-1.12.2-2.el7.x86_64 - 删除软件包:rpm -e nginx-1.12.2-2.el7.x86_64 ### yum - 安装软件包: yum install nginx - 检查可以更新的软件包:yum check-update - 更新指定的软件包:yum update nginx - 在资源库中查找软件包信息:yum info nginx* - 列出已经安装的所有软件包:yum info installed - 列出软件包名称:yum list nginx* - 模糊搜索软件包:yum search nginx ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/linux_command.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 还在百度Linux命令?推荐一套我用起来特顺手的命令! > 作为一位Java后端开发,怎能不会点Linux命令?总结了一套非常实用的Linux命令(基于CentOS 7.6),希望对大家有所帮助! ## 系统服务管理 ### systemctl > `systemctl`命令是`service`和`chkconfig`命令的组合体,可用于管理系统。 - 输出系统中各个服务的状态: ```bash systemctl list-units --type=service ``` ![](../images/linux_command_01.png) - 查看服务的运行状态: ```bash systemctl status firewalld ``` ![](../images/linux_command_02.png) - 关闭服务: ```bash systemctl stop firewalld ``` ![](../images/linux_command_03.png) - 启动服务: ```bash systemctl start firewalld ``` ![](../images/linux_command_04.png) - 重新启动服务(不管当前服务是启动还是关闭): ```bash systemctl restart firewalld ``` - 重新载入配置信息而不中断服务: ```bash systemctl reload firewalld ``` - 禁止服务开机自启动: ```bash systemctl disable firewalld ``` ![](../images/linux_command_05.png) - 设置服务开机自启动: ```bash systemctl enable firewalld ``` ![](../images/linux_command_06.png) ## 文件管理 ### ls 列出指定目录下的所有文件,列出`/`目录下的文件: ```bash ls -l / ``` ![](../images/linux_command_07.png) ### pwd 获取目前所在工作目录的绝对路径: ![](../images/linux_command_08.png) ### cd 改变当前工作目录: ```bash cd /usr/local ``` ![](../images/linux_command_09.png) ### date 显示或修改系统时间与日期; ```bash date '+%Y-%m-%d %H:%M:%S' ``` ![](../images/linux_command_10.png) ### passwd 用于设置用户密码: ```bash passwd root ``` ![](../images/linux_command_11.png) ### su 改变用户身份(切换到超级用户): ```bash su - ``` ### clear 用于清除屏幕信息 ### man 显示指定命令的帮助信息: ```bash man ls ``` ### who - 查询系统处于什么运行级别: ```bash who -r ``` ![](../images/linux_command_12.png) - 显示目前登录到系统的用户: ```bash who -buT ``` ![](../images/linux_command_13.png) ### free 显示系统内存状态(单位MB): ```bash free -m ``` ![](../images/linux_command_14.png) ### ps - 显示系统进程运行动态: ```bash ps -ef ``` - 查看`sshd`进程的运行动态: ```bash ps -ef | grep sshd ``` ![](../images/linux_command_15.png) ### top 查看即时活跃的进程,类似Windows的任务管理器。 ![](../images/linux_command_16.png) ### mkdir 创建目录: ![](../images/linux_command_17.png) ### more 用于分页查看文件,例如每页10行查看`boot.log`文件: ```bash more -c -10 /var/log/boot.log ``` ![](../images/linux_command_18.png) ### cat 用于查看文件,例如查看Linux启动日志文件文件,并标明行号: ```bash cat -Ab /var/log/boot.log ``` ![](../images/linux_command_19.png) ### touch 用于创建文件,例如创建`text.txt`文件: ```bash touch text.txt ``` ![](../images/linux_command_20.png) ### rm - 删除文件: ```bash rm text.txt ``` - 强制删除某个目录及其子目录: ```bash rm -rf testdir/ ``` ![](../images/linux_command_21.png) ### cp 用于拷贝文件,例如将`test1`目录复制到`test2`目录 ```bash cp -r /mydata/tes1 /mydata/test2 ``` ### mv 用于移动或覆盖文件: ```bash mv text.txt text2.txt ``` ## 压缩与解压 ### tar - 将`/etc`文件夹中的文件归档到文件`etc.tar`(并不会进行压缩): ```bash tar -cvf /mydata/etc.tar /etc ``` - 用`gzip`压缩文件夹`/etc`中的文件到文件`etc.tar.gz`: ```bash tar -zcvf /mydata/etc.tar.gz /etc ``` - 用`bzip2`压缩文件夹`/etc`到文件`/etc.tar.bz2`: ```bash tar -jcvf /mydata/etc.tar.bz2 /etc ``` ![](../images/linux_command_22.png) - 分页查看压缩包中内容(gzip): ```bash tar -ztvf /mydata/etc.tar.gz |more -c -10 ``` ![](../images/linux_command_24.png) - 解压文件到当前目录(gzip): ```bash tar -zxvf /mydata/etc.tar.gz ``` - 解压文件到指定目录(gzip): ```bash tar -zxvf /mydata/etc.tar.gz -C /mydata/etc ``` ## 磁盘和网络管理 ### df 查看磁盘空间占用情况: ```bash df -hT ``` ![](../images/linux_command_25.png) ### dh 查看当前目录下的文件及文件夹所占大小: ```bash du -h --max-depth=1 ./* ``` ![](../images/linux_command_26.png) ### ifconfig 显示当前网络接口状态: ![](../images/linux_command_27.png) ### netstat - 查看当前路由信息: ```bash netstat -rn ``` ![](../images/linux_command_28.png) - 查看所有有效TCP连接: ```bash netstat -an ``` - 查看系统中启动的监听服务: ```bash netstat -tulnp ``` ![](../images/linux_command_29.png) - 查看处于连接状态的系统资源信息: ```bash netstat -atunp ``` ### wget 从网络上下载文件 ![](../images/linux_command_30.png) ## 文件上传下载 - 安装上传下载工具`lrzsz`; ```bash yum install -y lrzsz ``` - 上传文件,输入以下命令`XShell`会弹出文件上传框; ```bash rz ``` - 下载文件,输入以下命令`XShell`会弹出文件保存框; ```bash sz fileName ``` ## 软件的安装与管理 ### rpm > RPM是`Red-Hat Package Manager`的缩写,一种Linux下通用的软件包管理方式,可用于安装和管理`.rpm`结尾的软件包。 - 安装软件包: ```bash rpm -ivh nginx-1.12.2-2.el7.x86_64.rpm ``` - 模糊搜索软件包: ```bash rpm -qa | grep nginx ``` - 精确查找软件包: ```bash rpm -qa nginx ``` - 查询软件包的安装路径: ```bash rpm -ql nginx-1.12.2-2.el7.x86_64 ``` - 查看软件包的概要信息: ```bash rpm -qi nginx-1.12.2-2.el7.x86_64 ``` - 验证软件包内容和安装文件是否一致: ```bash rpm -V nginx-1.12.2-2.el7.x86_64 ``` - 更新软件包: ```bash rpm -Uvh nginx-1.12.2-2.el7.x86_64 ``` - 删除软件包: ```bash rpm -e nginx-1.12.2-2.el7.x86_64 ``` ### yum > Yum是`Yellow dog Updater, Modified`的缩写,能够在线自动下载RPM包并安装,可以自动处理依赖性关系,并且一次安装所有依赖的软件包,非常方便! - 安装软件包: ```bash yum install nginx ``` - 检查可以更新的软件包: ```bash yum check-update ``` - 更新指定的软件包: ```bash yum update nginx ``` - 在资源库中查找软件包信息: ```bash yum info nginx* ``` - 列出已经安装的所有软件包: ```bash yum info installed ``` - 列出软件包名称: ```bash yum list nginx* ``` - 模糊搜索软件包: ```bash yum search nginx ``` ## 用户管理 ### 用户信息查看 - 查看用户信息: ```bash cat /etc/passwd ``` - 用户信息格式如下(密码已过滤): ```bash # 用户名:密码:用户标识号:组标识号:组注释性描述:主目录:默认shell root:x:0:0:root:/root:/bin/bash macro:x:1000:982:macro:/home/macro:/bin/bash ``` - 查看用户组信息: ```bash cat /etc/group ``` - 用户组信息格式如下: ```bash # 组名:密码:组标识号:组内用户列表 root:x:0: docker:x:982:macro,andy ``` ### passwd 用于设置用户密码: ```bash passwd root ``` ![](images/linux_command_11.png) ### su 改变用户身份(切换到超级用户): ```bash # 切换到root用户 su - # 切换到macro用户 su macro ``` ### groupadd 添加用户组,使用`-g`可以设置用户组的标志号: ```bash groupadd -g 1024 macrozheng ``` ### groupdel 删除用户组: ```bash groupdel macrozheng ``` ### useradd 添加用户,`-u`设置标志号,`-g`设置主用户组: ```bash useradd -u 1024 -g macrozheng macro ``` ### usermod 修改用户所属用户组: ```bash usermod -g docker macro ``` ### userdel 删除用户,使用`-r`可以删除用户主目录: ```bash userdel macro -r ``` ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/linux_firewall.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! ## Linux防火墙Firewall和Iptables的使用 > Linux中有两种防火墙软件,ConterOS7.0以上使用的是firewall,ConterOS7.0以下使用的是iptables,本文将分别介绍两种防火墙软件的使用。 ## Firewall - 开启防火墙: ```shell systemctl start firewalld ``` - 关闭防火墙: ```shell systemctl stop firewalld ``` - 查看防火墙状态: ```shell systemctl status firewalld ``` - 设置开机启动: ```shell systemctl enable firewalld ``` - 禁用开机启动: ```shell systemctl disable firewalld ``` - 重启防火墙: ```shell firewall-cmd --reload ``` - 开放端口(修改后需要重启防火墙方可生效): ```shell firewall-cmd --zone=public --add-port=8080/tcp --permanent ``` ![](../images/refer_screen_31.png) - 查看开放的端口: ```shell firewall-cmd --list-ports ``` ![](../images/refer_screen_32.png) - 关闭端口: ```shell firewall-cmd --zone=public --remove-port=8080/tcp --permanent ``` ![](../images/refer_screen_33.png) ## Iptables ### 安装 > 由于CenterOS7.0以上版本并没有预装Iptables,我们需要自行装。 - 安装前先关闭firewall防火墙 ![](../images/refer_screen_34.png) - 安装iptables: ```shell yum install iptables ``` - 安装iptables-services: ```shell yum install iptables-services ``` ### 使用 - 开启防火墙: ```shell systemctl start iptables.service ``` ![](../images/refer_screen_35.png) - 关闭防火墙: ```shell systemctl stop iptables.service ``` - 查看防火墙状态: ```shell systemctl status iptables.service ``` - 设置开机启动: ```shell systemctl enable iptables.service ``` - 禁用开机启动: ```shell systemctl disable iptables.service ``` - 查看filter表的几条链规则(INPUT链可以看出开放了哪些端口): ```shell iptables -L -n ``` ![](../images/refer_screen_36.png) - 查看NAT表的链规则: ```shell iptables -t nat -L -n ``` ![](../images/refer_screen_37.png) - 清除防火墙所有规则: ```shell iptables -F ``` ```shell iptables -X ``` ```shell iptables -Z ``` - 给INPUT链添加规则(开放8080端口): ```shell iptables -I INPUT -p tcp --dport 8080 -j ACCEPT ``` ![](../images/refer_screen_38.png) - 查找规则所在行号: ```shell iptables -L INPUT --line-numbers -n ``` ![](../images/refer_screen_39.png) - 根据行号删除过滤规则(关闭8080端口): ```shell iptables -D INPUT 1 ``` ![](../images/refer_screen_40.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/linux_install.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 虚拟机安装及使用Linux,看这一篇就够了! > 本文将从虚拟机安装、Linux系统安装、SSH客户端工具使用三方面来详细介绍Linux系统的安装及使用。 ## 虚拟机安装 > VirtualBox 是一款开源虚拟机软件,由Sun公司出品,现在则由Oracle进行开发。VirtualBox号称是最强的免费虚拟机软件,它性能优异且简单易用。可虚拟的系统包括Windows、Linux、MacOS、Android等操作系统!本文将使用VirtualBox作为虚拟机来安装Linux系统。 ### VirtualBox的安装 - 我们先下载VirtualBox安装包,下载地址:https://www.virtualbox.org/wiki/Downloads - 下载完成后双击运行安装包一路点击下一步即可: ![](../images/linux_install_01.png) - 中途需要自定义一下安装路径: ![](../images/linux_install_02.png) - 最后点击完成,完成安装。 ![](../images/linux_install_03.png) ### 创建虚拟机 - 创建一个Linux虚拟机: ![](../images/linux_install_04.png) - 分配虚拟机内存大小,可以根据自己电脑配置来决定: ![](../images/linux_install_05.png) - 创建虚拟硬盘: ![](../images/linux_install_06.png) - 设置虚拟硬盘文件类型,这里选择DVI格式: ![](../images/linux_install_07.png) - 选择动态分配空间大小,如果你的硬盘空间比较大的话,可以选择固定大小: ![](../images/linux_install_08.png) - 设置虚拟硬盘的位置和大小: ![](../images/linux_install_09.png) ## Linux安装 > CentOS(Community Enterprise Operating System)是Linux发行版之一,中文意思为社区企业操作系统。它是来自于商业版 Red Hat Enterprise Linux依照开放源代码规定释出的源代码所编译而成,因此具有高度稳定性且完全开源。本文将以CentOS 7.6为例来介绍Linux系统的安装。 ### 下载 - 下载地址:http://vault.centos.org/7.6.1810/isos/x86_64/ - 下载文件:CentOS-7-x86_64-DVD-1810.iso ### 安装 - 为虚拟机添加虚拟光盘,虚拟光盘指定为我们下载的ISO镜像文件: ![](../images/linux_install_10.png) - 点击启动运行虚拟机: ![](../images/linux_install_11.png) - 运行成功后,选择`Install CentOS 7`进行安装: ![](../images/linux_install_12.png) - 选择系统安装过程中的语言,建议选择`English`选项: ![](../images/linux_install_13.png) - 需要进行设置的部分示意图: ![](../images/linux_install_14.png) - 时区设置,地区选择`Asia`,城市选择`Shanghai`: ![](../images/linux_install_15.png) - 语言支持选择安装英文、简体中文两种语言安装包: ![](../images/linux_install_16.png) - 软件安装设置选择`Server with GUI`,同时选择如图三种附加环境: ![](../images/linux_install_17.png) - 磁盘分区设置,由于我的虚拟机设置的内存较小,需要创建一个较大的`swap`分区,内存设置较大的直接选择自动分区即可,这里我们使用手动分区: ![](../images/linux_install_18.png) - 按如图所示进行手动分区操作; ![](../images/linux_install_19.png) - 关于分区的几个目录的说明: - /:根分区; - swap:交换分区,可以当虚拟内存使用; - /boot:存储系统的引导信息和内核信息; - /usr:存储系统应用软件安装信息; - /var:存储系统日志信息。 - 网络设置,设置主机名称和进行网络激活操作: ![](../images/linux_install_20.png) - 单击`Begin Installation`进行安装: ![](../images/linux_install_21.png) - 安装过程中可以设置`root`用户的密码; ![](../images/linux_install_22.png) - 完成安装后重新启动即可进入系统,第一次启动需要同意协议并完成配置: ![](../images/linux_install_23.png) - 此时宿主机还无法直接访问虚拟机,需要将虚拟机的网络模式改为桥接模式才可以。 ![](../images/linux_install_24.png) ## Xshell使用 > Xshell是一款SSH连接客户端工具,可以用于远程操作Linux系统。Xshell对于家庭和学校可以免费使用,提供一个有效邮箱,下载链接就会发送到你的的邮箱。 - 免费版本申请地址:https://www.netsarang.com/zh/free-for-home-school/ - 下载并安装完成Xshell以后,在Linux系统中打开命令行: ![](../images/linux_install_25.png) - 输入`ifconfig`命令获取IP地址; ![](../images/linux_install_26.png) - 获取完成后通过Xshell进行连接,并输入账号和密码: ![](../images/linux_install_27.png) - 连接完成后即可远程使用Linux系统了。 ![](../images/linux_install_28.png) ## 其他相关 ### 修改默认启动模式 如果不想默认启动图形化界面的话,可以修改默认的启动模式,因为图形化界面还是比较占用内存的,使用命令如下。 ```bash # 将默认级别修改为多用户文本模式 systemctl set-default multi-user.target # 将默认级别修改为图形用户界面模式 systemctl set-default graphical.target # 重启 reboot ``` ### Linux常用命令 可以参考下这个:[开发者必备Linux命令](/reference/linux) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/lombok_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Lombok有啥牛皮的?SpringBoot和IDEA官方都要支持它! > 最近[IDEA 2020最后一个版本发布了](https://mp.weixin.qq.com/s/4eo3hRwItD5ZoFxQKTBpCQ),已经内置了Lombok插件,SpringBoot 2.1.x之后的版本也在Starter中内置了Lombok依赖。为什么他们都要支持Lombok呢?今天我来讲讲Lombok的使用,看看它有何神奇之处! ## Lombok简介 Lombok是一款Java代码功能增强库,在Github上已有9.8k+Star。它会自动集成到你的编辑器和构建工具中,从而使你的Java代码更加生动有趣。通过Lombok的注解,你可以不用再写getter、setter、equals等方法,Lombok将在编译时为你自动生成。 ## Lombok集成 首先我们需要在IDEA中安装好Lombok插件,如果你使用的是最新版IDEA 2020.3,则Lombok插件已经内置,无需安装。 ![](../images/lombok_start_01.png) 之后在项目的pom.xml文件中添加Lombok依赖,SpringBoot 2.1.x版本后无需指定Lombok版本,SpringBoot在`spring-boot-dependencies`中已经内置。 ```xml org.projectlombok lombok true ``` ## Lombok使用 > Lombok中有很多注解,这些注解使得我们可以更加方便的编写Java代码,下面介绍下这些注解的使用。 ### val 使用val注解可以取代任意类型作为局部变量,这样我们就不用写复杂的ArrayList和Map.Entry类型了,具体例子如下。 ```java /** * Created by macro on 2020/12/16. */ public class ValExample { public static void example() { //val代替ArrayList和String类型 val example = new ArrayList(); example.add("Hello World!"); val foo = example.get(0); System.out.println(foo.toLowerCase()); } public static void example2() { //val代替Map.Entry类型 val map = new HashMap(); map.put(0, "zero"); map.put(5, "five"); for (val entry : map.entrySet()) { System.out.printf("%d: %s\n", entry.getKey(), entry.getValue()); } } public static void main(String[] args) { example(); example2(); } } ``` 当我们使用了val注解后,Lombok会从局部变量的初始化表达式推断出具体类型,编译后会生成如下代码。 ```java public class ValExample { public ValExample() { } public static void example() { ArrayList example = new ArrayList(); example.add("Hello World!"); String foo = (String)example.get(0); System.out.println(foo.toLowerCase()); } public static void example2() { HashMap map = new HashMap(); map.put(0, "zero"); map.put(5, "five"); Iterator var1 = map.entrySet().iterator(); while(var1.hasNext()) { Entry entry = (Entry)var1.next(); System.out.printf("%d: %s\n", entry.getKey(), entry.getValue()); } } } ``` ### @NonNull 在方法上使用@NonNull注解可以做非空判断,如果传入空值的话会直接抛出NullPointerException。 ```java /** * Created by macro on 2020/12/16. */ public class NonNullExample { private String name; public NonNullExample(@NonNull String name){ this.name = name; } public static void main(String[] args) { new NonNullExample("test"); //会抛出NullPointerException new NonNullExample(null); } } ``` 编译后会在构造器中添加非空判断,具体代码如下。 ```java public class NonNullExample { private String name; public NonNullExample(@NonNull String name) { if (name == null) { throw new NullPointerException("name is marked non-null but is null"); } else { this.name = name; } } public static void main(String[] args) { new NonNullExample("test"); new NonNullExample((String)null); } } ``` ### @Cleanup 当我们在Java中使用资源时,不可避免地需要在使用后关闭资源。使用@Cleanup注解可以自动关闭资源。 ```java /** * Created by macro on 2020/12/16. */ public class CleanupExample { public static void main(String[] args) throws IOException { String inStr = "Hello World!"; //使用输入输出流自动关闭,无需编写try catch和调用close()方法 @Cleanup ByteArrayInputStream in = new ByteArrayInputStream(inStr.getBytes("UTF-8")); @Cleanup ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] b = new byte[1024]; while (true) { int r = in.read(b); if (r == -1) break; out.write(b, 0, r); } String outStr = out.toString("UTF-8"); System.out.println(outStr); } } ``` 编译后Lombok会生成如下代码。 ```java public class CleanupExample { public CleanupExample() { } public static void main(String[] args) throws IOException { String inStr = "Hello World!"; ByteArrayInputStream in = new ByteArrayInputStream(inStr.getBytes("UTF-8")); try { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { byte[] b = new byte[1024]; while(true) { int r = in.read(b); if (r == -1) { String outStr = out.toString("UTF-8"); System.out.println(outStr); return; } out.write(b, 0, r); } } finally { if (Collections.singletonList(out).get(0) != null) { out.close(); } } } finally { if (Collections.singletonList(in).get(0) != null) { in.close(); } } } } ``` ### @Getter/@Setter 有了@Getter/@Setter注解,我们再也不用编写getter/setter方法了。试想下之前即使我们使用IDEA自动生成getter/setter方法,如果类属性的类型和名称改了,又要重新生成getter/setter方法也是一件很麻烦的事情。 ```java /** * Created by macro on 2020/12/17. */ public class GetterSetterExample { @Getter @Setter private String name; @Getter @Setter(AccessLevel.PROTECTED) private Integer age; public static void main(String[] args) { GetterSetterExample example = new GetterSetterExample(); example.setName("test"); example.setAge(20); System.out.printf("name:%s age:%d",example.getName(),example.getAge()); } } ``` 编译后Lombok会生成如下代码。 ```java public class GetterSetterExample { private String name; private Integer age; public GetterSetterExample() { } public String getName() { return this.name; } public void setName(final String name) { this.name = name; } public Integer getAge() { return this.age; } protected void setAge(final Integer age) { this.age = age; } } ``` ### @ToString 把所有类属性都编写到toString方法中方便打印日志,是一件多么枯燥无味的事情。使用@ToString注解可以自动生成toString方法,默认会包含所有类属性,使用@ToString.Exclude注解可以排除属性的生成。 ```java /** * Created by macro on 2020/12/17. */ @ToString public class ToStringExample { @ToString.Exclude private Long id; private String name; private Integer age; public ToStringExample(Long id,String name,Integer age){ this.id =id; this.name = name; this.age = age; } public static void main(String[] args) { ToStringExample example = new ToStringExample(1L,"test",20); //自动实现toString方法,输出ToStringExample(name=test, age=20) System.out.println(example); } } ``` 编译后Lombok会生成如下代码。 ```java public class ToStringExample { private Long id; private String name; private Integer age; public ToStringExample(Long id, String name, Integer age) { this.id = id; this.name = name; this.age = age; } public String toString() { return "ToStringExample(name=" + this.name + ", age=" + this.age + ")"; } } ``` ### @EqualsAndHashCode 使用@EqualsAndHashCode注解可以自动生成hashCode和equals方法,默认包含所有类属性,使用@EqualsAndHashCode.Exclude可以排除属性的生成。 ```java /** * Created by macro on 2020/12/17. */ @Getter @Setter @EqualsAndHashCode public class EqualsAndHashCodeExample { private Long id; @EqualsAndHashCode.Exclude private String name; @EqualsAndHashCode.Exclude private Integer age; public static void main(String[] args) { EqualsAndHashCodeExample example1 = new EqualsAndHashCodeExample(); example1.setId(1L); example1.setName("test"); example1.setAge(20); EqualsAndHashCodeExample example2 = new EqualsAndHashCodeExample(); example2.setId(1L); //equals方法只对比id,返回true System.out.println(example1.equals(example2)); } } ``` 编译后Lombok会生成如下代码。 ```java public class EqualsAndHashCodeExample { private Long id; private String name; private Integer age; public EqualsAndHashCodeExample() { } public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof EqualsAndHashCodeExample)) { return false; } else { EqualsAndHashCodeExample other = (EqualsAndHashCodeExample)o; if (!other.canEqual(this)) { return false; } else { Object this$id = this.getId(); Object other$id = other.getId(); if (this$id == null) { if (other$id != null) { return false; } } else if (!this$id.equals(other$id)) { return false; } return true; } } } protected boolean canEqual(final Object other) { return other instanceof EqualsAndHashCodeExample; } public int hashCode() { int PRIME = true; int result = 1; Object $id = this.getId(); int result = result * 59 + ($id == null ? 43 : $id.hashCode()); return result; } } ``` ### @XxConstructor 使用@XxConstructor注解可以自动生成构造方法,有@NoArgsConstructor、@RequiredArgsConstructor和@AllArgsConstructor三个注解可以使用。 - @NoArgsConstructor:生成无参构造函数。 - @RequiredArgsConstructor:生成包含必须参数的构造函数,使用@NonNull注解的类属性为必须参数。 - @AllArgsConstructor:生成包含所有参数的构造函数。 ```java /** * Created by macro on 2020/12/17. */ @NoArgsConstructor @RequiredArgsConstructor(staticName = "of") @AllArgsConstructor public class ConstructorExample { @NonNull private Long id; private String name; private Integer age; public static void main(String[] args) { //无参构造器 ConstructorExample example1 = new ConstructorExample(); //全部参数构造器 ConstructorExample example2 = new ConstructorExample(1L,"test",20); //@NonNull注解的必须参数构造器 ConstructorExample example3 = ConstructorExample.of(1L); } } ``` 编译后Lombok会生成如下代码。 ```java public class ConstructorExample { @NonNull private Long id; private String name; private Integer age; public ConstructorExample() { } private ConstructorExample(@NonNull final Long id) { if (id == null) { throw new NullPointerException("id is marked non-null but is null"); } else { this.id = id; } } public static ConstructorExample of(@NonNull final Long id) { return new ConstructorExample(id); } public ConstructorExample(@NonNull final Long id, final String name, final Integer age) { if (id == null) { throw new NullPointerException("id is marked non-null but is null"); } else { this.id = id; this.name = name; this.age = age; } } } ``` ### @Data @Data是一个方便使用的组合注解,是@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstructor的组合体。 ```java /** * Created by macro on 2020/12/17. */ @Data public class DataExample { @NonNull private Long id; @EqualsAndHashCode.Exclude private String name; @EqualsAndHashCode.Exclude private Integer age; public static void main(String[] args) { //@RequiredArgsConstructor已生效 DataExample example1 = new DataExample(1L); //@Getter @Setter已生效 example1.setName("test"); example1.setAge(20); //@ToString已生效 System.out.println(example1); DataExample example2 = new DataExample(1L); //@EqualsAndHashCode已生效 System.out.println(example1.equals(example2)); } } ``` 编译后Lombok会生成如下代码。 ```java public class DataExample { @NonNull private Long id; private String name; private Integer age; public DataExample(@NonNull final Long id) { if (id == null) { throw new NullPointerException("id is marked non-null but is null"); } else { this.id = id; } } @NonNull public Long getId() { return this.id; } public String getName() { return this.name; } public Integer getAge() { return this.age; } public void setId(@NonNull final Long id) { if (id == null) { throw new NullPointerException("id is marked non-null but is null"); } else { this.id = id; } } public void setName(final String name) { this.name = name; } public void setAge(final Integer age) { this.age = age; } public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof DataExample)) { return false; } else { DataExample other = (DataExample)o; if (!other.canEqual(this)) { return false; } else { Object this$id = this.getId(); Object other$id = other.getId(); if (this$id == null) { if (other$id != null) { return false; } } else if (!this$id.equals(other$id)) { return false; } return true; } } } protected boolean canEqual(final Object other) { return other instanceof DataExample; } public int hashCode() { int PRIME = true; int result = 1; Object $id = this.getId(); int result = result * 59 + ($id == null ? 43 : $id.hashCode()); return result; } public String toString() { return "DataExample(id=" + this.getId() + ", name=" + this.getName() + ", age=" + this.getAge() + ")"; } } ``` ### @Value 使用@Value注解可以把类声明为不可变的,声明后此类相当于final类,无法被继承,其属性也会变成final属性。 ```java /** * Created by macro on 2020/12/17. */ @Value public class ValueExample { private Long id; private String name; private Integer age; public static void main(String[] args) { //只能使用全参构造器 ValueExample example = new ValueExample(1L,"test",20); // example.setName("andy") //没有生成setter方法,会报错 // example.name="andy" //字段被设置为final类型,会报错 } } ``` 编译后Lombok会生成如下代码。 ```java public final class ValueExample { private final Long id; private final String name; private final Integer age; public static void main(String[] args) { new ValueExample(1L, "test", 20); } public ValueExample(final Long id, final String name, final Integer age) { this.id = id; this.name = name; this.age = age; } public Long getId() { return this.id; } public String getName() { return this.name; } public Integer getAge() { return this.age; } } ``` ### @Builder 使用@Builder注解可以通过建造者模式来创建对象,建造者模式加链式调用,创建对象太方便了! ```java /** * Created by macro on 2020/12/17. */ @Builder @ToString public class BuilderExample { private Long id; private String name; private Integer age; public static void main(String[] args) { BuilderExample example = BuilderExample.builder() .id(1L) .name("test") .age(20) .build(); System.out.println(example); } } ``` 编译后Lombok会生成如下代码。 ```java public class BuilderExample { private Long id; private String name; private Integer age; BuilderExample(final Long id, final String name, final Integer age) { this.id = id; this.name = name; this.age = age; } public static BuilderExample.BuilderExampleBuilder builder() { return new BuilderExample.BuilderExampleBuilder(); } public String toString() { return "BuilderExample(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")"; } public static class BuilderExampleBuilder { private Long id; private String name; private Integer age; BuilderExampleBuilder() { } public BuilderExample.BuilderExampleBuilder id(final Long id) { this.id = id; return this; } public BuilderExample.BuilderExampleBuilder name(final String name) { this.name = name; return this; } public BuilderExample.BuilderExampleBuilder age(final Integer age) { this.age = age; return this; } public BuilderExample build() { return new BuilderExample(this.id, this.name, this.age); } public String toString() { return "BuilderExample.BuilderExampleBuilder(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")"; } } } ``` ### @SneakyThrows 还在手动捕获并抛出异常?使用@SneakyThrows注解自动实现试试! ```java /** * Created by macro on 2020/12/17. */ public class SneakyThrowsExample { //自动抛出异常,无需处理 @SneakyThrows(UnsupportedEncodingException.class) public static byte[] str2byte(String str){ return str.getBytes("UTF-8"); } public static void main(String[] args) { String str = "Hello World!"; System.out.println(str2byte(str).length); } } ``` 编译后Lombok会生成如下代码。 ```java public class SneakyThrowsExample { public SneakyThrowsExample() { } public static byte[] str2byte(String str) { try { return str.getBytes("UTF-8"); } catch (UnsupportedEncodingException var2) { throw var2; } } } ``` ### @Synchronized 当我们在多个线程中访问同一资源时,往往会出现线程安全问题,以前我们往往使用synchronized关键字修饰方法来实现同步访问。使用@Synchronized注解同样可以实现同步访问。 ```java package com.macro.mall.tiny.example; import lombok.*; /** * Created by macro on 2020/12/17. */ @Data public class SynchronizedExample { @NonNull private Integer count; @Synchronized @SneakyThrows public void reduceCount(Integer id) { if (count > 0) { Thread.sleep(500); count--; System.out.println(String.format("thread-%d count:%d", id, count)); } } public static void main(String[] args) { //添加@Synchronized三个线程可以同步调用reduceCount方法 SynchronizedExample example = new SynchronizedExample(20); new ReduceThread(1, example).start(); new ReduceThread(2, example).start(); new ReduceThread(3, example).start(); } @RequiredArgsConstructor static class ReduceThread extends Thread { @NonNull private Integer id; @NonNull private SynchronizedExample example; @Override public void run() { while (example.getCount() > 0) { example.reduceCount(id); } } } } ``` 编译后Lombok会生成如下代码。 ```java public class SynchronizedExample { private final Object $lock = new Object[0]; @NonNull private Integer count; public void reduceCount(Integer id) { try { synchronized(this.$lock) { if (this.count > 0) { Thread.sleep(500L); Integer var3 = this.count; Integer var4 = this.count = this.count - 1; System.out.println(String.format("thread-%d count:%d", id, this.count)); } } } catch (Throwable var7) { throw var7; } } } ``` ### @With 使用@With注解可以实现对原对象进行克隆,并改变其一个属性,使用时需要指定全参构造方法。 ```java @With @AllArgsConstructor public class WithExample { private Long id; private String name; private Integer age; public static void main(String[] args) { WithExample example1 = new WithExample(1L, "test", 20); WithExample example2 = example1.withAge(22); //将原对象进行clone并设置age,返回false System.out.println(example1.equals(example2)); } } ``` 编译后Lombok会生成如下代码。 ```java public class WithExample { private Long id; private String name; private Integer age; public WithExample withId(final Long id) { return this.id == id ? this : new WithExample(id, this.name, this.age); } public WithExample withName(final String name) { return this.name == name ? this : new WithExample(this.id, name, this.age); } public WithExample withAge(final Integer age) { return this.age == age ? this : new WithExample(this.id, this.name, age); } public WithExample(final Long id, final String name, final Integer age) { this.id = id; this.name = name; this.age = age; } } ``` ### @Getter(lazy=true) 当我们获取某一个属性比较消耗资源时,可以给@Getter添加`lazy=true`属性实现懒加载,会生成Double Check Lock 样板代码对属性进行懒加载。 ```java /** * Created by macro on 2020/12/17. */ public class GetterLazyExample { @Getter(lazy = true) private final double[] cached = expensive(); private double[] expensive() { double[] result = new double[1000000]; for (int i = 0; i < result.length; i++) { result[i] = Math.asin(i); } return result; } public static void main(String[] args) { //使用Double Check Lock 样板代码对属性进行懒加载 GetterLazyExample example = new GetterLazyExample(); System.out.println(example.getCached().length); } } ``` 编译后Lombok会生成如下代码。 ```java public class GetterLazyExample { private final AtomicReference cached = new AtomicReference(); public GetterLazyExample() { } private double[] expensive() { double[] result = new double[1000000]; for(int i = 0; i < result.length; ++i) { result[i] = Math.asin((double)i); } return result; } public double[] getCached() { Object value = this.cached.get(); if (value == null) { synchronized(this.cached) { value = this.cached.get(); if (value == null) { double[] actualValue = this.expensive(); value = actualValue == null ? this.cached : actualValue; this.cached.set(value); } } } return (double[])((double[])(value == this.cached ? null : value)); } } ``` ### @Log 使用@Log注解,可以直接生成日志对象log,通过log对象可以直接打印日志。 ```java /** * Created by macro on 2020/12/17. */ @Log public class LogExample { public static void main(String[] args) { log.info("level info"); log.warning("level warning"); log.severe("level severe"); } } ``` 编译后Lombok会生成如下代码。 ```java public class LogExample { private static final Logger log = Logger.getLogger(LogExample.class.getName()); public LogExample() { } public static void main(String[] args) { log.info("level info"); log.warning("level warning"); log.severe("level severe"); } } ``` ### @Slf4j 使用Lombok生成日志对象时,根据使用日志实现的不同,有多种注解可以使用。比如@Log、@Log4j、@Log4j2、@Slf4j等。 ```java /** * Created by macro on 2020/12/17. */ @Slf4j public class LogSlf4jExample { public static void main(String[] args) { log.info("level:{}","info"); log.warn("level:{}","warn"); log.error("level:{}", "error"); } } ``` 编译后Lombok会生成如下代码。 ```java public class LogSlf4jExample { private static final Logger log = LoggerFactory.getLogger(LogSlf4jExample.class); public LogSlf4jExample() { } public static void main(String[] args) { log.info("level:{}", "info"); log.warn("level:{}", "warn"); log.error("level:{}", "error"); } } ``` ## Lombok原理 如果IDEA不安装Lombok插件的话,我们打开使用Lombok的项目是无法通过编译的。装了以后IDEA才会提示我们Lombok为我们生成的方法和属性。 使用了@Data注解以后,查看类结构可以发现getter、setter、toString等方法。 ![](../images/lombok_start_02.png) 打开target目录下的`.class`文件,我们可以看到Lombok为我们生成的代码,可见Lombok是通过解析注解,然后在编译时生成代码来实现Java代码的功能增强的。 ![](../images/lombok_start_03.png) ## 参考资料 官方文档:https://projectlombok.org/features/all ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-lombok ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/mall_elk_advance.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 你居然还去服务器上捞日志,搭个日志收集系统难道不香么! > ELK日志收集系统进阶使用,本文主要讲解如何打造一个线上环境真实可用的日志收集系统。有了它,你就可以和去服务器上捞日志说再见了! ## ELK环境安装 > ELK是指Elasticsearch、Kibana、Logstash这三种服务搭建的日志收集系统,具体搭建方式可以参考[《SpringBoot应用整合ELK实现日志收集》](https://mp.weixin.qq.com/s/ll_A6ddBaU99LSYmKdttYw)。这里仅提供最新版本的docker-compose脚本和一些安装要点。 ### docker-compose脚本 ```yaml version: '3' services: elasticsearch: image: elasticsearch:6.4.0 container_name: elasticsearch environment: - "cluster.name=elasticsearch" #设置集群名称为elasticsearch - "discovery.type=single-node" #以单一节点模式启动 - "ES_JAVA_OPTS=-Xms512m -Xmx512m" #设置使用jvm内存大小 - TZ=Asia/Shanghai volumes: - /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins #插件文件挂载 - /mydata/elasticsearch/data:/usr/share/elasticsearch/data #数据文件挂载 ports: - 9200:9200 - 9300:9300 kibana: image: kibana:6.4.0 container_name: kibana links: - elasticsearch:es #可以用es这个域名访问elasticsearch服务 depends_on: - elasticsearch #kibana在elasticsearch启动之后再启动 environment: - "elasticsearch.hosts=http://es:9200" #设置访问elasticsearch的地址 - TZ=Asia/Shanghai ports: - 5601:5601 logstash: image: logstash:6.4.0 container_name: logstash environment: - TZ=Asia/Shanghai volumes: - /mydata/logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf #挂载logstash的配置文件 depends_on: - elasticsearch #kibana在elasticsearch启动之后再启动 links: - elasticsearch:es #可以用es这个域名访问elasticsearch服务 ports: - 4560:4560 - 4561:4561 - 4562:4562 - 4563:4563 ``` ### 安装要点 - 使用`docker-compose`命令运行所有服务: ```bash docker-compose up -d ``` - 第一次启动可能会发现Elasticsearch`无法启动`,那是因为`/usr/share/elasticsearch/data`目录没有访问权限,只需要修改`/mydata/elasticsearch/dat`a目录的权限,再重新启动; ```bash chmod 777 /mydata/elasticsearch/data/ ``` - Logstash需要安装`json_lines`插件。 ```bash logstash-plugin install logstash-codec-json_lines ``` ## 分场景收集日志 > 这里为了方便我们查看日志,提出一个分场景收集日志的概念,把日志分为以下四种。 - 调试日志:最全日志,包含了应用中所有`DEBUG`级别以上的日志,仅在开发、测试环境中开启收集; - 错误日志:只包含应用中所有`ERROR`级别的日志,所有环境只都开启收集; - 业务日志:在我们应用`对应包下`打印的日志,可用于查看我们自己在应用中打印的业务日志; - 记录日志:每个接口的`访问记录`,可以用来查看接口执行效率,获取接口访问参数。 ## Logback配置详解 > 要实现上面的分场景收集日志,主要通过Logback的配置来实现,我们先来了解下Logback的配置吧! ### 完全配置 > 在SpringBoot中,如果我们想要自定义Logback的配置,需要自行编写`logback-spring.xml`文件,下面是我们这次要使用的完全配置。 ```xml DEBUG ${FILE_LOG_PATTERN} UTF-8 ${LOG_FILE_PATH}/debug/${APP_NAME}-%d{yyyy-MM-dd}-%i.log ${LOG_FILE_MAX_SIZE:-10MB} ${LOG_FILE_MAX_HISTORY:-30} ERROR ACCEPT DENY ${FILE_LOG_PATTERN} UTF-8 ${LOG_FILE_PATH}/error/${APP_NAME}-%d{yyyy-MM-dd}-%i.log ${LOG_FILE_MAX_SIZE:-10MB} ${LOG_FILE_MAX_HISTORY:-30} DEBUG ${LOG_STASH_HOST}:4560 Asia/Shanghai { "project": "mall-tiny", "level": "%level", "service": "${APP_NAME:-}", "pid": "${PID:-}", "thread": "%thread", "class": "%logger", "message": "%message", "stack_trace": "%exception{20}" } 5 minutes ERROR ACCEPT DENY ${LOG_STASH_HOST}:4561 Asia/Shanghai { "project": "mall-tiny", "level": "%level", "service": "${APP_NAME:-}", "pid": "${PID:-}", "thread": "%thread", "class": "%logger", "message": "%message", "stack_trace": "%exception{20}" } 5 minutes ${LOG_STASH_HOST}:4562 Asia/Shanghai { "project": "mall-tiny", "level": "%level", "service": "${APP_NAME:-}", "pid": "${PID:-}", "thread": "%thread", "class": "%logger", "message": "%message", "stack_trace": "%exception{20}" } 5 minutes ${LOG_STASH_HOST}:4563 Asia/Shanghai { "project": "mall-tiny", "level": "%level", "service": "${APP_NAME:-}", "class": "%logger", "message": "%message" } 5 minutes ``` ### 配置要点解析 #### 使用默认的日志配置 > 一般我们不需要自定义控制台输出,可以采用默认配置,具体配置参考`console-appender.xml`,该文件在`spring-boot-${version}.jar`下面。 ```xml ``` #### springProperty > 该标签可以从SpringBoot的配置文件中获取配置属性,比如说在不同环境下我们的Logstash服务地址是不一样的,我们就可以把该地址定义在`application.yml`来使用。 例如在`application-dev.yml`中定义了这些属性: ```yaml logstash: host: localhost ``` 在`logback-spring.xml`中就可以直接这样使用: ```xml ``` #### filter > 在Logback中有两种不同的过滤器,用来过滤日志输出。 ThresholdFilter:临界值过滤器,过滤掉低于指定临界值的日志,比如下面的配置将过滤掉所有低于INFO级别的日志。 ```xml INFO ``` LevelFilter:级别过滤器,根据日志级别进行过滤,比如下面的配置将过滤掉所有非ERROR级别的日志。 ```xml ERROR ACCEPT DENY ``` #### appender > Appender可以用来控制日志的输出形式,主要有下面三种。 - ConsoleAppender:控制日志输出到控制台的形式,比如在`console-appender.xml`中定义的默认控制台输出。 ```xml ${CONSOLE_LOG_PATTERN} ``` - RollingFileAppender:控制日志输出到文件的形式,可以控制日志文件生成策略,比如文件名称格式、超过多大重新生成文件以及删除超过多少天的文件。 ```xml ${LOG_FILE_PATH}/error/${APP_NAME}-%d{yyyy-MM-dd}-%i.log ${LOG_FILE_MAX_SIZE:-10MB} ${LOG_FILE_MAX_HISTORY:-30} ``` - LogstashTcpSocketAppender:控制日志输出到Logstash的形式,可以用来配置Logstash的地址、访问策略以及日志的格式。 ```xml ${LOG_STASH_HOST}:4561 Asia/Shanghai { "project": "mall-tiny", "level": "%level", "service": "${APP_NAME:-}", "pid": "${PID:-}", "thread": "%thread", "class": "%logger", "message": "%message", "stack_trace": "%exception{20}" } 5 minutes ``` #### logger > 只有配置到logger节点上的appender才会被使用,logger用于配置哪种条件下的日志被打印,root是一种特殊的appender,下面介绍下日志划分的条件。 - 调试日志:所有的DEBUG级别以上日志; - 错误日志:所有的ERROR级别日志; - 业务日志:`com.macro.mall`包下的所有DEBUG级别以上日志; - 记录日志:`com.macro.mall.tiny.component.WebLogAspect`类下所有DEBUG级别以上日志,该类是统计接口访问信息的AOP切面类。 #### 控制框架输出日志 > 还有一些使用框架内部的日志,DEBUG级别的日志对我们并没有啥用处,都可以设置为了INFO以上级别。 ```xml ``` ## Logstash配置详解 > 接下来我们需要配置下Logstash,让它可以分场景收集不同的日志,下面详细介绍下使用到的配置。 ### 完全配置 ``` input { tcp { mode => "server" host => "0.0.0.0" port => 4560 codec => json_lines type => "debug" } tcp { mode => "server" host => "0.0.0.0" port => 4561 codec => json_lines type => "error" } tcp { mode => "server" host => "0.0.0.0" port => 4562 codec => json_lines type => "business" } tcp { mode => "server" host => "0.0.0.0" port => 4563 codec => json_lines type => "record" } } filter{ if [type] == "record" { mutate { remove_field => "port" remove_field => "host" remove_field => "@version" } json { source => "message" remove_field => ["message"] } } } output { elasticsearch { hosts => ["es:9200"] action => "index" codec => json index => "mall-tiny-%{type}-%{+YYYY.MM.dd}" template_name => "mall-tiny" } } ``` ### 配置要点 - input:使用不同端口收集不同类型的日志,从4560~4563开启四个端口; - filter:对于记录类型的日志,直接将JSON格式的message转化到source中去,便于搜索查看; - output:按类型、时间自定义索引格式。 ## SpringBoot配置 > 在SpringBoot中的配置可以直接用来覆盖Logback中的配置,比如`logging.level.root`就可以覆盖``节点中的`level`配置。 - 开发环境配置:application-dev.yml ```yaml logstash: host: localhost logging: level: root: debug ``` - 测试环境配置:application-test.yml ```yaml logstash: host: 192.168.3.101 logging: level: root: debug ``` - 生产环境配置:application-prod.yml ```yaml logstash: host: logstash-prod logging: level: root: info ``` ## Kibana进阶使用 > 进过上面ELK环境的搭建和配置以后,我们的日志收集系统终于可以用起来了,下面介绍下在Kibana中的使用技巧! - 首先启动我们的测试Demo,然后通用调用接口(可以使用Swagger),产生一些日志信息; ![](../images/elk_advance_01.png) - 调用完成后在`Management->Kibana->Index Patterns`中可以创建`Index Patterns`,Kibana服务访问地址:http://192.168.3.101:5601 ![](../images/elk_advance_02.png) - 创建完成后可以在`Discover`中查看所有日志,调试日志只需直接查看`mall-tiny-debug*`模式的日志即可; ![](../images/elk_advance_03.png) - 对于日志搜索,kibana有非常强大的提示功能,可以通过搜索栏右侧的`Options`按钮打开; ![](../images/elk_advance_04.png) - 记录日志只需直接查看`mall-tiny-record*`模式的日志即可,如果我们想要搜索uri为`/brand/listAll`的记录日志,只需在搜索栏中输入`uri : "/brand/listAll"`; ![](../images/elk_advance_05.png) - 错误日志,只需直接查看`mall-tiny-error*`模式的日志即可; ![](../images/elk_advance_06.png) - 业务日志,只需直接查看`mall-tiny-business*`模式的日志即可,这里我们可以查看一些SQL日志的输出; ![](../images/elk_advance_07.png) - 如果日志太大了,可以通过`Elasticsearch->Index Management`选择删除即可。 ![](../images/elk_advance_08.png) ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-log ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/maven_docker_fabric8.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 还在手动部署SpringBoot应用?试试这个自动化插件! > 最近又发现了一款好用的Maven插件,fabric8io出品的`docker-maven-plugin`,可以把SpringBoot应用方便的部署到Docker容器中去。该插件可以实现打包镜像、推送到镜像仓库、运行应用等一系列操作,本文将对其用法进行详细介绍,希望对大家有所帮助! ## 安装私有镜像仓库 > 由于之后我们需要推送到私有镜像仓库,我们预先安装好,使用的是Docker公司开发的私有镜像仓库Registry。 - 下载Registry的Docker镜像; ```bash docker pull registry:2 ``` - 使用Docker容器运行Registry服务,需要添加环境变量`REGISTRY_STORAGE_DELETE_ENABLED=true`开启删除镜像的功能; ```bash docker run -p 5000:5000 --name registry2 \ --restart=always \ -e REGISTRY_STORAGE_DELETE_ENABLED="true" \ -d registry:2 ``` - 修改Docker Daemon的配置文件,文件位置为`/etc/docker/daemon.json`,由于Docker默认使用HTTPS推送镜像,而我们的镜像仓库没有支持,所以需要添加如下配置,改为使用HTTP推送; ```json { "insecure-registries": ["192.168.3.101:5000"] } ``` - 最后使用如下命令重启Docker服务。 ```bash systemctl daemon-reload && systemctl restart docker ``` ## 镜像仓库可视化 > 由于私有镜像仓库管理比较麻烦,而`docker-registry-ui`有专门的页面可以方便地管理镜像,所以我们安装它来管理私有镜像仓库。 - 下载`docker-registry-ui`的Docker镜像; ```bash docker pull joxit/docker-registry-ui:static ``` - 使用Docker容器运行`docker-registry-ui`服务; ```bash docker run -p 8280:80 --name registry-ui \ --link registry2:registry2 \ -e REGISTRY_URL="http://registry2:5000" \ -e DELETE_IMAGES="true" \ -e REGISTRY_TITLE="Registry2" \ -d joxit/docker-registry-ui:static ``` - 我们先来试试私有镜像仓库是否可用,首先下载一个测试用的镜像`busybox`; ```bash docker pull busybox ``` - 给镜像`busybox`打上私有仓库的标签,并设置版本为`v1.0`; ```bash docker tag busybox 192.168.3.101:5000/busybox:v1.0 ``` - 之后推送到私有镜像仓库去; ```bash docker push 192.168.3.101:5000/busybox:v1.0 ``` - 访问`docker-registry-ui`管理界面,即可查看到`busybox`镜像,地址:http://192.168.3.101:8280 ![](../images/maven_fabric8_start_01.png) ## 插件使用 > fabric8io出品的`docker-maven-plugin`是一款集Docker镜像管理和容器管理于一身的插件,动动手指就可以把我们的SpringBoot应用部署到Docker容器中了,非常好用,下面来讲讲它的用法。 ### 在IDEA中正确使用Maven插件 - 一般我们如果没有使用IDEA,都是手敲Maven命令来执行,在IDEA中我们只要双击右侧面板中的Maven命令即可执行,非常方便。 ![](../images/maven_fabric8_start_02.png) - 如果你想使用自定义命令的话,可以使用`Execute Maven Goal`这个功能,这里我使用的是`mvn clean package`命令。 ![](../images/maven_fabric8_start_03.png) ### 构建镜像 - 要想使用`docker-maven-plugin`,需要在`pom.xml`中添加该插件; ```xml io.fabric8 docker-maven-plugin 0.33.0 http://192.168.3.101:2375 http://192.168.3.101:5000 192.168.3.101:5000/mall-tiny/${project.name}:${project.version} java:8 ${project.build.finalName}.jar / artifact ["java", "-jar","/${project.build.finalName}.jar"] macrozheng ``` - 我们构建镜像之前需要先将项目打包,然后再构建,否则会出错,直接使用如下命令即可; ```bash mvn package docker:build ``` - 打包完成后就可以在我们的服务器上看到这个镜像了; ```bash [root@linux-local mydata]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE 192.168.3.101:5000/mall-tiny/mall-tiny-fabric 0.0.1-SNAPSHOT 6b8bc6faeb0b 9 seconds ago 680MB ``` - 当然我们也可以设置使用`package`命令时直接打包镜像,修改`pom.xml`,在``节点下添加``配置即可; ```xml io.fabric8 docker-maven-plugin 0.33.0 build-image package build ``` - 使用不同的Maven插件构建Docker镜像时方法往往不同,这时候直接使用`Dockerfile`来构建会比较好,我们先写好Dockerfile文件并放到项目根目录下; ```dockerfile # 该镜像需要依赖的基础镜像 FROM java:8 # 将当前maven目录生成的文件复制到docker容器的/目录下 COPY maven / # 声明服务运行在8080端口 EXPOSE 8080 # 指定docker容器启动时运行jar包 ENTRYPOINT ["java", "-jar","/mall-tiny-fabric-0.0.1-SNAPSHOT.jar"] # 指定维护者的名字 MAINTAINER macrozheng ``` - 然后修改`pom.xml`文件,将``节点配置替换为如下内容,仅需配置Dockerfile所在目录即可。 ```xml ${project.basedir} ``` ### 推送到镜像仓库 - 接下来我们使用`docker:push`命令即可把镜像推送到私有镜像仓库; ```bash mvn docker:push ``` - 之后在我们的私有镜像仓库就可以看到镜像了; ![](../images/maven_fabric8_start_04.png) ### 操作容器 - `docker-maven-plugin`不仅可以操作镜像,还可以操作容器,比如我们以前需要使用如下Docker命令来运行容器; ```bash docker run -p 8080:8080 --name mall-tiny-fabric \ --link mysql:db \ -v /etc/localtime:/etc/localtime \ -v /mydata/app/mall-tiny-fabric/logs:/var/logs \ -d 192.168.3.101:5000/mall-tiny/mall-tiny-fabric:0.0.1-SNAPSHOT ``` - 现在我们只需在插件中配置即可,在``节点下添加``节点可以定义容器启动的行为: ```xml ${project.artifactId} 8080:8080 mysql:db /etc/localtime:/etc/localtime /mydata/app/${project.artifactId}/logs:/var/logs ``` - 之后直接使用`docker:start`命令即可启动了; ```bash mvn docker:start ``` ```bash [root@linux-local mydata]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 95ce77c0394b 192.168.3.101:5000/mall-tiny/mall-tiny-fabric:0.0.1-SNAPSHOT "java -jar /mall-tin…" 32 seconds ago Up 31 seconds 0.0.0.0:8080->8080/tcp mall-tiny-fabric ``` - 停止容器使用`docker:stop`命令即可; ```bash mvn docker:stop ``` - 删除容器使用`docker:remove`命令,是不是很方便! ```bash mvn docker:remove ``` ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-fabric ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/minio.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Github标星19K+Star,10分钟自建对象存储服务! > 对象存储服务可以用来存储各类文件,`mall`项目中的图片存储采用的是OSS,今天我们来讲下如何自己搭建一个对象存储服务来存储图片。 ## MinIO简介 MinIO 是一款基于Go语言的高性能对象存储服务,在Github上已有19K+Star。它采用了Apache License v2.0开源协议,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等。 本文将使用 MinIO 来自建一个对象存储服务用于存储图片。 ## 安装及部署 > MinIO的安装方式有很多,这里我们使用它在Docker环境下的安装方式。 - 下载MinIO的Docker镜像: ```bash docker pull minio/minio ``` - 在Docker容器中运行MinIO,这里我们将MiniIO的数据和配置文件夹挂在到宿主机上: ```bash docker run -p 9090:9000 --name minio \ -v /mydata/minio/data:/data \ -v /mydata/minio/config:/root/.minio \ -d minio/minio server /data ``` - 运行成功后,访问该地址来登录并使用MinIO,默认Access Key和Secret都是`minioadmin`:http://192.168.6.132:9090 ![](../images/minio_use_01.png) ## 上传文件及使用 > 通过使用MinIO的网页端即可完成文件的上传下载功能,下面我们以图片上传下载为例来演示下该功能。 - 在存储文件之前,我们需要新建一个存储桶: ![](../images/minio_use_02.png) - 存储桶创建完成后,通过上传按钮可以上传文件,这里我们上传一张图片: ![](../images/minio_use_03.png) - 图片上传完成后,我们可以通过拷贝链接按钮来获取图片访问路径,但是这只是个临时的访问路径: ![](../images/minio_use_04.png) - 要想获取一个永久的访问路径,需要修改存储桶的访问策略,我们可以点击存储桶右上角的编辑策略按钮来修改访问策略; ![](../images/minio_use_05.png) - 这里有三种访问策略可以选择,一种只读、一种只写、一种可读可写,这里我们选择只读即可,但是需要注意的是,访问前缀需要设置为`*.*`,否则会无法访问; ![](../images/minio_use_06.png) - 设置完成后,我们只需要通过拷贝链接中的前一串路径即可永久访问该文件; ![](../images/minio_use_07.png) - 访问图片效果展示: ![](../images/minio_use_08.png) ## MinIO客户端的使用 > 虽然MinIO的网页端管理已经很方便了,但是官网还是给我们提供了基于命令行的客户端MinIO Client(简称mc),下面我们来讲讲它的使用方法。 ### 常用命令 > 下面我们先来熟悉下mc的命令,这些命令和Linux中的命令有很多相似之处。 | 命令 | 作用 | | ------- | --------------------------------------------- | | ls | 列出文件和文件夹 | | mb | 创建一个存储桶或一个文件夹 | | cat | 显示文件和对象内容 | | pipe | 将一个STDIN重定向到一个对象或者文件或者STDOUT | | share | 生成用于共享的URL | | cp | 拷贝文件和对象 | | mirror | 给存储桶和文件夹做镜像 | | find | 基于参数查找文件 | | diff | 对两个文件夹或者存储桶比较差异 | | rm | 删除文件和对象 | | events | 管理对象通知 | | watch | 监听文件和对象的事件 | | policy | 管理访问策略 | | session | 为cp命令管理保存的会话 | | config | 管理mc配置文件 | | update | 检查软件更新 | | version | 输出版本信息 | ### 安装及配置 > 由于MinIO服务端中并没有自带客户端,所以我们需要安装配置完客户端后才能使用,这里以Docker环境下的安装为例。 - 下载MinIO Client 的Docker镜像: ```bash docker pull minio/mc ``` - 在Docker容器中运行mc: ```bash docker run -it --entrypoint=/bin/sh minio/mc ``` - 运行完成后我们需要进行配置,将我们自己的MinIO服务配置到客户端上去,配置的格式如下: ```bash mc config host add ``` - 对于我们的MinIO服务可以这样配置: ```bash mc config host add minio http://192.168.6.132:9090 minioadmin minioadmin S3v4 ``` ### 常用操作 - 查看存储桶和查看存储桶中存在的文件: ```bash # 查看存储桶 mc ls minio # 查看存储桶中存在的文件 mc ls minio/blog ``` ![](../images/minio_use_09.png) - 创建一个名为`test`的存储桶: ```bash mc mb minio/test ``` ![](../images/minio_use_10.png) - 共享`avatar.png`文件的下载路径: ```bash mc share download minio/blog/avatar.png ``` ![](../images/minio_use_11.png) - 查找`blog`存储桶中的png文件: ```bash mc find minio/blog --name "*.png" ``` ![](../images/minio_use_12.png) - 设置`test`存储桶的访问权限为`只读`: ```bash # 目前可以设置这四种权限:none, download, upload, public mc policy set download minio/test/ # 查看存储桶当前权限 mc policy list minio/test/ ``` ![](../images/minio_use_13.png) ## 参考资料 详细了解MinIO可以参考官方文档:https://docs.min.io/cn/minio-quickstart-guide.html ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/mongodb_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # MongoDB快速入门,掌握这些刚刚好! > 虽说现在关系型数据库还是主流,但是面对某些需求的时候,需要非关系型数据库来补充它,学习一个主流的NoSQL数据库还是很有必要的。MongoDB是一个功能丰富的NoSQL数据库,本文整理了它最常用的部分形成了这篇入门教程,希望对大家有所帮助。 ## 简介 MongoDB是一个基于分布式文件存储的数据库。由C++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个介于关系型数据库和非关系型数据库之间的产品,是非关系型数据库当中功能最丰富,最像关系型数据库的。 ## 安装 > 以前写的MongoDB安装教程是基于3.2版本的,发现有的朋友使用新版本安装有问题,这次我们使用最新版本再来安装一次,本文所使用的MongoDB版本为4.2.5,总的来说,新版本的安装更简单了。 ### Windows下的安装 - 下载MongoDB安装包,选择`Windows x64`版本安装,下载地址:https://www.mongodb.com/download-center/community ![](../images/mongodb_start_01.png) - 运行MongoDB安装包并选择自定义安装,设置好安装路径; ![](../images/mongodb_start_02.png) - 配置MongoDB,让MongoDB作为服务运行,并配置好数据目录和日志目录; ![](../images/mongodb_start_03.png) - 取消MongoDB Compass的安装选项(不取消安装极慢),需要可自行安装; ![](../images/mongodb_start_04.png) - 双击`mongo.exe`可以运行MongoDB自带客户端,操作MongoDB; ![](../images/mongodb_start_05.png) - 连接成功后会显示如下信息; ![](../images/mongodb_start_06.png) - 如果需要移除MongoDB服务,只需使用管理员权限运行`cmd`工具,并输入如下命令。 ```bash sc.exe delete MongoDB ``` ### Linux下的安装 - 下载MongoDB的Docker镜像; ```bash docker pull mongo:4.2.5 ``` - 使用Docker命令启动MongoDB服务; ```bash docker run -p 27017:27017 --name mongo \ -v /mydata/mongo/db:/data/db \ -d mongo:4.2.5 ``` - 有时候我们需要为MongoDB设置账号,可以使用如下命令启动; ```bash docker run -p 27017:27017 --name mongo \ -v /mydata/mongo/db:/data/db \ -d mongo:4.2.5 --auth ``` - 然后我们需要进入容器中的MongoDB客户端; ```bash docker exec -it mongo mongo ``` - 之后在`admin`集合中创建一个账号用于连接,这里创建的是基于`root`角色的超级管理员帐号; ``` use admin db.createUser({ user: 'mongoadmin', pwd: 'secret', roles: [ { role: "root", db: "admin" } ] }); ``` - 创建完成后验证是否可以登录; ``` db.auth("mongoadmin","secret") ``` - 整个账号创建过程可以参考下图。 ![](../images/mongodb_start_11.png) ### 客户端工具 > MongoDB的客户端工具有很多,上面没安装的MongoDB Compass就是其中之一,另外Navicat 15版本也有MongoDB的管理功能。这里我们使用的是一款免费的客户端工具Robo 3T(以前叫Robomongo)。 - 首先下载客户端工具,下载地址:https://robomongo.org/download ![](../images/mongodb_start_07.png) - 下载完成后解压,双击`robo3t.exe`即可使用; ![](../images/mongodb_start_08.png) - 之后创建一个到MongoDB的连接; ![](../images/mongodb_start_09.png) - 创建连接成功以后,就可以操作MongoDB了。 ![](../images/mongodb_start_10.png) ## 相关概念 > MongoDB是非关系型数据库当中最像关系型数据库的,所以我们通过它与关系型数据库的对比,来了解下它的概念。 | SQL概念 | MongoDB概念 | 解释/说明 | | :---------- | :---------- | :---------------------------------- | | database | database | 数据库 | | table | collection | 数据库表/集合 | | row | document | 数据记录行/文档 | | column | field | 数据字段/域 | | index | index | 索引 | | primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 | ## 数据库操作 - 创建数据库,使用`use`命令去创建数据库,当插入第一条数据时会创建数据库,例如创建一个`test`数据库; ``` > use test switched to db test > db.article.insert({name:"MongoDB 教程"}) WriteResult({ "nInserted" : 1 }) > show dbs admin 0.000GB config 0.000GB local 0.000GB test 0.000GB ``` - 删除数据库,使用db对象中的`dropDatabase()`方法来删除; ``` > db.dropDatabase() { "dropped" : "test", "ok" : 1 } > show dbs admin 0.000GB config 0.000GB local 0.000GB ``` ## 集合操作 - 创建集合,使用db对象中的`createCollection()`方法来创建集合,例如创建一个`article`集合; ``` > use test switched to db test > db.createCollection("article") { "ok" : 1 } > show collections article ``` - 删除集合,使用collection对象的`drop()`方法来删除集合,例如删除一个`article`集合; ``` > db.article.drop() true > show collections ``` ## 文档操作 > 上面的数据库和集合操作是在MongoDB的客户端中进行的,下面的文档操作都是在Robomongo中进行的。 ### 插入文档 - MongoDB通过collection对象的`insert()`方法向集合中插入文档,语法如下; ``` db.collection.insert(document) ``` - 使用collection对象的`insert()`方法来插入文档,例如插入一个`article`文档; ``` db.article.insert({title: 'MongoDB 教程', description: 'MongoDB 是一个 Nosql 数据库', by: 'Andy', url: 'https://www.mongodb.com/', tags: ['mongodb', 'database', 'NoSQL'], likes: 100 }) ``` - 使用collection对象的`find()`方法可以获取文档,例如获取所有的`article`文档; ``` db.article.find({}) ``` ``` { "_id" : ObjectId("5e9943661379a112845e4056"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "Andy", "url" : "https://www.mongodb.com/", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100.0 } ``` ### 更新文档 - MongoDB通过collection对象的`update()`来更新集合中的文档,语法如下; ``` db.collection.update( , , { multi: } ) # query:修改的查询条件,类似于SQL中的WHERE部分 # update:更新属性的操作符,类似与SQL中的SET部分 # multi:设置为true时会更新所有符合条件的文档,默认为false只更新找到的第一条 ``` - 将title为`MongoDB 教程`的所有文档的title修改为`MongoDB`; ``` db.article.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}},{multi:true}) ``` - 除了`update()`方法以外,`save()`方法可以用来替换已有文档,语法如下; ``` db.collection.save(document) ``` - 这次我们将ObjectId为`5e9943661379a112845e4056`的文档的title改为`MongoDB 教程`; ``` db.article.save({ "_id" : ObjectId("5e9943661379a112845e4056"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "Andy", "url" : "https://www.mongodb.com/", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100.0 }) ``` ### 删除文档 - MongoDB通过collection对象的`remove()`方法来删除集合中的文档,语法如下; ``` db.collection.remove( , { justOne: } ) # query:删除的查询条件,类似于SQL中的WHERE部分 # justOne:设置为true只删除一条记录,默认为false删除所有记录 ``` - 删除title为`MongoDB 教程`的所有文档; ``` db.article.remove({'title':'MongoDB 教程'}) ``` ### 查询文档 - MongoDB通过collection对象的`find()`方法来查询文档,语法如下; ``` db.collection.find(query, projection) # query:查询条件,类似于SQL中的WHERE部分 # projection:可选,使用投影操作符指定返回的键 ``` - 查询`article`集合中的所有文档; ``` db.article.find() ``` ``` /* 1 */ { "_id" : ObjectId("5e994dcb1379a112845e4057"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "Andy", "url" : "https://www.mongodb.com/", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 50.0 } /* 2 */ { "_id" : ObjectId("5e994df51379a112845e4058"), "title" : "Elasticsearch 教程", "description" : "Elasticsearch 是一个搜索引擎", "by" : "Ruby", "url" : "https://www.elastic.co/cn/", "tags" : [ "elasticearch", "database", "NoSQL" ], "likes" : 100.0 } /* 3 */ { "_id" : ObjectId("5e994e111379a112845e4059"), "title" : "Redis 教程", "description" : "Redis 是一个key-value数据库", "by" : "Andy", "url" : "https://redis.io/", "tags" : [ "redis", "database", "NoSQL" ], "likes" : 150.0 } ``` - MongoDB中的条件操作符,通过与SQL语句的对比来了解下; | 操作 | 格式 | SQL中的类似语句 | | :--------- | :----------------------- | :----------------------------- | | 等于 | `{:}` | `where title = 'MongoDB 教程'` | | 小于 | `{:{$lt:}}` | `where likes < 50` | | 小于或等于 | `{:{$lte:}}` | `where likes <= 50` | | 大于 | `{:{$gt:}}` | `where likes > 50` | | 大于或等于 | `{:{$gte:}}` | `where likes >= 50` | | 不等于 | `{:{$ne:}}` | `where likes != 50` | - 条件查询,查询title为`MongoDB 教程`的所有文档; ``` db.article.find({'title':'MongoDB 教程'}) ``` - 条件查询,查询likes大于50的所有文档; ``` db.article.find({'likes':{$gt:50}}) ``` - AND条件可以通过在`find()`方法传入多个键,以逗号隔开来实现,例如查询title为`MongoDB 教程`并且by为`Andy`的所有文档; ``` db.article.find({'title':'MongoDB 教程','by':'Andy'}) ``` - OR条件可以通过使用`$or`操作符实现,例如查询title为`Redis 教程`或`MongoDB 教程`的所有文档; ``` db.article.find({$or:[{"title":"Redis 教程"},{"title": "MongoDB 教程"}]}) ``` - AND 和 OR条件的联合使用,例如查询likes大于50,并且title为`Redis 教程`或者`"MongoDB 教程`的所有文档。 ``` db.article.find({"likes": {$gt:50}, $or: [{"title": "Redis 教程"},{"title": "MongoDB 教程"}]}) ``` ## 其他操作 ### Limit与Skip操作 - 读取指定数量的文档,可以使用`limit()`方法,语法如下; ``` db.collection.find().limit(NUMBER) ``` - 只查询article集合中的2条数据; ``` db.article.find().limit(2) ``` - 跳过指定数量的文档来读取,可以使用`skip()`方法,语法如下; ``` db.collection.find().limit(NUMBER).skip(NUMBER) ``` - 从第二条开始,查询article集合中的2条数据; ``` db.article.find().limit(2).skip(1) ``` ### 排序 - 在MongoDB中使用`sort()`方法对数据进行排序,`sort()`方法通过参数来指定排序的字段,并使用1和-1来指定排序方式,1为升序,-1为降序; ``` db.collection.find().sort({KEY:1}) ``` - 按article集合中文档的likes字段降序排列; ``` db.article.find().sort({likes:-1}) ``` ### 索引 - 索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。 - MongoDB使用`createIndex()`方法来创建索引,语法如下; ``` db.collection.createIndex(keys, options) # background:建索引过程会阻塞其它数据库操作,设置为true表示后台创建,默认为false # unique:设置为true表示创建唯一索引 # name:指定索引名称,如果没有指定会自动生成 ``` - 给title和description字段创建索引,1表示升序索引,-1表示降序索引,指定以后台方式创建; ``` db.article.createIndex({"title":1,"description":-1}, {background: true}) ``` - 查看article集合中已经创建的索引; ``` db.article.getIndexes() ``` ```` /* 1 */ [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.article" }, { "v" : 2, "key" : { "title" : 1.0, "description" : -1.0 }, "name" : "title_1_description_-1", "ns" : "test.article", "background" : true } ] ```` ### 聚合 - MongoDB中的聚合使用`aggregate()`方法,类似于SQL中的group by语句,语法如下; ``` db.collection.aggregate(AGGREGATE_OPERATION) ``` - 聚合中常用操作符如下; | 操作符 | 描述 | | :----- | :--------- | | $sum | 计算总和 | | $avg | 计算平均值 | | $min | 计算最小值 | | $max | 计算最大值 | - 根据by字段聚合文档并计算文档数量,类似与SQL中的count()函数; ``` db.article.aggregate([{$group : {_id : "$by", sum_count : {$sum : 1}}}]) ``` ``` /* 1 */ { "_id" : "Andy", "sum_count" : 2.0 } /* 2 */ { "_id" : "Ruby", "sum_count" : 1.0 } ``` - 根据by字段聚合文档并计算likes字段的平局值,类似与SQL中的avg()语句; ``` db.article.aggregate([{$group : {_id : "$by", avg_likes : {$avg : "$likes"}}}]) ``` ``` /* 1 */ { "_id" : "Andy", "avg_likes" : 100.0 } /* 2 */ { "_id" : "Ruby", "avg_likes" : 100.0 } ``` ### 正则表达式 - MongoDB使用`$regex`操作符来设置匹配字符串的正则表达式,可以用来模糊查询,类似于SQL中的like操作; - 例如查询title中包含`教程`的文档; ``` db.article.find({title:{$regex:"教程"}}) ``` - 不区分大小写的模糊查询,使用`$options`操作符; ``` db.article.find({title:{$regex:"elasticsearch",$options:"$i"}}) ``` ## 结合SpringBoot使用 具体参考:[《mall整合Mongodb实现文档操作》](https://mp.weixin.qq.com/s/YZUnpp3QRHGKyuvN7UnWNw) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/my_debug_skill.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 被我用烂的DEBUG调试技巧,专治各种搜索不到的问题! > 在开发过程中,遇到问题,我们经常会使用搜索引擎来查找问题的解决方案,然后予以解决。但是有些问题一时半会搜索不到解决方案,需要自己去解决。这里分享下我解决这些问题使用的调试技巧,给大家一个解决问题的新思路! ## 问题描述 在[《我扒了半天源码,终于找到了Oauth2自定义处理结果的最佳方案!》](https://mp.weixin.qq.com/s/f1fDd98nnMJRexyJ3bydiw)一文中,当JWT令牌过期或者签名不正确时,我们想要自定义网关认证失败的返回结果。这个问题解决起来很简单,只需修改一行代码即可。但是当时查找解决方案确实花费了一番功夫,通过DEBUG源码才找到了Spring Security中提供的自定义配置,解决了该问题。下面讲讲我是如何通过DEBUG源码找到这个解决方案的! ## 解决过程 - 首先我们需要找到一个切入点,既然问题是由于JWT令牌过期或者签名不正确才产生的,我们很容易想到`RSASSAVerifier`这个关键类,它的`verify()`方法是用来验证签名是否正确的,我们可以在该方法上面打个断点DEBUG一下,发现程序执行过程果然会经过这里,要是签名不正确会直接返回false; ![](../images/my_debug_skill_01.png) - 这时候我们可以查下堆栈信息,了解下这次调用的整个过程,可以看到红框以下的调用都是WebFlux里面的调用,没有参考意义,所以调用最早是从`NimbusReactiveJwtDecoder`类开始的; ![](../images/my_debug_skill_02.png) - 我们搜索下`NimbusReactiveJwtDecoder`在哪里被使用到了,可以找到又一个关键类`ServerHttpSecurity`,我们在网关的安全配置`ResourceServerConfig`中曾经用到过它,猜想下如果Spring Security提供了自定义配置,那估计就在这个类里面了; ![](../images/my_debug_skill_03.png) - 查看下`ServerHttpSecurity`的类注释,我们可以发现它相当于WebFlux版本的Spring Security配置; ```java /** * A {@link ServerHttpSecurity} is similar to Spring Security's {@code HttpSecurity} but for WebFlux. * It allows configuring web based security for specific http requests. By default it will be applied * to all requests, but can be restricted using {@link #securityMatcher(ServerWebExchangeMatcher)} or * other similar methods. **/ ``` - 在我们网关的`ResourceServerConfig`中,我们曾经调用过`ServerHttpSecurity`的`build()`方法,用于生成`SecurityWebFilterChain`; ![](../images/my_debug_skill_04.png) - 让我们看看这个`build()`方法干了点啥,其中有段比较关键的是它调用了`OAuth2ResourceServerSpec`类的`configure()`方法; ![](../images/my_debug_skill_05.png) - 而`OAuth2ResourceServerSpec`类的`configure()`方法又调用了`JwtSpec`类的`configure()`方法; ![](../images/my_debug_skill_06.png) - 这个`JwtSpec`对象是不会为空的,因为我们在`ResourceServerConfig`中调用了`OAuth2ResourceServerSpec`类的`jwt()`方法创建了它; ![](../images/my_debug_skill_07.png) - `JwtSpec`类的`configure`方法很关键,使用过滤器来进行认证是Spring Security实现认证的老套路了,于是我们找到了默认的认证过滤器`BearerTokenAuthenticationWebFilter`; ![](../images/my_debug_skill_08.png) - `BearerTokenAuthenticationWebFilter`使用了`OAuth2ResourceServerSpec`中的`entryPoint`来处理认证失败,默认实现为`BearerTokenServerAuthenticationEntryPoint`; ![](../images/my_debug_skill_09.png) - 之后我们在`BearerTokenAuthenticationWebFilter`的`filter()`方法,`BearerTokenServerAuthenticationEntryPoint`的`commence()`方法上分别打个断点,来验证下,调用过程中都经过了,完全正确; ![](../images/my_debug_skill_10.png) - 也就是说我们只要把默认的认证失败处理器换成我们自定义的就行了,直接通过如下代码把`OAuth2ResourceServerSpec`中的`entryPoint`来设置成自定义的即可。 ```java /** * 资源服务器配置 * Created by macro on 2020/6/19. */ @AllArgsConstructor @Configuration @EnableWebFluxSecurity public class ResourceServerConfig { private final RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Bean public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { //省略若干代码... //自定义处理JWT请求头过期或签名错误的结果 http.oauth2ResourceServer().authenticationEntryPoint(restAuthenticationEntryPoint); //省略若干代码... return http.build(); } } ``` ## 总结 对于一时找不到解决方法的问题,我推荐使用DEBUG源码的方式来解决。首先寻找一个突破口,可以从你熟悉的一些类中去寻找一个必定会执行的方法,然后打断点,进行DEBUG,从调用的栈信息中查找出关键的类,之后通过这些关键类顺藤摸瓜就能找解决方法了! ## 项目源码地址 https://github.com/macrozheng/springcloud-learning/tree/master/micro-oauth2 ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/my_tools.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 盘点下我用的顺手的那些工具! > 之前经常有朋友问我一些常用的工具,比如我的架构图是用什么工具做的?我的数据库是用什么工具设计的?今天给大家介绍下我用的顺手的工具! ## IntelliJ IDEA ![](../images/my_tools_01.png) 业界公认最好的Java开发工具,平时用的最多。可以安装大量插件丰富功能,开发前端应用也不在话下! ## X-shell ![](../images/my_tools_02.png) 一款强大的安全终端模拟软件,可以用来连接和管理远程Linux服务器。 ## Postman ![](../images/my_tools_03.png) API接口调试工具,平时用来测试开发好的接口,有时也用来格式化下JSON字符串。 ## PowerDesigner ![](../images/my_tools_04.png) 数据库设计工具,平时用来设计数据库表,设计完成之后可以直接导出数据库表。 ## Navicat ![](../images/my_tools_05.png) 数据库可视化工具,支持多种数据库,平时用来连接并管理数据库,项目上线的时候可以用来同步表结构。 ## RedisDesktop ![](../images/my_tools_06.png) Redis可视化工具,平时用来查看和管理Redis缓存中的数据,有时候需要清空缓存的时候就用到它了。 ## Robomongo ![](../images/my_tools_07.png) MongoDB可视化工具,平时用来查看和管理MongoDB中的数据。 ## Typora ![](../images/my_tools_08.png) 平时用来写文章的Markdown编辑器,编辑与预览二合一,界面简洁且功能强大! ## ProcessOn ![](../images/my_tools_09.png) 作图工具,可以用来制作思维导图和流程图,mall项目的架构图就是用这个画的! ## MindMaster ![](../images/my_tools_10.png) 好用的思维导图制作工具,设计功能的时候可以用来整理下思路。 ## Snipaste ![](../images/my_tools_11.png) 一款好用的截屏工具,文章中很多图片都是用这个截的。 ## ScreenToGif ![](../images/my_tools_12.png) 用来制作Gif的工具,mall项目功能演示的Gif就是用这个做的。 ## 官网地址 - IntelliJ IDEA:https://www.jetbrains.com/idea/download - X-shell:http://www.netsarang.com/download/software.html - Postman:https://www.postman.com/ - PowerDesigner:http://powerdesigner.de/ - Navicat:http://www.formysql.com/xiazai.html - RedisDesktop:https://redisdesktop.com/download - Robomongo:https://robomongo.org/download - Typora:https://typora.io/ - ProcessOn:https://processon.com/ - MindMaster:http://www.edrawsoft.cn/mindmaster - Snipaste:https://www.snipaste.com/ - ScreenToGif:https://www.screentogif.com ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/my_web_tools.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 写了100多篇原创文章,我常用的在线工具网站推荐给大家! > 不知不觉写博客已经一年多了,累计写了100多篇原创文章,今天给大家分享下我经常使用的在线工具网站,希望对大家有所帮助! ## Markdown Nice 支持自定义样式的在线Markdown编辑器,编辑完成后可以一键复制富文本到微信公众号、知乎和掘金等平台。多达17种主题,总有一种适合你的!平时用`Typora`写完文章以后,我都会用这个排版,然后复制到公众号,非常好用! 地址:https://mdnice.com/ ![](../images/my_web_tools_01.png) ## Process On 一款在线作图工具,支持流程图、思维导图、原型图、UML、网络拓扑图、组织结构图等。平时文章中的流程图和思维导图,我都是用这个工具画的! 地址:https://www.processon.com/ ![](../images/my_web_tools_02.png) ## draw.io 又一款在线作图工具,平时用来画画流程图还是很不错的,最大优点是可以将图片存储到不同的网络位置。 地址:https://www.draw.io/ ![](../images/my_web_tools_03.png) ## 有道云笔记 文档管理,高效记录,它还有电脑客户端和手机端,使用方便。有个在线笔记工具还是很有必要的,比如平时搜索到的一些有用的文章,我会保存到笔记中去,方便以后查阅。平时有一些想学习的技术或者需要办理的事情我也会记录下来,毕竟好记性不如烂笔头。 地址:http://note.youdao.com/ ![](../images/my_web_tools_04.png) ## Iconfont 阿里巴巴矢量图标库,可以根据关键字搜索的图标库。写过前端的朋友肯定很熟悉,需要图标的时候上去找就对了!还记得`mall-swarm`中的微服务架构图么,图标就是从上面找的。 地址:https://www.iconfont.cn/ ![](../images/my_web_tools_05.png) ## 稿定设计 做图做视频必备,在线作图神器。平时公众号的头图就是用这个做的,当然它的功能远不止这个,在线抠图、编辑图片、在线PS也都支持! 地址:https://www.gaoding.com ![](../images/my_web_tools_06.png) ## Pexels Pexels是一个提供海量共享图片素材的网站,每周都会定量更新一些图片,图片质量很不错,有时候会在上面找点图片做公众号头图。 地址:https://www.pexels.com/zh-tw/ ![](../images/my_web_tools_07.png) ## Tinypng 图片无损压缩网站,平时文章里面的图片我都会放到这个网站上压缩下,因为有时候公众号图片太大上传会模糊! 地址:https://tinypng.com/ ![](../images/my_web_tools_08.png) ## Docsmall 不仅仅能压缩图片的压缩网站,还支持GIF、PDF的压缩。打开网站看下界面,非常美观的一个网站! 地址:https://docsmall.com/ ![](../images/my_web_tools_09.png) ## Markmap-lib Markdown笔记转思维导图工具,你值得拥有! 地址:https://markmap.js.org/ ![](../images/my_web_tools_10.png) ## Shields 徽章制作工具,很多开源项目顶部都有一些表示特定意义的徽章,其实都是用这个网站做的。 地址:https://shields.io/ ![](../images/my_web_tools_11.png) ## 小码短连接 简单易用的渠道短链接统计工具,可以有效缩短访问链接长度,看起来更美观,还可以统计链接访问次数,非常方便! 地址:https://xiaomark.com/ ![](../images/my_web_tools_12.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/mybatis_dynamic_sql.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 干掉mapper.xml!MyBatis新特性动态SQL真香! > 当我们使用MyBatis的时候,需要在mapper.xml中书写大量的SQL语句。当我们使用MyBatis Generator(MBG)作为代码生成器时,也会生成大量的mapper.xml文件。其实从MBG 1.3.6版本以后,MyBatis官方已经推荐使用Dynamic SQL,使用这一新特性基本就不用写mapper.xml文件了,使用起来非常方便,推荐给大家! ## Dynamic SQL简介 在我们使用Spring的时候,有XML和Java两种配置方式。在使用SpringBoot时,已经推荐使用Java配置,基本不用xml配置了。使用Dynamic SQL就好比是使用Java的方式来操作MyBatis。Dynamic SQL是用于生成动态SQL语句的框架,提倡使用Java API的方式来实现SQL操作,支持复杂查询和多表查询。 Dynamic SQL具有如下特性: - 类型安全:可以确保参数类型和数据库字段类型相匹配; - 富有表现力:语句的构建方式可以清楚地传达其含义; - 使用灵活:可以使用and,or和nested条件的任意组合来构建where子句; - 扩展性强:可以同时为MyBatis3, Spring JDBC和纯JDBC框架生成SQL语句; - 轻量级:只需添加一个小的依赖项,没有传递依赖。 ## 开始使用 > 首先我们通过一个入门示例将Dynamic SQL用起来,该示例会包含基础的CRUD操作。对MBG使用不了解的朋友可以先看下之前的文章[《解放双手!MyBatis官方代码生成工具给力!》](https://mp.weixin.qq.com/s/Wqstw_YTUBSi3sGIHZZmLQ) ### 集成Dynamic SQL - 在`pom.xml`中添加如下依赖,对比之前使用MBG,仅仅多添加了MyBatis的动态SQL依赖; ```xml org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.3 com.github.pagehelper pagehelper-spring-boot-starter 1.3.0 com.alibaba druid-spring-boot-starter 1.1.10 org.mybatis.generator mybatis-generator-core 1.4.0 org.mybatis.dynamic-sql mybatis-dynamic-sql 1.2.1 mysql mysql-connector-java 8.0.15 ``` - 在`application.yml`中对数据源和MyBatis的`mapper.xml`文件路径进行配置,只需配置自定义mapper.xml路径即可; ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mybatis: mapper-locations: - classpath:dao/*.xml ``` - 添加Java配置,用于扫描Mapper接口路径,MBG生成的放在`mapper`包下,自定义的放在`dao`包下。 ```java /** * MyBatis配置类 * Created by macro on 2019/4/8. */ @Configuration @MapperScan({"com.macro.mall.tiny.mbg.mapper","com.macro.mall.tiny.dao"}) public class MyBatisConfig { } ``` ### 使用代码生成器 - 在使用MBG生成代码前,我们还需要对其进行一些配置,首先在`generator.properties`文件中配置好数据库连接信息; ``` jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ``` - 然后在`generatorConfig.xml`文件中对MBG进行配置,配置属性说明直接参考注释即可; ```xml
``` - 与之前使用MBG有所不同,`targetRuntime`需要改为`MyBatis3DynamicSql`,用于配置生成mapper.xml路径的`sqlMapGenerator`标签也不需要配置了; - 之前使用MBG时自定义了实体类注解的生成,写了个类CommentGenerator继承DefaultCommentGenerator,在`addFieldComment`方法中将Swagger注解写入到了实体类的属性上; ```java /** * 自定义注释生成器 * Created by macro on 2018/4/26. */ public class CommentGenerator extends DefaultCommentGenerator { /** * 给字段添加注释 */ @Override public void addFieldComment(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) { String remarks = introspectedColumn.getRemarks(); //根据参数和备注信息判断是否添加备注信息 if(addRemarkComments&&StringUtility.stringHasValue(remarks)){ //数据库中特殊字符需要转义 if(remarks.contains("\"")){ remarks = remarks.replace("\"","'"); } //给model的字段添加swagger注解 field.addJavaDocLine("@ApiModelProperty(value = \""+remarks+"\")"); } } } ``` - 在使用Dynamic SQL的时候,这种方法已经无用,需要在`addFieldAnnotation`中将Swagger注解写入到了实体类的属性上; ```java /** * 自定义注释生成器 * Created by macro on 2018/4/26. */ public class CommentGenerator extends DefaultCommentGenerator { @Override public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn, Set imports) { if (!addRemarkComments || CollUtil.isEmpty(imports)) return; long count = imports.stream() .filter(item -> API_MODEL_PROPERTY_FULL_CLASS_NAME.equals(item.getFullyQualifiedName())) .count(); if (count <= 0L) { return; } String remarks = introspectedColumn.getRemarks(); //根据参数和备注信息判断是否添加备注信息 if (StringUtility.stringHasValue(remarks)) { //数据库中特殊字符需要转义 if (remarks.contains("\"")) { remarks = remarks.replace("\"", "'"); } //给model的字段添加swagger注解 field.addJavaDocLine("@ApiModelProperty(value = \"" + remarks + "\")"); } } } ``` - 一切准备就绪,执行Generator类的main方法,生成代码结构信息如下,可以发现已经不再生成mapper.xml文件和Example类,取而代之的是生成了DynamicSqlSupport类。 ![](../images/mybatis_dynamic_sql_01.png) ### 实现基本的CRUD操作 > 这里使用的是`mall-tiny`项目中权限管理功能相关表,具体可以参考[《还在从零开始搭建项目?手撸了款快速开发脚手架!》](https://mp.weixin.qq.com/s/tN3zjoKQxg1U19D4Slih8w)。 - 查看下MBG生成的Mapper接口,比之前使用MBG时增加了很多方法,并且有了一些默认的方法实现,可见之前在mapper.xml中的实现都已经转移到Mapper接口中去了,单表CRUD直接调用对应方法即可; ```java @Mapper public interface UmsAdminMapper { @Generated("org.mybatis.generator.api.MyBatisGenerator") BasicColumn[] selectList = BasicColumn.columnList(id, username, password, icon, email, nickName, note, createTime, loginTime, status); @Generated("org.mybatis.generator.api.MyBatisGenerator") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); @Generated("org.mybatis.generator.api.MyBatisGenerator") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); @Generated("org.mybatis.generator.api.MyBatisGenerator") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); @Generated("org.mybatis.generator.api.MyBatisGenerator") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ResultMap("UmsAdminResult") Optional selectOne(SelectStatementProvider selectStatement); @Generated("org.mybatis.generator.api.MyBatisGenerator") @SelectProvider(type=SqlProviderAdapter.class, method="select") @Results(id="UmsAdminResult", value = { @Result(column="id", property="id", jdbcType=JdbcType.BIGINT, id=true), @Result(column="username", property="username", jdbcType=JdbcType.VARCHAR), @Result(column="password", property="password", jdbcType=JdbcType.VARCHAR), @Result(column="icon", property="icon", jdbcType=JdbcType.VARCHAR), @Result(column="email", property="email", jdbcType=JdbcType.VARCHAR), @Result(column="nick_name", property="nickName", jdbcType=JdbcType.VARCHAR), @Result(column="note", property="note", jdbcType=JdbcType.VARCHAR), @Result(column="create_time", property="createTime", jdbcType=JdbcType.TIMESTAMP), @Result(column="login_time", property="loginTime", jdbcType=JdbcType.TIMESTAMP), @Result(column="status", property="status", jdbcType=JdbcType.INTEGER) }) List selectMany(SelectStatementProvider selectStatement); @Generated("org.mybatis.generator.api.MyBatisGenerator") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); @Generated("org.mybatis.generator.api.MyBatisGenerator") default long count(CountDSLCompleter completer) { return MyBatis3Utils.countFrom(this::count, umsAdmin, completer); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int delete(DeleteDSLCompleter completer) { return MyBatis3Utils.deleteFrom(this::delete, umsAdmin, completer); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int deleteByPrimaryKey(Long id_) { return delete(c -> c.where(id, isEqualTo(id_)) ); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int insert(UmsAdmin record) { return MyBatis3Utils.insert(this::insert, record, umsAdmin, c -> c.map(username).toProperty("username") .map(password).toProperty("password") .map(icon).toProperty("icon") .map(email).toProperty("email") .map(nickName).toProperty("nickName") .map(note).toProperty("note") .map(createTime).toProperty("createTime") .map(loginTime).toProperty("loginTime") .map(status).toProperty("status") ); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int insertSelective(UmsAdmin record) { return MyBatis3Utils.insert(this::insert, record, umsAdmin, c -> c.map(username).toPropertyWhenPresent("username", record::getUsername) .map(password).toPropertyWhenPresent("password", record::getPassword) .map(icon).toPropertyWhenPresent("icon", record::getIcon) .map(email).toPropertyWhenPresent("email", record::getEmail) .map(nickName).toPropertyWhenPresent("nickName", record::getNickName) .map(note).toPropertyWhenPresent("note", record::getNote) .map(createTime).toPropertyWhenPresent("createTime", record::getCreateTime) .map(loginTime).toPropertyWhenPresent("loginTime", record::getLoginTime) .map(status).toPropertyWhenPresent("status", record::getStatus) ); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default Optional selectOne(SelectDSLCompleter completer) { return MyBatis3Utils.selectOne(this::selectOne, selectList, umsAdmin, completer); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default List select(SelectDSLCompleter completer) { return MyBatis3Utils.selectList(this::selectMany, selectList, umsAdmin, completer); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default List selectDistinct(SelectDSLCompleter completer) { return MyBatis3Utils.selectDistinct(this::selectMany, selectList, umsAdmin, completer); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default Optional selectByPrimaryKey(Long id_) { return selectOne(c -> c.where(id, isEqualTo(id_)) ); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int update(UpdateDSLCompleter completer) { return MyBatis3Utils.update(this::update, umsAdmin, completer); } @Generated("org.mybatis.generator.api.MyBatisGenerator") static UpdateDSL updateAllColumns(UmsAdmin record, UpdateDSL dsl) { return dsl.set(username).equalTo(record::getUsername) .set(password).equalTo(record::getPassword) .set(icon).equalTo(record::getIcon) .set(email).equalTo(record::getEmail) .set(nickName).equalTo(record::getNickName) .set(note).equalTo(record::getNote) .set(createTime).equalTo(record::getCreateTime) .set(loginTime).equalTo(record::getLoginTime) .set(status).equalTo(record::getStatus); } @Generated("org.mybatis.generator.api.MyBatisGenerator") static UpdateDSL updateSelectiveColumns(UmsAdmin record, UpdateDSL dsl) { return dsl.set(username).equalToWhenPresent(record::getUsername) .set(password).equalToWhenPresent(record::getPassword) .set(icon).equalToWhenPresent(record::getIcon) .set(email).equalToWhenPresent(record::getEmail) .set(nickName).equalToWhenPresent(record::getNickName) .set(note).equalToWhenPresent(record::getNote) .set(createTime).equalToWhenPresent(record::getCreateTime) .set(loginTime).equalToWhenPresent(record::getLoginTime) .set(status).equalToWhenPresent(record::getStatus); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int updateByPrimaryKey(UmsAdmin record) { return update(c -> c.set(username).equalTo(record::getUsername) .set(password).equalTo(record::getPassword) .set(icon).equalTo(record::getIcon) .set(email).equalTo(record::getEmail) .set(nickName).equalTo(record::getNickName) .set(note).equalTo(record::getNote) .set(createTime).equalTo(record::getCreateTime) .set(loginTime).equalTo(record::getLoginTime) .set(status).equalTo(record::getStatus) .where(id, isEqualTo(record::getId)) ); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int updateByPrimaryKeySelective(UmsAdmin record) { return update(c -> c.set(username).equalToWhenPresent(record::getUsername) .set(password).equalToWhenPresent(record::getPassword) .set(icon).equalToWhenPresent(record::getIcon) .set(email).equalToWhenPresent(record::getEmail) .set(nickName).equalToWhenPresent(record::getNickName) .set(note).equalToWhenPresent(record::getNote) .set(createTime).equalToWhenPresent(record::getCreateTime) .set(loginTime).equalToWhenPresent(record::getLoginTime) .set(status).equalToWhenPresent(record::getStatus) .where(id, isEqualTo(record::getId)) ); } } ``` - 生成代码中有一些DynamicSqlSupport类,比如UmsAdminDynamicSqlSupport,主要是把数据库表和字段抽象成了SqlTable和SqlColumn对象,估计是为了防止我们硬编码; ```java public final class UmsAdminDynamicSqlSupport { @Generated("org.mybatis.generator.api.MyBatisGenerator") public static final UmsAdmin umsAdmin = new UmsAdmin(); public static final SqlColumn id = umsAdmin.id; public static final SqlColumn username = umsAdmin.username; public static final SqlColumn password = umsAdmin.password; public static final SqlColumn icon = umsAdmin.icon; public static final SqlColumn email = umsAdmin.email; public static final SqlColumn nickName = umsAdmin.nickName; public static final SqlColumn note = umsAdmin.note; public static final SqlColumn createTime = umsAdmin.createTime; public static final SqlColumn loginTime = umsAdmin.loginTime; public static final SqlColumn status = umsAdmin.status; @Generated("org.mybatis.generator.api.MyBatisGenerator") public static final class UmsAdmin extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); public final SqlColumn username = column("username", JDBCType.VARCHAR); public final SqlColumn password = column("password", JDBCType.VARCHAR); public final SqlColumn icon = column("icon", JDBCType.VARCHAR); public final SqlColumn email = column("email", JDBCType.VARCHAR); public final SqlColumn nickName = column("nick_name", JDBCType.VARCHAR); public final SqlColumn note = column("note", JDBCType.VARCHAR); public final SqlColumn createTime = column("create_time", JDBCType.TIMESTAMP); public final SqlColumn loginTime = column("login_time", JDBCType.TIMESTAMP); public final SqlColumn status = column("status", JDBCType.INTEGER); public UmsAdmin() { super("ums_admin"); } } } ``` - 利用好MBG生成的代码即可完成单表的CRUD操作了,比如下面最常见的操作。 ```java /** * 后台用户管理Service实现类 * Created by macro on 2020/12/8. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Autowired private UmsAdminMapper adminMapper; @Override public void create(UmsAdmin entity) { adminMapper.insert(entity); } @Override public void update(UmsAdmin entity) { adminMapper.updateByPrimaryKeySelective(entity); } @Override public void delete(Long id) { adminMapper.deleteByPrimaryKey(id); } @Override public UmsAdmin select(Long id) { Optional optionalEntity = adminMapper.selectByPrimaryKey(id); return optionalEntity.orElse(null); } @Override public List listAll(Integer pageNum, Integer pageSize) { PageHelper.startPage(pageNum, pageSize); return adminMapper.select(SelectDSLCompleter.allRows()); } } ``` ## 进阶使用 > 想要用好Dynamic SQL,上面的基础操作是不够的,还需要一些进阶的使用技巧。 ### SqlBuilder SqlBuilder是一个非常有用的类,使用它可以灵活地构建SQL语句的条件,一些常用的条件构建方法如下。 | 条件 | 例子 | 对应SQL | | -------------- | ----------------------------------- | -------------------------------------------- | | Between | where(foo, isBetween(x).and(y)) | where foo between ? and ? | | Equals | where(foo, isEqualTo(x)) | where foo = ? | | Greater Than | where(foo, isGreaterThan(x)) | where foo > ? | | In | where(foo, isIn(x, y)) | where foo in (?,?) | | Like | where(foo, isLike(x)) | where foo like ? | | Not Equals | where(foo, isNotEqualTo(x)) | where foo <> ? | | Null | where(foo, isNull()) | where foo is null | | Present Equals | where(foo, isEqualToWhenPresent(x)) | where foo = ? (will render if x is non-null) | ### StatementProvider 回想一下之前我们在mapper.xml中定义select标签的方式,各个select标签相当于Statement。而这里的StatementProvider好比是Statement中参数和SQL语句的封装,方便以Java的方式创建Statement。 ### 条件查询 > 使用SqlBuilder类构建StatementProvider,然后调用Mapper接口中的方法即可。 - 这里以按用户名和状态查询后台用户并按创建时间降序排列为例,SQL实现如下; ```sql SELECT id, username, PASSWORD, icon, email, nick_name, note, create_time, login_time, STATUS FROM ums_admin WHERE ( username = 'macro' AND STATUS IN ( 0, 1 ) ) ORDER BY create_time DESC; ``` - 使用Dynamic SQL对应的Java代码实现如下,使用SqlBuilder的select方法可以指定查询列,使用from方法可以指定查询表,使用where方法可以构建查询条件,使用orderBy方法可以指定排序。 ```java /** * 后台用户管理Service实现类 * Created by macro on 2020/12/8. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Override public List list(Integer pageNum, Integer pageSize, String username, List statusList) { PageHelper.startPage(pageNum, pageSize); SelectStatementProvider selectStatement = SqlBuilder.select(UmsAdminMapper.selectList) .from(UmsAdminDynamicSqlSupport.umsAdmin) .where(UmsAdminDynamicSqlSupport.username, isEqualToWhenPresent(username)) .and(UmsAdminDynamicSqlSupport.status, isIn(statusList)) .orderBy(UmsAdminDynamicSqlSupport.createTime.descending()) .build() .render(RenderingStrategies.MYBATIS3); return adminMapper.selectMany(selectStatement); } } ``` ### Lambda条件查询 > 使用Lambda表达式实现单表条件查询更加简单,实现上面的条件查询,对应Java代码实现如下。 ```java /** * 后台用户管理Service实现类 * Created by macro on 2020/12/8. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Override public List lambdaList(Integer pageNum, Integer pageSize, String username, List statusList) { PageHelper.startPage(pageNum, pageSize); List list = adminMapper.select(c -> c.where(UmsAdminDynamicSqlSupport.username, isEqualToWhenPresent(username)) .and(UmsAdminDynamicSqlSupport.status, isIn(statusList)) .orderBy(UmsAdminDynamicSqlSupport.createTime.descending())); return list; } } ``` ### 子查询 > 之前使用MBG需要在mapper.xml中手写SQL才能实现子查询,使用Dynamic SQL可以直接在Java代码中实现。 - 这里以按角色ID查询后台用户为例,SQL实现如下; ```sql SELECT * FROM ums_admin WHERE id IN ( SELECT admin_id FROM ums_admin_role_relation WHERE role_id = 1 ) ``` - 使用Dynamic SQL对应的Java代码实现如下,可以发现SqlBuilder的条件构造方法isIn中还可以嵌套SqlBuilder的查询。 ```java /** * 后台用户管理Service实现类 * Created by macro on 2020/12/8. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Override public List subList(Long roleId) { SelectStatementProvider selectStatement = SqlBuilder.select(UmsAdminMapper.selectList) .from(UmsAdminDynamicSqlSupport.umsAdmin) .where(UmsAdminDynamicSqlSupport.id, isIn(SqlBuilder.select(UmsAdminRoleRelationDynamicSqlSupport.adminId) .from(UmsAdminRoleRelationDynamicSqlSupport.umsAdminRoleRelation) .where(UmsAdminRoleRelationDynamicSqlSupport.roleId, isEqualTo(roleId)))) .build() .render(RenderingStrategies.MYBATIS3); return adminMapper.selectMany(selectStatement); } } ``` ### Group和Join查询 > 涉及到多表查询,之前使用MBG的时候基本只能在mapper.xml中手写SQL实现,使用Dynamic SQL可以支持多表查询。 - 这里以按角色统计后台用户数量为例,SQL实现如下; ```sql SELECT ur.id AS roleId, ur.NAME AS roleName, count( ua.id ) AS count FROM ums_role ur LEFT JOIN ums_admin_role_relation uarr ON ur.id = uarr.role_id LEFT JOIN ums_admin ua ON uarr.admin_id = ua.id GROUP BY ur.id; ``` - 先在Dao中添加一个`groupList`方法,然后使用`@Results`注解定义好resultMap; ```java /** * Created by macro on 2020/12/9. */ public interface UmsAdminDao { @SelectProvider(type = SqlProviderAdapter.class, method = "select") @Results(id = "RoleStatResult", value = { @Result(column = "roleId", property = "roleId", jdbcType = JdbcType.BIGINT, id = true), @Result(column = "roleName", property = "roleName", jdbcType = JdbcType.VARCHAR), @Result(column = "count", property = "count", jdbcType = JdbcType.INTEGER) }) List groupList(SelectStatementProvider selectStatement); } ``` - 然后在Service中调用`groupList`方法传入StatementProvider即可,对应的Java代码实现如下。 ```java /** * 后台用户管理Service实现类 * Created by macro on 2020/12/8. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Override public List groupList() { SelectStatementProvider selectStatement = SqlBuilder.select(UmsRoleDynamicSqlSupport.id.as("roleId"), UmsRoleDynamicSqlSupport.name.as("roleName"), count(UmsAdminDynamicSqlSupport.id).as("count")) .from(UmsRoleDynamicSqlSupport.umsRole) .leftJoin(UmsAdminRoleRelationDynamicSqlSupport.umsAdminRoleRelation) .on(UmsRoleDynamicSqlSupport.id, equalTo(UmsAdminRoleRelationDynamicSqlSupport.roleId)) .leftJoin(UmsAdminDynamicSqlSupport.umsAdmin) .on(UmsAdminRoleRelationDynamicSqlSupport.adminId, equalTo(UmsAdminDynamicSqlSupport.id)) .groupBy(UmsRoleDynamicSqlSupport.id) .build() .render(RenderingStrategies.MYBATIS3); return adminDao.groupList(selectStatement); } } ``` ### 条件删除 > 使用Dynamic SQL实现条件删除,直接调用Mapper接口中生成好的delete方法即可。 - 这里以按用户名删除后台用户为例,SQL实现如下; ```sql DELETE FROM ums_admin WHERE username = 'andy'; ``` - 使用Dynamic SQL对应Java中的实现如下。 ```java /** * 后台用户管理Service实现类 * Created by macro on 2020/12/8. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Override public void deleteByUsername(String username) { DeleteStatementProvider deleteStatement = SqlBuilder.deleteFrom(UmsAdminDynamicSqlSupport.umsAdmin) .where(UmsAdminDynamicSqlSupport.username, isEqualTo(username)) .build() .render(RenderingStrategies.MYBATIS3); adminMapper.delete(deleteStatement); } } ``` ### 条件修改 > 使用Dynamic SQL实现条件修改,直接调用Mapper接口中生成好的update方法即可。 - 这里以按指定ID修改后台用户的状态为例,SQL实现如下; ```sql UPDATE ums_admin SET STATUS = 1 WHERE id IN ( 1, 2 ); ``` - 使用Dynamic SQL对应Java中的实现如下。 ```java /** * 后台用户管理Service实现类 * Created by macro on 2020/12/8. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Override public void updateByIds(List ids, Integer status) { UpdateStatementProvider updateStatement = SqlBuilder.update(UmsAdminDynamicSqlSupport.umsAdmin) .set(UmsAdminDynamicSqlSupport.status).equalTo(status) .where(UmsAdminDynamicSqlSupport.id, isIn(ids)) .build() .render(RenderingStrategies.MYBATIS3); adminMapper.update(updateStatement); } } ``` ### 一对多查询 > 使用Dynamic SQL也可以实现一对多查询,只是由于Java注解无法实现循环引用,所以一对多的resultMap只能在mapper.xml来配置,这可能是唯一需要使用mapper.xml的地方。 - 这里以按ID查询后台用户信息(包含对应角色列表)为例,SQL实现如下; ```sql SELECT ua.*, ur.id AS role_id, ur.NAME AS role_name, ur.description AS role_description, ur.create_time AS role_create_time, ur.STATUS AS role_status, ur.sort AS role_sort FROM ums_admin ua LEFT JOIN ums_admin_role_relation uarr ON ua.id = uarr.admin_id LEFT JOIN ums_role ur ON uarr.role_id = ur.id WHERE ua.id = 1 ``` - 然后在Dao接口中添加`selectWithRoleList`方法,这里使用`@ResultMap`注解引用mapper.xml中定义的resultMap; ```java /** * Created by macro on 2020/12/9. */ public interface UmsAdminDao { @SelectProvider(type = SqlProviderAdapter.class, method = "select") @ResultMap("AdminRoleResult") AdminRoleDto selectWithRoleList(SelectStatementProvider selectStatement); } ``` - 在mapper.xml中添加名称为`AdminRoleResult`的resultMap,这里有个小技巧,可以直接引用在Mapper接口中定义好的resultMap; ```xml ``` - 然后在Service实现类中调用即可,为了方便结果集映射给查询列取了别名。 ```java /** * 后台用户管理Service实现类 * Created by macro on 2020/12/8. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Override public AdminRoleDto selectWithRoleList(Long id) { List columnList = new ArrayList<>(CollUtil.toList(UmsAdminMapper.selectList)); columnList.add(UmsRoleDynamicSqlSupport.id.as("role_id")); columnList.add(UmsRoleDynamicSqlSupport.name.as("role_name")); columnList.add(UmsRoleDynamicSqlSupport.description.as("role_description")); columnList.add(UmsRoleDynamicSqlSupport.createTime.as("role_create_time")); columnList.add(UmsRoleDynamicSqlSupport.status.as("role_status")); columnList.add(UmsRoleDynamicSqlSupport.sort.as("role_sort")); SelectStatementProvider selectStatement = SqlBuilder.select(columnList) .from(UmsAdminDynamicSqlSupport.umsAdmin) .leftJoin(UmsAdminRoleRelationDynamicSqlSupport.umsAdminRoleRelation) .on(UmsAdminDynamicSqlSupport.id, equalTo(UmsAdminRoleRelationDynamicSqlSupport.adminId)) .leftJoin(UmsRoleDynamicSqlSupport.umsRole) .on(UmsAdminRoleRelationDynamicSqlSupport.roleId, equalTo(UmsRoleDynamicSqlSupport.id)) .where(UmsAdminDynamicSqlSupport.id, isEqualTo(id)) .build() .render(RenderingStrategies.MYBATIS3); return adminDao.selectWithRoleList(selectStatement); } } ``` ## 总结 当我们使用MyBatis官方代码生成器MBG时,配置的targetRuntime决定了使用它的使用方式。Dynamic SQL更倾向于使用Java API来实现SQL操作,传统的方式更倾向于在mapper.xml中手写SQL来实现SQL操作。虽然MyBatis官方推荐使用Dynamic SQL,但选择那种方式全看个人习惯了! ## 参考资料 官方文档:https://mybatis.org/mybatis-dynamic-sql/docs/introduction.html ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-dynamic-sql ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/mybatis_generator_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 解放双手!MyBatis官方代码生成工具给力! > 在我们使用MyBatis的过程中,如果所有实体类和单表CRUD代码都需要手写,那将会是一件相当麻烦的事情。MyBatis官方代码生成器MyBatis Generator可以帮助我们解决这个问题,在我的开源项目mall中也是使用的这个代码生成器,用习惯了也挺不错的。本文将介绍MyBatis Generator的使用方法及使用技巧,希望对大家有所帮助! ## 简介 MyBatis Generator(简称MBG)是MyBatis官方提供的代码生成工具。可以通过数据库表直接生成实体类、单表CRUD代码、mapper.xml文件,从而解放我们的双手! ## 开始使用 > 首先我们通过一个入门示例将MBG用起来,该示例会包含基础的CRUD操作。 ### 集成MBG - 在`pom.xml`中添加如下依赖,主要添加了MyBatis、PageHelper、Druid、MBG和MySQL驱动等依赖; ```xml org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.3 com.github.pagehelper pagehelper-spring-boot-starter 1.3.0 com.alibaba druid-spring-boot-starter 1.1.10 org.mybatis.generator mybatis-generator-core 1.4.0 mysql mysql-connector-java 8.0.15 ``` - 在`application.yml`中对数据源和MyBatis的`mapper.xml`文件路径进行配置,这里做个约定,MBG生成的放在`resources/com/**/mapper`目录下,自定义的放在`resources/dao`目录下; ```yaml # 数据源配置 spring: datasource: url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root # MyBatis mapper.xml路径配置 mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml ``` - 添加Java配置,用于扫码Mapper接口路径,这里还有个约定,MBG生成的放在`mapper`包下,自定义的放在`dao`包下。 ```java /** * MyBatis配置类 * Created by macro on 2019/4/8. */ @Configuration @MapperScan({"com.macro.mall.tiny.mbg.mapper","com.macro.mall.tiny.dao"}) public class MyBatisConfig { } ``` ### 使用代码生成器 - 在使用MBG生成代码前,我们还需要对其进行一些配置,首先在`generator.properties`文件中配置好数据库连接信息; ``` jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ``` - 然后在`generatorConfig.xml`文件中对MBG进行配置,配置属性说明直接参考注释即可; ```xml
``` - 这里值得一提的是`targetRuntime`这个属性,设置不同的属性生成的代码和生成代码的使用方式会有所不同,常用的有`MyBatis3`和`MyBatis3DynamicSql`两种,这里使用的是`MyBatis3`; - 如果你想自定义MBG生成的代码的话,可以自己写一个CommentGenerator来继承DefaultCommentGenerator,这里我自定义了实体类代码的生成,添加了Swagger注解的支持; ```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 MAPPER_SUFFIX="Mapper"; 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)){ //数据库中特殊字符需要转义 if(remarks.contains("\"")){ remarks = remarks.replace("\"","'"); } //给model的字段添加swagger注解 field.addJavaDocLine("@ApiModelProperty(value = \""+remarks+"\")"); } } @Override public void addJavaFileComment(CompilationUnit compilationUnit) { super.addJavaFileComment(compilationUnit); //只在model中添加swagger注解类的导入 String fullyQualifiedName = compilationUnit.getType().getFullyQualifiedName(); if(!fullyQualifiedName.contains(MAPPER_SUFFIX)&&!fullyQualifiedName.contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ``` - 最后我们写个Generator类用于生成代码,直接运行main方法即可生成所有代码; ```java /** * 用于生产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); } } } ``` - 一切准备就绪,执行main方法,生成代码结构信息如下。 ![](../images/mybatis_generator_start_01.png) ### 实现基本的CRUD操作 > 这里使用的是`mall-tiny`项目中权限管理功能相关表,具体可以参考[《还在从零开始搭建项目?手撸了款快速开发脚手架!》](https://mp.weixin.qq.com/s/tN3zjoKQxg1U19D4Slih8w)。 - 查看下MBG生成的Mapper接口,发现已经包含了基本的CRUD方法,具体SQL实现也已经在mapper.xml中生成了,单表CRUD直接调用对应方法即可; ```java public interface UmsAdminMapper { long countByExample(UmsAdminExample example); int deleteByExample(UmsAdminExample example); int deleteByPrimaryKey(Long id); int insert(UmsAdmin record); int insertSelective(UmsAdmin record); List selectByExample(UmsAdminExample example); UmsAdmin selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("record") UmsAdmin record, @Param("example") UmsAdminExample example); int updateByExample(@Param("record") UmsAdmin record, @Param("example") UmsAdminExample example); int updateByPrimaryKeySelective(UmsAdmin record); int updateByPrimaryKey(UmsAdmin record); } ``` - 生成代码中有一些Example类,比如UmsAdminExample,我们可以把它理解为一个条件构建器,用于构建SQL语句中的各种条件; ![](../images/mybatis_generator_start_02.png) - 利用好MBG生成的代码即可完成单表的CRUD操作了,比如下面最常见的操作。 ```java /** * 后台用户管理Service实现类 * Created by macro on 2020/12/8. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Autowired private UmsAdminMapper adminMapper; @Autowired private UmsAdminDao adminDao; @Override public void create(UmsAdmin entity) { adminMapper.insert(entity); } @Override public void update(UmsAdmin entity) { adminMapper.updateByPrimaryKeySelective(entity); } @Override public void delete(Long id) { adminMapper.deleteByPrimaryKey(id); } @Override public UmsAdmin select(Long id) { return adminMapper.selectByPrimaryKey(id); } @Override public List listAll(Integer pageNum, Integer pageSize) { PageHelper.startPage(pageNum, pageSize); return adminMapper.selectByExample(new UmsAdminExample()); } } ``` ## 进阶使用 > 想要用好MBG,上面的基础操作是不够的,还需要一些进阶的使用技巧。 ### 条件查询 > 使用Example类构建查询条件可以很方便地实现条件查询。 - 这里以按用户名和状态查询后台用户并按创建时间降序排列为例,SQL实现如下; ```sql SELECT id, username, PASSWORD, icon, email, nick_name, note, create_time, login_time, STATUS FROM ums_admin WHERE ( username = 'macro' AND STATUS IN ( 0, 1 ) ) ORDER BY create_time DESC; ``` - 对应的Java代码实现如下。 ```java /** * 后台用户管理Service实现类 * Created by macro on 2020/12/8. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Override public List list(Integer pageNum, Integer pageSize, String username, List statusList) { PageHelper.startPage(pageNum, pageSize); UmsAdminExample umsAdminExample = new UmsAdminExample(); UmsAdminExample.Criteria criteria = umsAdminExample.createCriteria(); if(StrUtil.isNotEmpty(username)){ criteria.andUsernameEqualTo(username); } criteria.andStatusIn(statusList); umsAdminExample.setOrderByClause("create_time desc"); return adminMapper.selectByExample(umsAdminExample); } } ``` ### 子查询 > 使用MBG生成的代码并不能实现子查询,需要自己手写SQL实现。 - 这里以按角色ID查询后台用户为例,首先定义一个UmsAdminDao接口,这里约定下Dao里面存放的方法都是自定义SQL实现的方法,首先在Dao接口中添加`subList`方法; ```java /** * Created by macro on 2020/12/9. */ public interface UmsAdminDao { List subList(@Param("roleId") Long roleId); } ``` - 然后创建一个UmsAdminDao.xml文件,对应UmsAdminDao接口的SQL实现,写好对应的SQL实现,注意使用的resultMap MBG已经帮我们生成好了,无需自己手写。 ```xml ``` ### Group和Join查询 > Group和Join查询也不能使用MBG生成的代码实现。 - 这里以按角色统计后台用户数量为例,首先在Dao接口中添加`groupList`方法; ```java public interface UmsAdminDao { List groupList(); } ``` - 然后在mapper.xml中添加对应的SQL实现,我们可以通过给查询出来的列起别名,直接把列映射到resultType所定义的对象中去。 ```xml ``` ### 条件删除 > 条件删除很简单,直接使用Example类构造删除条件即可。 - 这里以按用户名删除后台用户为例,SQL实现如下; ```sql DELETE FROM ums_admin WHERE username = 'andy'; ``` - 对应Java中的实现为。 ```java /** * 后台用户管理Service实现类 * Created by macro on 2020/12/8. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Override public void deleteByUsername(String username) { UmsAdminExample example = new UmsAdminExample(); example.createCriteria().andUsernameEqualTo(username); adminMapper.deleteByExample(example); } } ``` ### 条件修改 > 条件删除很简单,直接使用Example类构造修改条件,然后传入修改参数即可。 - 这里以按指定ID修改后台用户的状态为例,SQL实现如下; ```sql UPDATE ums_admin SET STATUS = 1 WHERE id IN ( 1, 2 ); ``` - 对应Java中的实现为。 ```java /** * 后台用户管理Service实现类 * Created by macro on 2020/12/8. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Override public void updateByIds(List ids, Integer status) { UmsAdmin record = new UmsAdmin(); record.setStatus(status); UmsAdminExample example = new UmsAdminExample(); example.createCriteria().andIdIn(ids); adminMapper.updateByExampleSelective(record,example); } } ``` ### 一对多查询 > 一对多查询无法直接使用MBG生成的代码实现,需要手写SQL实现,并使用resultMap来进行结果集映射。 - 这里以按ID查询后台用户信息(包含对应角色列表)为例,先在Dao接口中添加`selectWithRoleList`方法; ```java /** * Created by macro on 2020/12/9. */ public interface UmsAdminDao { AdminRoleDto selectWithRoleList(@Param("id") Long id); } ``` - 然后在mapper.xml中添加对应的SQL实现,这里有个小技巧,可以给角色表查询出来的列取个别名,添加一个`role_`前缀; ```xml ``` - 然后定义一个叫做`AdminRoleResult`的ResultMap,通过`collection`标签直接将以`role_`开头的列映射到UmsRole对象中去即可。 ```xml ``` ### 一对一查询 > 一对一查询无法直接使用MBG生成的代码实现,需要手写SQL实现,并使用resultMap来进行结果集映射。 - 这里以按ID查询资源信息(包括分类信息)为例,先在Dao接口中添加`selectResourceWithCate`方法; ```java /** * Created by macro on 2020/12/9. */ public interface UmsAdminDao { ResourceWithCateDto selectResourceWithCate(@Param("id")Long id); } ``` - 然后在mapper.xml中添加对应的SQL实现,可以给分类表查询出来的列取个别名,添加一个`cate_`前缀; ```xml ``` - 然后定义一个叫做`ResourceWithCateResult`的ResultMap,通过`association`标签直接将以`cate_`开头的列映射到UmsResourceCategory对象中去即可。 ```xml ``` ## 总结 总的来说MyBatis官方代码生成器MBG还是很强大的,可以生成一些常用的单表CRUD方法,减少了我们的工作量。但是对于子查询、多表查询和一些复杂查询支持有点偏弱,依然需要在mapper.xml中手写SQL实现。 ## 参考资料 官方文档:https://mybatis.org/generator/index.html ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-generator ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/mybatis_plus_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 还在手写CRUD代码?这款开源框架助你解放双手! > 相信很多朋友在项目中使用的ORM框架都是MyBatis,如果单用MyBatis来操作数据库的话,需要手写很多单表查询的SQL实现。这时候我们往往会选择一个增强工具来实现这些单表CRUD操作,这里推荐一款好用的工具MyBatis-Plus! ## MyBatis-Plus简介 MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。MyBatis-Plus 提供了代码生成器,可以一键生成controller、service、mapper、model、mapper.xml代码,同时提供了丰富的CRUD操作方法,助我们解放双手! ## MyBatis-Plus集成 > 首先我们需要在SpringBoot项目中集成MyBatis-Plus,之后我们再详细介绍它的使用方法! - 在`pom.xml`中添加相关依赖,主要是MyBatis-Plus、MyBatis-Plus Generator和Velocity模板引擎; ```xml com.baomidou mybatis-plus-boot-starter 3.3.2 com.baomidou mybatis-plus-generator 3.3.2 org.apache.velocity velocity-engine-core 2.2 ``` - 在SpringBoot配置文件`application.yml`添加如下配置,配置好数据源和MyBatis-Plus; ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mybatis-plus: mapper-locations: classpath:/mapper/**/*.xml #指定mapper.xml路径 global-config: db-config: id-type: auto #全局默认主键类型设置为自增 configuration: auto-mapping-behavior: partial #只对非嵌套的 resultMap 进行自动映射 map-underscore-to-camel-case: true #开启自动驼峰命名规则映射 ``` - 添加MyBatis-Plus的Java配置,使用`@MapperScan`注解配置好需要扫码的Mapper接口路径,MyBatis-Plus自带分页功能,需要配置好分页插件`PaginationInterceptor`。 ```java /** * MyBatis配置类 * Created by macro on 2019/4/8. */ @Configuration @MapperScan("com.macro.mall.tiny.modules.*.mapper") public class MyBatisConfig { @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); return paginationInterceptor; } } ``` ## 代码生成器 > MyBatis-Plus 提供了代码生成器,可以一键生成controller、service、mapper、model、mapper.xml代码,非常方便! - 首先我们创建代码生成器类`MyBatisPlusGenerator`,直接运行其`main`方法即可生成相关代码; ```java /** * MyBatisPlus代码生成器 * Created by macro on 2020/8/20. */ public class MyBatisPlusGenerator { public static void main(String[] args) { String projectPath = System.getProperty("user.dir") + "/mall-tiny-plus"; String moduleName = scanner("模块名"); String[] tableNames = scanner("表名,多个英文逗号分割").split(","); // 代码生成器 AutoGenerator autoGenerator = new AutoGenerator(); autoGenerator.setGlobalConfig(initGlobalConfig(projectPath)); autoGenerator.setDataSource(initDataSourceConfig()); autoGenerator.setPackageInfo(initPackageConfig(moduleName)); autoGenerator.setCfg(initInjectionConfig(projectPath, moduleName)); autoGenerator.setTemplate(initTemplateConfig()); autoGenerator.setStrategy(initStrategyConfig(tableNames)); autoGenerator.setTemplateEngine(new VelocityTemplateEngine()); autoGenerator.execute(); } /** * 读取控制台内容信息 */ private static String scanner(String tip) { Scanner scanner = new Scanner(System.in); System.out.println(("请输入" + tip + ":")); if (scanner.hasNext()) { String next = scanner.next(); if (StrUtil.isNotEmpty(next)) { return next; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } /** * 初始化全局配置 */ private static GlobalConfig initGlobalConfig(String projectPath) { GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setOutputDir(projectPath + "/src/main/java"); globalConfig.setAuthor("macro"); globalConfig.setOpen(false); globalConfig.setSwagger2(true); globalConfig.setBaseResultMap(true); globalConfig.setFileOverride(true); globalConfig.setDateType(DateType.ONLY_DATE); globalConfig.setEntityName("%s"); globalConfig.setMapperName("%sMapper"); globalConfig.setXmlName("%sMapper"); globalConfig.setServiceName("%sService"); globalConfig.setServiceImplName("%sServiceImpl"); globalConfig.setControllerName("%sController"); return globalConfig; } /** * 初始化数据源配置 */ private static DataSourceConfig initDataSourceConfig() { Props props = new Props("generator.properties"); DataSourceConfig dataSourceConfig = new DataSourceConfig(); dataSourceConfig.setUrl(props.getStr("dataSource.url")); dataSourceConfig.setDriverName(props.getStr("dataSource.driverName")); dataSourceConfig.setUsername(props.getStr("dataSource.username")); dataSourceConfig.setPassword(props.getStr("dataSource.password")); return dataSourceConfig; } /** * 初始化包配置 */ private static PackageConfig initPackageConfig(String moduleName) { Props props = new Props("generator.properties"); PackageConfig packageConfig = new PackageConfig(); packageConfig.setModuleName(moduleName); packageConfig.setParent(props.getStr("package.base")); packageConfig.setEntity("model"); return packageConfig; } /** * 初始化模板配置 */ private static TemplateConfig initTemplateConfig() { TemplateConfig templateConfig = new TemplateConfig(); //可以对controller、service、entity模板进行配置 //mapper.xml模板需单独配置 templateConfig.setXml(null); return templateConfig; } /** * 初始化策略配置 */ private static StrategyConfig initStrategyConfig(String[] tableNames) { StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig.setNaming(NamingStrategy.underline_to_camel); strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); strategyConfig.setEntityLombokModel(true); strategyConfig.setRestControllerStyle(true); //当表名中带*号时可以启用通配符模式 if (tableNames.length == 1 && tableNames[0].contains("*")) { String[] likeStr = tableNames[0].split("_"); String likePrefix = likeStr[0] + "_"; strategyConfig.setLikeTable(new LikeTable(likePrefix)); } else { strategyConfig.setInclude(tableNames); } return strategyConfig; } /** * 初始化自定义配置 */ private static InjectionConfig initInjectionConfig(String projectPath, String moduleName) { // 自定义配置 InjectionConfig injectionConfig = new InjectionConfig() { @Override public void initMap() { // 可用于自定义属性 } }; // 模板引擎是Velocity String templatePath = "/templates/mapper.xml.vm"; // 自定义输出配置 List focList = new ArrayList<>(); // 自定义配置会被优先输出 focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! return projectPath + "/src/main/resources/mapper/" + moduleName + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); injectionConfig.setFileOutConfigList(focList); return injectionConfig; } } ``` - 然后在`resources`目录下添加配置文件`generator.properties`,添加代码生成器的数据源配置及存放业务代码的基础包名称; ```properties dataSource.url=jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai dataSource.driverName=com.mysql.cj.jdbc.Driver dataSource.username=root dataSource.password=root package.base=com.macro.mall.tiny.modules ``` - 细心的朋友可以发现`MyBatisPlusGenerator`中很多配置代码都没添加注释,其实MyBatis-Plus源码中的中文注释非常完善,只需查看源码即可,这里摘抄一段`DataSourceConfig`中的源码; ```java /** * 数据库配置 * * @author YangHu, hcl * @since 2016/8/30 */ @Data @Accessors(chain = true) public class DataSourceConfig { /** * 数据库信息查询 */ private IDbQuery dbQuery; /** * 数据库类型 */ private DbType dbType; /** * PostgreSQL schemaName */ private String schemaName; /** * 类型转换 */ private ITypeConvert typeConvert; /** * 关键字处理器 * @since 3.3.2 */ private IKeyWordsHandler keyWordsHandler; /** * 驱动连接的URL */ private String url; /** * 驱动名称 */ private String driverName; /** * 数据库连接用户名 */ private String username; /** * 数据库连接密码 */ private String password; //省略若干代码...... } ``` - 代码生成器支持两种模式,一种生成单表的代码,比如只生成`pms_brand`表代码可以先输入`pms`,后输入`pms_brand`; ![](../images/mybatis_plus_start_01.png) - 生成单表代码结构一览; ![](../images/mybatis_plus_start_02.png) - 另一种直接生成整个模块的代码,需要带通配符`*`,比如生成`ums`模块代码可以先输入`ums`,后输入`ums_*`; ![](../images/mybatis_plus_start_03.png) - 生成整个模块代码结构一览。 ![](../images/mybatis_plus_start_04.png) ## 自定义生成模板 > MyBatis-Plus 使用模板引擎来生成代码,支持 Velocity(默认)、Freemarker、Beetl模板引擎,这里以Velocity为了来介绍下如何自定义生成模板。 - 首先我们可以从 MyBatis-Plus Generator依赖包的源码中找到默认模板,拷贝到项目的`resources/templates`目录下; ![](../images/mybatis_plus_start_05.png) - 在`MyBatisPlusGenerator`类中对`TemplateConfig`进行配置,配置好各个模板的路径; ```java /** * MyBatisPlus代码生成器 * Created by macro on 2020/8/20. */ public class MyBatisPlusGenerator { /** * 初始化模板配置 */ private static TemplateConfig initTemplateConfig() { TemplateConfig templateConfig = new TemplateConfig(); //可以对controller、service、entity模板进行配置 templateConfig.setEntity("templates/entity.java"); templateConfig.setMapper("templates/mapper.java"); templateConfig.setController("templates/controller.java"); templateConfig.setService("templates/service.java"); templateConfig.setServiceImpl("templates/serviceImpl.java"); //mapper.xml模板需单独配置 templateConfig.setXml(null); return templateConfig; } } ``` - 对模板进行定制,在定制过程中我们可以发现很多内置变量,用于输出到模板中去,这里以`service.java.vm`模板为例子,比如`package`、`table`这些变量; ``` package ${package.Service}; import ${package.Entity}.${entity}; import ${superServiceClassPackage}; /** *

* $!{table.comment} 服务类 *

* * @author ${author} * @since ${date} */ #if(${kotlin}) interface ${table.serviceName} : ${superServiceClass}<${entity}> #else public interface ${table.serviceName} extends ${superServiceClass}<${entity}> { } #end ``` - 搞懂这些变量从哪来的,对我们定制模板很有帮助,其实这些变量都来着于`AbstractTemplateEngine`的`getObjectMap`方法,具体变量作用可以参考源码。 ```java /** * 模板引擎抽象类 * * @author hubin * @since 2018-01-10 */ public abstract class AbstractTemplateEngine { /** * 渲染对象 MAP 信息 * * @param tableInfo 表信息对象 * @return ignore */ public Map getObjectMap(TableInfo tableInfo) { Map objectMap = new HashMap<>(30); ConfigBuilder config = getConfigBuilder(); if (config.getStrategyConfig().isControllerMappingHyphenStyle()) { objectMap.put("controllerMappingHyphenStyle", config.getStrategyConfig().isControllerMappingHyphenStyle()); objectMap.put("controllerMappingHyphen", StringUtils.camelToHyphen(tableInfo.getEntityPath())); } objectMap.put("restControllerStyle", config.getStrategyConfig().isRestControllerStyle()); objectMap.put("config", config); objectMap.put("package", config.getPackageInfo()); GlobalConfig globalConfig = config.getGlobalConfig(); objectMap.put("author", globalConfig.getAuthor()); objectMap.put("idType", globalConfig.getIdType() == null ? null : globalConfig.getIdType().toString()); objectMap.put("logicDeleteFieldName", config.getStrategyConfig().getLogicDeleteFieldName()); objectMap.put("versionFieldName", config.getStrategyConfig().getVersionFieldName()); objectMap.put("activeRecord", globalConfig.isActiveRecord()); objectMap.put("kotlin", globalConfig.isKotlin()); objectMap.put("swagger2", globalConfig.isSwagger2()); objectMap.put("date", new SimpleDateFormat("yyyy-MM-dd").format(new Date())); objectMap.put("table", tableInfo); objectMap.put("enableCache", globalConfig.isEnableCache()); objectMap.put("baseResultMap", globalConfig.isBaseResultMap()); objectMap.put("baseColumnList", globalConfig.isBaseColumnList()); objectMap.put("entity", tableInfo.getEntityName()); objectMap.put("entitySerialVersionUID", config.getStrategyConfig().isEntitySerialVersionUID()); objectMap.put("entityColumnConstant", config.getStrategyConfig().isEntityColumnConstant()); objectMap.put("entityBuilderModel", config.getStrategyConfig().isEntityBuilderModel()); objectMap.put("chainModel", config.getStrategyConfig().isChainModel()); objectMap.put("entityLombokModel", config.getStrategyConfig().isEntityLombokModel()); objectMap.put("entityBooleanColumnRemoveIsPrefix", config.getStrategyConfig().isEntityBooleanColumnRemoveIsPrefix()); objectMap.put("superEntityClass", getSuperClassName(config.getSuperEntityClass())); objectMap.put("superMapperClassPackage", config.getSuperMapperClass()); objectMap.put("superMapperClass", getSuperClassName(config.getSuperMapperClass())); objectMap.put("superServiceClassPackage", config.getSuperServiceClass()); objectMap.put("superServiceClass", getSuperClassName(config.getSuperServiceClass())); objectMap.put("superServiceImplClassPackage", config.getSuperServiceImplClass()); objectMap.put("superServiceImplClass", getSuperClassName(config.getSuperServiceImplClass())); objectMap.put("superControllerClassPackage", verifyClassPacket(config.getSuperControllerClass())); objectMap.put("superControllerClass", getSuperClassName(config.getSuperControllerClass())); return Objects.isNull(config.getInjectionConfig()) ? objectMap : config.getInjectionConfig().prepareObjectMap(objectMap); } } ``` ## CRUD操作 > MyBatis-Plus的强大之处不止在于它的代码生成功能,还在于它提供了丰富的CRUD方法,让我们实现单表CRUD几乎不用手写SQL实现! - 我们之前生成的`PmsBrandMapper`接口由于继承了`BaseMapper`接口,直接拥有了各种CRUD方法; ```java /** *

* 品牌表 Mapper 接口 *

* * @author macro * @since 2020-08-20 */ public interface PmsBrandMapper extends BaseMapper { } ``` - 我们来看下`BaseMapper`中的方法,是不是基本可以满足我们的日常所需了; ![](../images/mybatis_plus_start_06.png) - 我们之前生成的`PmsBrandService`接口由于继承了`IService`接口,也拥有了各种CRUD方法; ```java /** *

* 品牌表 服务类 *

* * @author macro * @since 2020-08-20 */ public interface PmsBrandService extends IService { } ``` - 可以看下比`BaseMapper`中的更加丰富; ![](../images/mybatis_plus_start_07.png) - 有了这些`IService`和`BaseMapper`中提供的这些方法,我们单表查询就几乎不用手写SQL实现了,使用MyBatis-Plus实现以前`PmsBrandController`的方法更轻松了! ```java /** *

* 品牌表 前端控制器 *

* * @author macro * @since 2020-08-20 */ @Api(tags = "PmsBrandController", description = "商品品牌管理") @RestController @RequestMapping("/brand") public class PmsBrandController { private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class); @Autowired private PmsBrandService brandService; @ApiOperation("获取所有品牌列表") @RequestMapping(value = "/listAll", method = RequestMethod.GET) @ResponseBody public CommonResult> getBrandList() { return CommonResult.success(brandService.list()); } @ApiOperation("添加品牌") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) { CommonResult commonResult; boolean result = brandService.save(pmsBrand); if (result) { 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", method = RequestMethod.POST) @ResponseBody public CommonResult updateBrand(@RequestBody PmsBrand pmsBrand) { CommonResult commonResult; boolean result = brandService.updateById(pmsBrand); if (result) { commonResult = CommonResult.success(pmsBrand); LOGGER.debug("updateBrand success:{}", pmsBrand); } else { commonResult = CommonResult.failed("操作失败"); LOGGER.debug("updateBrand failed:{}", pmsBrand); } return commonResult; } @ApiOperation("删除指定id的品牌") @RequestMapping(value = "/delete/{id}", method = RequestMethod.GET) @ResponseBody public CommonResult deleteBrand(@PathVariable("id") Long id) { boolean result = brandService.removeById(id); if (result) { 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) { Page page = new Page<>(pageNum, pageSize); Page pageResult = brandService.page(page); return CommonResult.success(CommonPage.restPage(pageResult)); } @ApiOperation("获取指定id的品牌详情") @RequestMapping(value = "/{id}", method = RequestMethod.GET) @ResponseBody public CommonResult brand(@PathVariable("id") Long id) { return CommonResult.success(brandService.getById(id)); } } ``` ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-plus ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/mysql.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 开发者必备Mysql命令 > 开发者必备Mysql常用命令,涵盖了数据定义语句、数据操纵语句及数据控制语句,基于Mysql5.7。 ## 数据定义语句(DDL) ### 数据库操作 - 登录数据库: ```shell mysql -uroot -proot ``` - 创建数据库: ```sql create database test ``` - 查看所有数据库: ```sql show databases ``` ![](../images/refer_screen_41.png) - 选择数据库并使用: ```sql use test ``` - 查看所有数据表: ```sql show tables ``` - 删除数据库: ```sql drop database test ``` ### 表操作 - 创建表: ```sql create table emp(ename varchar(10),hiredate date,sal decimal(10,2),deptno int(2)) ``` ```sql create table dept(deptno int(2),deptname varchar(10)) ``` ![](../images/refer_screen_42.png) - 查看表的定义: ```sql desc emp ``` ![](../images/refer_screen_43.png) - 查看表定义(详细): ```sql show create table emp \G ``` ![](../images/refer_screen_44.png) - 删除表: ```sql drop table emp ``` - 修改表字段: ```sql alter table emp modify ename varchar(20) ``` - 添加表字段: ```sql alter table emp add column age int(3) ``` - 删除表字段: ```sql alter table emp drop column age ``` - 字段改名; ```sql alter table emp change age age1 int(4) ``` - 修改表名: ```sql alter table emp rename emp1 ``` ## 数据操纵语句(DML) ### 插入记录 - 指定名称插入: ```sql insert into emp (ename,hiredate,sal,deptno) values ('zhangsan','2018-01-01','2000',1) ``` - 不指定名称插入: ```sql insert into emp values ('lisi','2018-01-01','2000',1) ``` - 批量插入数据: ```sql insert into dept values(1,'dept1'),(2,'dept2') ``` ### 修改记录 ```sql update emp set sal='4000',deptno=2 where ename='zhangsan' ``` ### 删除记录 ```sql delete from emp where ename='zhangsan' ``` ### 查询记录 - 查询所有记录: ```sql select * from emp ``` - 查询不重复的记录: ```sql select distinct deptno from emp ``` - 条件查询: ```sql select * from emp where deptno=1 and sal<3000 ``` - 排序和限制: ```sql select * from emp order by deptno desc limit 2 ``` - 分页查询(查询从第0条记录开始10条): ```sql select * from emp order by deptno desc limit 0,10 ``` - 聚合(查询部门人数大于1的部门编号): ```sql select deptno,count(1) from emp group by deptno having count(1) > 1 ``` - 连接查询: ```sql select * from emp e left join dept d on e.deptno=d.deptno ``` - 子查询: ```sql select * from emp where deptno in (select deptno from dept) ``` - 记录联合: ```sql select deptno from emp union select deptno from dept ``` ## 数据控制语句(DCL) ### 权限相关 - 授予操作权限(将test数据库中所有表的select和insert权限授予test用户): ```sql grant select,insert on test.* to 'test'@'localhost' identified by '123' ``` - 查看账号权限: ```sql show grants for 'test'@'localhost' ``` ![](../images/refer_screen_45.png) - 收回操作权限: ```sql revoke insert on test.* from 'test'@'localhost' ``` ![](../images/refer_screen_46.png) - 授予所有数据库的所有权限: ```sql grant all privileges on *.* to 'test'@'localhost' ``` - 授予所有数据库的所有权限(包括grant): ```sql grant all privileges on *.* to 'test'@'localhost' with grant option ``` - 授予SUPER PROCESS FILE权限(系统权限不能指定数据库): ```sql grant super,process,file on *.* to 'test'@'localhost' ``` - 只授予登录权限: ```sql grant usage on *.* to 'test'@'localhost' ``` ### 帐号相关 - 删除账号: ```sql drop user 'test'@'localhost' ``` - 修改自己的密码: ```sql set password = password('123') ``` - 管理员修改他人密码: ```sql set password for 'test'@'localhost' = password('123') ``` ## 其他 ### 字符集相关 - 查看字符集: ```sql show variables like 'character%' ``` ![](../images/refer_screen_47.png) - 创建数据库时指定字符集: ```sql create database test2 character set utf8 ``` ![](../images/refer_screen_48.png) ### 时区相关 - 查看当前时区(UTC为世界统一时间,中国为UTC+8): ```sql show variables like "%time_zone%" ``` ![](../images/refer_screen_49.png) - 修改mysql全局时区为北京时间,即我们所在的东8区: ```sql set global time_zone = '+8:00'; ``` - 修改当前会话时区: ```sql set time_zone = '+8:00' ``` ![](../images/refer_screen_50.png) - 立即生效: ```sql flush privileges ``` ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/mysql_master_slave.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # MySql主从复制,从原理到实践! > 本文将从MySql主从复制的原理出发,详细介绍MySql在Docker环境下的主从复制搭建,以一个主实例和一个从实例实现主从复制为例。 ## 什么是主从复制? 主从复制是指将主数据库的DDL和DML操作通过二进制日志传到从数据库上,然后在从数据库上对这些日志进行重新执行,从而使从数据库和主数据库的数据保持一致。 ## 主从复制的原理 - MySql主库在事务提交时会把数据变更作为事件记录在二进制日志Binlog中; - 主库推送二进制日志文件Binlog中的事件到从库的中继日志Relay Log中,之后从库根据中继日志重做数据变更操作,通过逻辑复制来达到主库和从库的数据一致性; - MySql通过三个线程来完成主从库间的数据复制,其中Binlog Dump线程跑在主库上,I/O线程和SQL线程跑着从库上; - 当在从库上启动复制时,首先创建I/O线程连接主库,主库随后创建Binlog Dump线程读取数据库事件并发送给I/O线程,I/O线程获取到事件数据后更新到从库的中继日志Relay Log中去,之后从库上的SQL线程读取中继日志Relay Log中更新的数据库事件并应用,如下图所示。 ![](../images/mysql_master_slave_06.png) ## 主实例搭建 - 运行mysql主实例: ```bash docker run -p 3307:3306 --name mysql-master \ -v /mydata/mysql-master/log:/var/log/mysql \ -v /mydata/mysql-master/data:/var/lib/mysql \ -v /mydata/mysql-master/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD=root \ -d mysql:5.7 ``` - 在mysql的配置文件夹`/mydata/mysql-master/conf`中创建一个配置文件`my.cnf`: ```bash touch my.cnf ``` - 修改配置文件my.cnf,配置信息如下: ```bash [mysqld] ## 设置server_id,同一局域网中需要唯一 server_id=101 ## 指定不需要同步的数据库名称 binlog-ignore-db=mysql ## 开启二进制日志功能 log-bin=mall-mysql-bin ## 设置二进制日志使用内存大小(事务) binlog_cache_size=1M ## 设置使用的二进制日志格式(mixed,statement,row) binlog_format=mixed ## 二进制日志过期清理时间。默认值为0,表示不自动清理。 expire_logs_days=7 ## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。 ## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致 slave_skip_errors=1062 ``` - 修改完配置后重启实例: ```bash docker restart mysql-master ``` - 进入`mysql-master`容器中: ```bash docker exec -it mysql-master /bin/bash ``` - 在容器中使用mysql的登录命令连接到客户端: ```bash mysql -uroot -proot ``` - 创建数据同步用户: ```bash CREATE USER 'slave'@'%' IDENTIFIED BY '123456'; GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%'; ``` ## 从实例搭建 - 运行mysql从实例: ```bash docker run -p 3308:3306 --name mysql-slave \ -v /mydata/mysql-slave/log:/var/log/mysql \ -v /mydata/mysql-slave/data:/var/lib/mysql \ -v /mydata/mysql-slave/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD=root \ -d mysql:5.7 ``` - 在mysql的配置文件夹`/mydata/mysql-slave/conf`中创建一个配置文件`my.cnf`: ```bash touch my.cnf ``` - 修改配置文件my.cnf: ```bash [mysqld] ## 设置server_id,同一局域网中需要唯一 server_id=102 ## 指定不需要同步的数据库名称 binlog-ignore-db=mysql ## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用 log-bin=mall-mysql-slave1-bin ## 设置二进制日志使用内存大小(事务) binlog_cache_size=1M ## 设置使用的二进制日志格式(mixed,statement,row) binlog_format=mixed ## 二进制日志过期清理时间。默认值为0,表示不自动清理。 expire_logs_days=7 ## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。 ## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致 slave_skip_errors=1062 ## relay_log配置中继日志 relay_log=mall-mysql-relay-bin ## log_slave_updates表示slave将复制事件写进自己的二进制日志 log_slave_updates=1 ## slave设置为只读(具有super权限的用户除外) read_only=1 ``` - 修改完配置后重启实例: ```bash docker restart mysql-slave ``` ## 将主从数据库进行连接 - 连接到主数据库的mysql客户端,查看主数据库状态: ```bash show master status; ``` - 主数据库状态显示如下: ![](../images/mysql_master_slave_01.png) - 进入`mysql-slave`容器中: ```bash docker exec -it mysql-slave /bin/bash ``` - 在容器中使用mysql的登录命令连接到客户端: ```bash mysql -uroot -proot ``` - 在从数据库中配置主从复制: ```bash change master to master_host='192.168.6.132', master_user='slave', master_password='123456', master_port=3307, master_log_file='mall-mysql-bin.000001', master_log_pos=617, master_connect_retry=30; ``` - 主从复制命令参数说明: - master_host:主数据库的IP地址; - master_port:主数据库的运行端口; - master_user:在主数据库创建的用于同步数据的用户账号; - master_password:在主数据库创建的用于同步数据的用户密码; - master_log_file:指定从数据库要复制数据的日志文件,通过查看主数据的状态,获取File参数; - master_log_pos:指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取Position参数; - master_connect_retry:连接失败重试的时间间隔,单位为秒。 - 查看主从同步状态: ```bash show slave status \G; ``` - 从数据库状态显示如下: ![](../images/mysql_master_slave_02.png) - 开启主从同步: ```bash start slave; ``` - 查看从数据库状态发现已经同步: ![](../images/mysql_master_slave_03.png) ## 主从复制测试 > 主从复制的测试方法有很多,可以在主实例中创建一个数据库,看看从实例中是否有该数据库,如果有,表示主从复制已经搭建成功。 - 在主实例中创建一个数据库`mall`; ![](../images/mysql_master_slave_04.png) - 在从实例中查看数据库,发现也有一个`mall`数据库,可以判断主从复制已经搭建成功。 ![](../images/mysql_master_slave_05.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/mysql_workbench.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 干掉Navicat!MySQL官方客户端到底行不行? > 在我们选择工具的时候,往往会优先选择那些免费又好用的工具!Navicat作为一款付费软件,虽然功能强大,但也阻止不了我们探索新工具的步伐。最近体验了一把MySQL的官方客户端工具`MySQL Workbench`,本文将对其和Navicat做个对比,看看它能否取代Navicat! ## 安装 > 我们先把`MySQL Workbench`安装好,其中有个小坑需要注意下。 - 首先我们需要下载`MySQL Workbench`的安装包,下载地址:https://dev.mysql.com/downloads/workbench/ ![](../images/mysql_workbench_01.png) - 下载完成后我们双击安装会遇到一个问题,`MySQL Workbench 8.0`版本安装需要先安装`Visual C++ 2019 Redistributable Package`依赖; ![](../images/mysql_workbench_02.png) - 下载`Visual C++ 2019 Redistributable Package`,下载地址:https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads ![](../images/mysql_workbench_03.png) - 下载完成后安装`Visual C++ 2019 Redistributable Package`,一直点击下一步即可; ![](../images/mysql_workbench_04.png) - 之后重新双击`MySQL Workbench`的安装包,一路点击下一步即可顺利安装了。 ![](../images/mysql_workbench_05.png) ## 使用 > 接下来我们将通过基础的数据库管理功能,来体验下`MySQL Workbench`的使用。 ### 建立新连接 双击打开`MySQL Workbench`,然后输入数据库连接信息即可建立新连接。 ![](../images/mysql_workbench_06.png) ### 外观设置 由于默认的编辑器字体比较小,可以改大一些,通过`Edit->Preferences`选项来修改,不过这里只能通过输入字体名称和大小来改变,有点不太方便! ![](../images/mysql_workbench_07.png) ### 表管理 - 查看数据库表结构,通过右击表选择`Table Inspector`来查看; ![](../images/mysql_workbench_08.png) - 修改数据库表结构,通过右击表选择`Alter Table`来修改。 ![](../images/mysql_workbench_09.png) ### 数据管理 - 查看表数据,通过右击表选择`Select Rows`来查看,我们可以发现在SQL编辑器中自动生成了SELECT语句; ![](../images/mysql_workbench_10.png) - 修改表数据,我们需要双击需要修改的表字段,然后点击`Apply`来应用; ![](../images/mysql_workbench_11.png) - 我们可以发现最后工具中的修改被转化为了UPDATE语句,可见`MySQL Workbench`中的数据操作最终会转化为语句来执行。 ![](../images/mysql_workbench_12.png) ### SQL操作 - 我们可以使用SQL编辑器来执行SQL语句,使用左上角的按钮可以创建一个SQL编辑器; ![](../images/mysql_workbench_13.png) - `MySQL Workbench`的SQL提示还是挺全的,来写个SQL试试,基本能够满足平时编辑SQL的需求。 ![](../images/mysql_workbench_14.gif) ### 实例管理 - 查看MySQL服务状态信息; ![](../images/mysql_workbench_15.png) - 管理MySQL用户和权限; ![](../images/mysql_workbench_16.png) - 管理MySQL实例,实现启动和关闭; ![](../images/mysql_workbench_17.png) - 查看展示MySQL服务性能信息的仪表盘。 ![](../images/mysql_workbench_18.png) ## 对比Navicat - Navicat中有个非常好用的数据库设计功能,为此我抛弃了笨重的PowerDesigner,很显然`MySQL Workbench`并不支持该功能。 ![](../images/mysql_workbench_19.png) - 使用Navicat设计数据库,具体可以参考[《我用起来顺手的数据库设计工具,这次推荐给大家!》](https://mp.weixin.qq.com/s/SJI0x7qQw5JkCvDWou7XaQ)。 - Navicat具有强大的数据备份和结构同步功能,平时用来数据备份,同步测试环境的数据库结构到生产环境很好用,如果使用`MySQL Workbench`估计就只能将SQL进行导入导出了。 ![](../images/mysql_workbench_20.png) - 使用Navicat实现数据备份和结构同步,具体可以参考[《Navicat实用功能:数据备份与结构同步》](https://mp.weixin.qq.com/s/Km7lg-T0p9Kzb_WeyHVaqw)。 - `MySQL Workbench`中特有的实例管理功能是Navicat所不具备的。 - Navicat能支持MySQL、MariaDB、MongoDB、SQL Server、Oracle、PostgreSQL等多种数据库,很显然`MySQL Workbench`只能支持MySQL。 ## 总结 总的来说,`MySQL Workbench`作为一款MySQL数据库管理工具显然足够了,但是Navicat的很多功能它是无法取代的。如果你想设计数据库,拥有更好的数据库运维功能,或者你需要使用多种不同的数据库,那还是使用Navicat吧! ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/navicat.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Navicat实用功能:数据备份与结构同步 > 当我们要对数据库做有风险的操作时需要对数据库备份,每次上线项目时,线上与线下数据库结构总会有不一致的情况,本文将讲解如何利用Navicat来方便的解决这两个问题。 ## Navicat > Navicat是一套快速、可靠的数据库管理工具,专为简化数据库的管理及降低系统管理成本而设。它的设计符合数据库管理员、开发人员及中小企业的需要。Navicat 是以直觉化的图形用户界面而建的,让你可以以安全并且简单的方式创建、组织、访问并共用信息。 注意:本教程采用的时Navicat12版本,下载地址:[https://www.navicat.com.cn/download/navicat-premium](https://www.navicat.com.cn/download/navicat-premium) ### 使用数据库介绍 > 现在数据库中有两个数据库,mall-test表示测试环境数据库,mall-prod表示线上环境数据库。 ### 数据备份 > 现在我们先对mall-test数据库备份,备份完成后,删除商品表的数据,然后利用备份进行数据还原。 #### mall-test中的数据概览 > 目前数据库中只有商品模块的数据库表,pms_product表中有一定的数据。 ![../images/arch_screen_86.png](../images/arch_screen_86.png) ![../images/arch_screen_87.png](../images/arch_screen_87.png) #### 进行数据备份 - 先点击顶部工具栏的备份图标,再点击新建备份按钮 ![../images/arch_screen_88.png](../images/arch_screen_88.png) - 点击开始按钮开始备份 ![../images/arch_screen_89.png](../images/arch_screen_89.png) - 备份完成后会生成一个备份文件 ![../images/arch_screen_90.png](../images/arch_screen_90.png) #### 清空pms_product表的数据 ![../images/arch_screen_91.png](../images/arch_screen_91.png) #### 从备份中还原数据 ![../images/arch_screen_92.png](../images/arch_screen_92.png) ![../images/arch_screen_93.png](../images/arch_screen_93.png) ### 结构同步 #### 相关背景说明 原来的mall-test模块中只有商品模块的表,现在我们经过了一段时间的开发,新增了订单模块,同时删除和修改了商品模块的一些表,而mall-prod表中还是原来的商品模块表,我们现在要做的是把mall-test的数据库表结构同步到mall-prod。 ![../images/arch_screen_94.png](../images/arch_screen_94.png) ![../images/arch_screen_95.png](../images/arch_screen_95.png) #### 进行结构同步 - 选择结构同步功能 ![../images/arch_screen_96.png](../images/arch_screen_96.png) - 选择源数据库和目标数据库,点击比对按钮 ![../images/arch_screen_97.png](../images/arch_screen_97.png) - 可以看到修改、新增和删除的表信息,点击部署会出现sql脚本的预览 ![../images/arch_screen_98.png](../images/arch_screen_98.png) - 确认了部署脚本信息后,点击运行会进行数据结构的同步 ![../images/arch_screen_99.png](../images/arch_screen_99.png) - 再次进行结构同步时,已经发现两个数据库结构已经完全一致 ![../images/arch_screen_100.png](../images/arch_screen_100.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/navicat_designer.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 我用起来顺手的数据库设计工具,这次推荐给大家! > 好的数据库设计工具,可以帮助我们进行思考并提高我们的设计效率。以前一直使用的是PowerDesigner,最近发现Navicat的数据库设计功能也很不错,界面简洁且容易使用,特此推荐给大家。 ## Navicat > Navicat是一套快速、可靠的数据库管理工具,专为简化数据库的管理及降低系统管理成本而设。它的设计符合数据库管理员、开发人员及中小企业的需要。Navicat 是以直觉化的图形用户界面而建的,让你可以以安全并且简单的方式创建、组织、访问并共用信息。 注意:本教程采用的时Navicat12版本,下载地址:https://www.navicat.com.cn/download/navicat-premium ## 数据库设计 > 下面我们来讲下如何使用Navicat来设计数据库,以`mall`项目的权限管理模块为例。 ### 打开模型 首先我们需要打开Navicat的数据库设计功能,该功能在工具栏中的`模型`按钮下,直接打开即可。 ![](../images/navicat_designer_01.png) ### 新建表 - 通过工具栏中的`表`按钮新建一张表; ![](../images/navicat_designer_02.png) - 新建完成后通过双击`设计表`的界面,然后添加对应字段,这里新建了一张`ums_admin`表; ![](../images/navicat_designer_03.png) ### 建立外键关系 > 如果我们的表没有外键,当表越来越多,关系越来越复杂时,我们就无法理清表与表之间的关系了,所以我们在设计的时候需要通过外键来标注表与表之间的关系。 - 我们再新建两张表`ums_role`和`ums_admin_role_relation`用于演示建立多对多关系,并通过工具栏的`外键`按钮建立外键; ![](../images/navicat_designer_04.png) - 点击`外键`按钮后直接点击需要建立外键的字段,这里点击的是`admin_id`,之后你会发现多了一个`小连线`; ![](../images/navicat_designer_05.png) - 双击这个`小连线`进行外键的编辑操作,修改参考表为`ums_admin`,参考字段为`id`; ![](../images/navicat_designer_06.png) - 编辑完成后就会出现表示外键关系的连线了; ![](../images/navicat_designer_07.png) - 之后可以把整个`mall`项目权限管理模块的表都建立起来练习下,下面是建立完成后的效果; ![](../images/navicat_designer_08.png) - 如何你觉得排版不好的话,可以点击下工具栏的`自动调整版面功能`,是不是个很贴心的功能呢! ![](../images/navicat_designer_09.png) ### 导出SQL > 我们一般在设计数据库的时候通过`外键`来建立关系,但是在数据库中往往不使用外键,通常通过逻辑来关联,所以在我们导出SQL的时候需要设置去除外键的生成。 - 导出SQL功能在工具菜单下面; ![](../images/navicat_designer_10.png) - 导出时需要在`高级`中去除外键的生成,点击确定就可以成功导出SQL语句了。 ![](../images/navicat_designer_11.png) ## 逆向工程 > 之前有些朋友反馈`mall`中的PowerDesigner数据库设计文件无法打开,这里提供另一种方法,通过Navicat来查看数据库设计。 - 首先我们需要一份有外键关系的SQL文件,这里我已经生成好了,下载地址:https://github.com/macrozheng/mall-learning/blob/master/document/navicat/mall-ref.sql - 之后将该SQL文件导入到数据库中,这里导入的是`pd-test`数据库; - 然后通过逆向工程从数据库中去生成数据库设计图,该功能在工具目录下面; ![](../images/navicat_designer_12.png) - 之后选择需要导入的数据库`pd-test`; ![](../images/navicat_designer_14.png) - 导入成功后就可以看到完整、有关系的数据库设计图了,大家可以按自己的喜好修改表的位置。 ![](../images/navicat_designer_13.png) ## 总结 总的来说Navicat的数据库设计功能还是相当不错的,简洁易用,界面也很漂亮。设计数据库在PowerDesigner中只是一个功能,使用起来未免太沉重,而Navicat的数据库设计功能更轻巧! ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/nginx.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Nginx的这些妙用,你肯定有不知道的! > 本文将从反向代理、文件压缩、地址重写三方面来讲解Nginx在Docker环境下的使用技巧! ## 在Docker环境下的安装 ### 下载nginx的docker镜像 ```bash docker pull nginx:1.10 ``` ### 从容器中拷贝nginx配置 - 先运行一次容器(为了拷贝配置文件): ```bash docker run -p 80:80 --name nginx \ -v /mydata/nginx/html:/usr/share/nginx/html \ -v /mydata/nginx/logs:/var/log/nginx \ -d nginx:1.10 ``` - 将容器内的配置文件拷贝到指定目录: ```bash docker container cp nginx:/etc/nginx /mydata/nginx/ ``` - 修改文件名称: ```bash mv nginx conf ``` - 终止并删除容器: ```bash docker stop nginx docker rm nginx ``` ### 使用docker命令启动 ```bash docker run -p 80:80 --name nginx \ -v /mydata/nginx/html:/usr/share/nginx/html \ -v /mydata/nginx/logs:/var/log/nginx \ -v /mydata/nginx/conf:/etc/nginx \ -d nginx:1.10 ``` ## 反向代理 > 反向代理就是当请求访问你的代理服务器时,代理服务器会对你的请求进行转发,可以转发到静态的资源路径上去,也可以转发到动态的服务接口上去。下面我们以对域名进行代理为例,来讲讲如何进行静态代理和动态代理。 ### 静态代理 > 静态代理就是将请求代理到不同的静态资源路径上去,这里我们将对`docs.macrozheng.com`的请求代理到我的文档项目中,对`mall.macrozheng.com`的请求代理到mall的前端项目中。 - 首先我们修改下本机的host文件: ``` 192.168.6.132 docs.macrozheng.com 192.168.6.132 mall.macrozheng.com ``` - 然后将我们的文档项目和mall前端项目上传到nginx的html目录中去,并进行解压操作: ![](../images/nginx_use_01.png) - 在`/mydata/nginx/conf/conf.d`文件夹中添加配置文件docs.conf对文档项目进行反向代理: ``` server { listen 80; server_name docs.macrozheng.com; #修改域名 location / { root /usr/share/nginx/html/docs; #代理到docs文件夹中 index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` - 在`/mydata/nginx/conf/conf.d`文件夹中添加配置文件mall.conf对mall的前端项目进行反向代理: ``` server { listen 80; server_name mall.macrozheng.com; #修改域名 location / { root /usr/share/nginx/html/mall; #代理到mall文件夹中 index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` - 重启nginx服务: ```bash docker restart nginx ``` - 通过`docs.macrozheng.com`即可访问到我们的文档项目了: ![](../images/nginx_use_02.png) - 通过`mall.macrozheng.com`即可访问到mall的前端项目了: ![](../images/nginx_use_03.png) ### 动态代理 > 动态代理就是把代理服务器的请求转发到另一个服务上去,这里我们将对`api.macrozheng.com`的请求代理到mall-admin的后台服务上去。 - 首先我们修改下本机的host文件,添加如下规则: ``` 192.168.6.132 api.macrozheng.com ``` - 在`/mydata/nginx/conf/conf.d`文件夹中添加配置文件api.conf对将请求代理到远程的mall-admin服务上去: ``` server { listen 80; server_name api.macrozheng.com; #修改域名 location / { proxy_pass http://120.27.63.9:8080; #修改为代理服务地址 index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` - 重启动nginx服务后,通过`api.macrozheng.com/swagger-ui.html`即可访问到mall-admin的API文档页面了: ![](../images/nginx_use_04.png) ## 文件压缩 > 如果我们租用了一个带宽很低的服务器,网站访问速度会很慢,这时我们可以通过让nginx开启GZIP压缩来提高网站的访问速度。这里我们以mall的前端项目为例来演示下它的提速效果。 - 首先我们对nginx进行限速操作,限制每个连接的访问速度为128K来建立一个比较慢的访问场景; - 修改mall.conf配置文件,进行限速操作: ``` server { listen 80; server_name mall.macrozheng.com; limit_rate 128k; #限制网速为128K location / { root /usr/share/nginx/html/mall; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` - 对mall的前端项目`mall.macrozheng.com`进行访问,我们可以发现网站中有个js文件比较大,需要加载12s: ![](../images/nginx_use_05.png) - nginx返回请求头信息如下: ![](../images/nginx_use_06.png) - 修改`/mydata/nginx/conf`目录下的`nginx.conf`配置文件,开启GZIP压缩; ``` http { gzip on; #开启gzip gzip_disable "msie6"; #IE6不使用gzip gzip_vary on; #设置为on会在Header里增加 "Vary: Accept-Encoding" gzip_proxied any; #代理结果数据的压缩 gzip_comp_level 6; #gzip压缩比(1~9),越小压缩效果越差,但是越大处理越慢,所以一般取中间值 gzip_buffers 16 8k; #获取多少内存用于缓存压缩结果 gzip_http_version 1.1; #识别http协议的版本 gzip_min_length 1k; #设置允许压缩的页面最小字节数,超过1k的文件会被压缩 gzip_types application/javascript text/css; #对特定的MIME类型生效,js和css文件会被压缩 include /etc/nginx/conf.d/*.conf; } ``` - 再次对mall的前端项目`mall.macrozheng.com`进行访问,我们可以发现js文件已经被压缩,加载时间缩短到3.88s,提速3倍左右: ![](../images/nginx_use_07.png) - nginx返回请求头中添加了`Content-Encoding: gzip`的信息: ![](../images/nginx_use_08.png) ## 地址重写 > 有的时候我们的网站更换了域名,但还有用户在使用老的域名访问,这时可以通过nginx的地址重写来让用户跳转到新的域名进行访问。 - 比如说原来我们用的`docs.macrozheng.com`这个域名不用了,现在改成`www.macrozheng.com`了来访问文档项目了; - 修改docs.conf配置文件,将地址带参数重写到新地址: ``` server { listen 80; server_name docs.macrozheng.com; rewrite "^/(.*)$" http://www.macrozheng.com/$1; #地址重写到新地址 location / { root /usr/share/nginx/html/docs; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` - 此时访问旧域名`docs.macrozheng.com`会直接跳转到`www.macrozheng.com`去。 ## 按目录划分项目 > 有时候我们需要使用同一个域名来访问不同的前端项目,这时候就需要通过子目录来区分前端项目了。 - 比如说我们需要按以下路径来访问各个前端项目; ``` www.macrozheng.com #访问文档项目 www.macrozheng.com/admin #访问后台项目 www.macrozheng.com/app #访问移动端项目 ``` - 修改www.conf配置文件,添加不同的location规则,要注意`alias`和`root`指令的区别,root不会将location配置的路径去掉,而alias会将location配置的路径去掉。 ``` server { listen 80; server_name www.macrozheng.com; location / { root /usr/share/nginx/html/www; index index.html index.htm; } location /admin { alias /usr/share/nginx/html/admin; index index.html index.htm; } location /app { alias /usr/share/nginx/html/app; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/nginx_https_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Nginx如何支持HTTPS?手把手教贼简单! > 随着我们网站用户的增多,我们会逐渐意识到HTTPS加密的重要性。在不修改现有代码的情况下,要从HTTP升级到HTTPS,让Nginx支持HTTPS是个很好的选择。今天我们来讲下如何从Nginx入手,从HTTP升级到HTTPS,同时支持静态网站和SpringBoot应用,希望对大家有所帮助! ## 生成SSL自签名证书 > 虽然自签名证书浏览器认为并不是安全的,但是学习下SSL证书的生成还是很有必要的! - 首先创建SSL证书私钥,期间需要输入两次用户名和密码,生成文件为`blog.key`; ```bash openssl genrsa -des3 -out blog.key 2048 ``` - 利用私钥生成一个不需要输入密码的密钥文件,生成文件为`blog_nopass.key`; ```bash openssl rsa -in blog.key -out blog_nopass.key ``` - 创建SSL证书签名请求文件,生成SSL证书时需要使用到,生成文件为`blog.csr`; ```bash openssl req -new -key blog.key -out blog.csr ``` - 在生成过程中,我们需要输入一些信息,需要注意的是`Common Name`需要和网站域名一致; ``` Enter pass phrase for blog.key: ----- Country Name (2 letter code) [XX]:CN # 国家代码 State or Province Name (full name) []:jiangsu # 省份 Locality Name (eg, city) [Default City]:jiangsu # 城市 Organization Name (eg, company) [Default Company Ltd]:macrozheng # 机构名称 Organizational Unit Name (eg, section) []:dev # 单位名称 Common Name (eg, your name or your server's hostname) []:blog.macrozheng.com # 网站域名 Email Address []:macrozheng@qq.com # 邮箱 Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: # 私钥保护密码,可以不输入直接回车 An optional company name []: # 可选公司名称,可以不输入直接回车 ``` - 生成SSL证书,有效期为365天,生成文件为`blog.crt`; ```bash openssl x509 -req -days 365 -in blog.csr -signkey blog.key -out blog.crt ``` - 其实最终有用的文件是两个,一个是证书文件`blog.crt`,另一个是不需要输入密码的证书私钥文件`blog_nopass.key`。 ## Nginx支持HTTPS > SSL证书生成好了,接下来我们就可以配置Nginx来支持HTTPS了! ### 安装Nginx - 我们还是使用在Docker容器中安装Nginx的方式,先下载Nginx的Docker镜像; ```bash docker pull nginx:1.10 ``` - 下载完成后先运行一次Nginx,由于之后我们要把宿主机的Nginx配置文件映射到Docker容器中去,运行一次方便我们拷贝默认配置; ```bash docker run -p 80:80 --name nginx \ -v /mydata/nginx/html:/usr/share/nginx/html \ -v /mydata/nginx/logs:/var/log/nginx \ -d nginx:1.10 ``` - 运行成功后将容器中的Nginx配置目录拷贝到宿主机上去; ```bash docker container cp nginx:/etc/nginx /mydata/nginx/ ``` - 将宿主机上的`nginx`目录改名为`conf`,要不然`/mydata/nginx/nginx`这个配置文件目录看着有点别扭; ```bash mv /mydata/nginx/nginx /mydata/nginx/conf ``` - 创建的Nginx容器复制完配置后就没用了,停止并删除容器; ```bash docker stop nginx docker rm nginx ``` - 使用Docker命令重新启动Nginx服务,需要映射好配置文件,由于我们要支持HTTPS,还需要开放`443`端口。 ```bash docker run -p 80:80 -p 443:443 --name nginx \ -v /mydata/nginx/html:/usr/share/nginx/html \ -v /mydata/nginx/logs:/var/log/nginx \ -v /mydata/nginx/conf:/etc/nginx \ -d nginx:1.10 ``` ### 配置支持HTTPS - 将我们生成好的SSL证书和私钥拷贝到Nginx的`html/ssl`目录下; ```bash cp blog_nopass.key /mydata/nginx/html/ssl/ cp blog.crt /mydata/nginx/html/ssl/ ``` - 接下来我们需要给`blog.macrozheng.com`这个域名添加HTTPS支持,在`/mydata/nginx/conf/conf.d/`目录下添加Nginx配置文件`blog.conf`,配置文件内容如下; ``` server { listen 80; # 同时支持HTTP listen 443 ssl; # 添加HTTPS支持 server_name blog.macrozheng.com; #SSL配置 ssl_certificate /usr/share/nginx/html/ssl/blog/blog.crt; # 配置证书 ssl_certificate_key /usr/share/nginx/html/ssl/blog/blog_nopass.key; # 配置证书私钥 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 配置SSL协议版本 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; # 配置SSL加密算法 ssl_prefer_server_ciphers on; # 优先采取服务器算法 ssl_session_cache shared:SSL:10m; # 配置共享会话缓存大小 ssl_session_timeout 10m; # 配置会话超时时间 location / { root /usr/share/nginx/html/www; index index.html index.htm; } location /admin { alias /usr/share/nginx/html/admin; index index.html index.htm; } location /app { alias /usr/share/nginx/html/app; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` - 通过`HTTPS`访问`blog.macrozheng.com`这个域名,由于我们使用的是自己签名的SSL证书,浏览器会提示`您的连接不是私密连接`,点击继续前往可以通过HTTPS正常访问; ![](../images/nginx_https_start_01.png) - 我们可以查看下证书的`颁发者`信息,可以发现正好是之前我们创建SSL证书签名请求文件时录入的信息; ![](../images/nginx_https_start_02.png) - 接下来我们需要给`api.macrozheng.com`这个域名添加HTTPS支持,通过这个域名可以使用HTTPS访问我们的SpringBoot应用,`api.crt`和`api_nopass.key`文件需要自行生成,在`/mydata/nginx/conf/conf.d/`目录下添加Nginx配置文件`api.conf`,配置文件内容如下; ``` server { listen 80; # 同时支持HTTP listen 443 ssl; # 添加HTTPS支持 server_name api.macrozheng.com; #修改域名 #ssl配置 ssl_certificate /usr/share/nginx/html/ssl/api/api.crt; # 配置证书 ssl_certificate_key /usr/share/nginx/html/ssl/api/api_nopass.key; # 配置证书私钥 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 配置SSL协议版本 # 配置SSL加密算法 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; # 优先采取服务器算法 ssl_session_cache shared:SSL:10m; # 配置共享会话缓存大小 ssl_session_timeout 10m; # 配置会话超时时间 location / { proxy_pass http://192.168.3.101:8080; # 设置代理服务访问地址 proxy_set_header Host $http_host; # 设置客户端真实的域名(包括端口号) proxy_set_header X-Real-IP $remote_addr; # 设置客户端真实IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 设置在多层代理时会包含真实客户端及中间每个代理服务器的IP proxy_set_header X-Forwarded-Proto $scheme; # 设置客户端真实的协议(http还是https) index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` - 通过`HTTPS`访问`api.macrozheng.com`这个域名,访问地址为:https://api.macrozheng.com/swagger-ui.html ![](../images/nginx_https_start_03.png) - 任意调用一个接口测试下,比如说登录接口,可以发现已经可以通过HTTPS正常访问SpringBoot应用提供的接口。 ![](../images/nginx_https_start_04.png) ## 使用受信任的证书 > 之前我们使用的是自签名的SSL证书,对于浏览器来说是无效的。使用权威机构颁发的SSL证书浏览器才会认为是有效的,这里给大家推荐两种申请免费SSL证书的方法,一种是从阿里云申请,另一种是从FreeSSL申请。 ### 阿里云证书 - 阿里云上可以申请的免费证书目前只有支持单个域名的DV级SSL证书。比如说你有`blog.macrozheng.com`和`api.macrozheng.com`两个二级域名需要使用HTTPS,就需要申请两个SSL证书。 ![](../images/nginx_https_start_05.png) - 申请成功后点击下载Nginx证书即可; ![](../images/nginx_https_start_06.png) - 下载完成后解压会有下面两个文件; ```bash blog.macrozheng.com.key # 证书私钥文件 blog.macrozheng.com.pem # 证书文件 ``` - 拷贝证书文件到Nginx的指定目录下,然后修改配置文件`blog.conf`,只要修改证书配置路径即可,修改完成后重启Nginx; ``` #SSL配置 ssl_certificate /usr/share/nginx/html/ssl/blog/blog.macrozheng.com.pem; # 配置证书 ssl_certificate_key /usr/share/nginx/html/ssl/blog/blog.macrozheng.com.key; # 配置证书私钥 ``` - 再次通过HTTPS访问`blog.macrozheng.com`这个域名,发现证书已经有效了,连接也是安全的了。 ![](../images/nginx_https_start_07.png) ### FreeSSL证书 - 如果你有使用通配符域名的需求,可以上`FreeSSL`申请SSL证书,不过免费的有效期只有3个月,这就意味着你过3个月就要重新申请一次了。 ![](../images/nginx_https_start_08.png) - 附上官网地址:https://freessl.cn/ ### 使用`acme.sh`自动申请证书 - `acme.sh`脚本实现了`acme`协议, 可以从`letsencrypt`生成免费的证书。一般我们申请的证书有效期都是1年,过期就要重新申请了,使用`acme.sh`脚本可以实现到期自动申请,再也不用担心证书过期了! ![](../images/nginx_https_start_09.png) - 附上官网地址:https://github.com/acmesh-official/acme.sh ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/postman.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Postman:API接口调试利器 > Postman是一款API接口调试工具,使用它可以很方便的对接口进行测试,并且后端人员可以将自己的调试结果导出,方便前端人员调试。 ## 安装 - 下载地址:https://www.getpostman.com/downloads/ - 下载完安装包后直接双击安装即可。 ## 设置 ### 主题设置 这里不得不说,Postman的界面还是做的很好的,比起Swagger来说好多了,Postman默认提供了两种主题,一种亮色和一种暗色,可以通过左上角的File->Settings按钮打开。 ![](../images/postman_screen_01.png) ![](../images/postman_screen_02.png) ### 调整字体大小 可能界面默认的字体大小并不适合你,尤其是大屏幕的电脑,可以在View下的Zoom In和Zoom Out按钮进行放大和缩小。 ![](../images/postman_screen_03.png) ## 进行接口调试 > 测试接口均来自mall-admin后台,启动后可以直接测试。 ### 调用GET请求 ![](../images/postman_screen_04.png) ### 调用POST请求提交JSON格式数据 ![](../images/postman_screen_05.png) ### 调用POST请求提交表单 ![](../images/postman_screen_06.png) ### 调用文件上传接口 ![](../images/postman_screen_07.png) ![](../images/postman_screen_08.png) ### 调用需要登录的接口 ### 调用登录接口获取令牌 ![](../images/postman_screen_09.png) ### 设置令牌头并调用需要登录的接口 ![](../images/postman_screen_10.png) ## 调试文件的导入与导出 ### 将调试接口信息进行保存 ![](../images/postman_screen_11.png) ![](../images/postman_screen_12.png) ### 导出Collection中的调试信息 ![](../images/postman_screen_17.png) ### 导入Collection中的调试信息 ![](../images/postman_screen_18.png) ![](../images/postman_screen_19.png) ## 使用过程中的一些技巧 ### 设置不同的环境 我们开发时,都会分本地环境和测试环境,本地环境用于本机调试接口,测试环境用于前后端联调接口。上面我们把[http://localhost:8080](http://localhost:8080)这个ip端口直接写在请求路径之中,当我们要调试测试环境接口时,就会产生麻烦。定义多个环境变量,在接口地址中进行引用,可以解决这个问题。 #### 添加本地环境 ![](../images/postman_screen_13.png) #### 添加测试环境 ![](../images/postman_screen_14.png) #### 引用环境变量 ![](../images/postman_screen_15.png) #### 环境变量的切换 ![](../images/postman_screen_16.png) ### 设置通用的登录令牌 当我们有很多接口需要登录令牌头时,如果以前使用的令牌失效了,那所有接口的令牌头都会需要修改,这里可以把登录令牌定义好,再引用,这样令牌失效了,只需要修改一处即可。 ![](../images/postman_screen_20.png) ![](../images/postman_screen_21.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/power_job_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 还在手写任务调度代码?试试这款可视化分布式调度框架! > 在微服务系统中,经常会有用到任务调度的场景。比如每天定时同步数据、定时生成业务报表、定期清理日志等。今天给大家推荐一个分布式调度框架,助你你轻松完成任务调度工作! ## PowerJob 简介 PowerJob是全新一代分布式调度与计算框架,能让你轻松完成任务的调度与繁杂任务的分布式计算。 主要特性: - 使用简单:提供前端Web界面,允许开发者可视化完成调度任务的管理及查看任务运行状态和日志。 - 定时策略完善:支持CRON表达式、固定频率、固定延迟和API四种定时调度策略。 - 执行模式丰富:支持单机、广播、Map、MapReduce四种执行模式。 - 依赖精简:最小仅依赖关系型数据库(MySQL等),扩展依赖为MongoDB(用于存储庞大的在线日志)。 ## 为什么要有调度中心 一般情况下我们会使用`QuartZ`或`Spring Task`这些框架在应用中实现定时任务来进行任务调度,但是在微服务架构下,如果很多应用都充斥着这种任务调度代码就显得有些不合适。合理的方案应该是这样的,任务的执行方法存在于应用中,而我们有一个调度中心,调度中心负责调度这些方法,我们只需在调度中心配置好任务即可,PowerJob正是这样一个分布式调度框架。 ## 安装准备 > 由于PowerJob的调度中心(powerjob-server)需要使用MySQL存储数据,使用MongoDB存储日志,所以我们先安装并启动这两个服务。 - 在Docker容器中启动MySQL服务; ```bash docker run -p 3306:3306 --name mysql \ -v /mydata/mysql/log:/var/log/mysql \ -v /mydata/mysql/data:/var/lib/mysql \ -v /mydata/mysql/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD=root \ -d mysql:5.7 ``` - 在MySQL中创建PowerJob需要的数据库`powerjob-product`; ```sql CREATE DATABASE IF NOT EXISTS `powerjob-product` DEFAULT CHARSET utf8mb4 ``` - 在Docker容器中启动MongoDB服务。 ```bash docker run -p 27017:27017 --name mongo \ -v /mydata/mongo/db:/data/db \ -d mongo:4.2.5 ``` ## 安装调度中心 > 在Docker环境下安装PowerJob的调度中心非常简单,分分钟搞定! - 下载镜像`powerjob-server`的Docker镜像: ```bash docker pull tjqq/powerjob-server:latest ``` - 在Docker容器中运行`powerjob-server`服务: ```bash docker run -p 7700:7700 -p 10086:10086 --name powerjob-server \ --link mysql:db \ --link mongo:mongo \ -e TZ="Asia/Shanghai" \ -e JVMOPTIONS="" \ -e PARAMS="--spring.profiles.active=product --spring.datasource.core.jdbc-url=jdbc:mysql://db:3306/powerjob-product?useUnicode=true&characterEncoding=UTF-8 --spring.datasource.core.username=root --spring.datasource.core.password=root --spring.data.mongodb.uri=mongodb://mongo:27017/powerjob-product" \ -v ~/docker/powerjob-server:/mydata/powerjob/powerjob-server \ -v ~/.m2:/mydata/powerjob/.m2 \ -d tjqq/powerjob-server:latest ``` - 运行成功后即可访问`powerjob-server`的Web界面,注意Linux防火墙需要开放`7700`和`10086`两个端口,访问地址:http://192.168.3.101:7700/ ![](../images/power_job_start_01.png) ## 在应用中初始化执行器 > 安装完调度中心后,我们需要在SpringBoot应用中初始化PowerJob的执行器(powerjob-worker)。 - 首先在`pom.xml`中添加`powerjob-worker`的相关依赖: ```xml com.github.kfcfans powerjob-worker-spring-boot-starter 3.2.3 ``` - 之后在`application.yml`配置文件中添加`powerjob-worker`相关配置,注意`powerjob.worker.app-name`这个配置; ```yaml powerjob: worker: akka-port: 27777 # akka 工作端口 app-name: mall-tiny-powerjob # 接入应用名称,用于分组隔离 server-address: 192.168.3.101:7700 # 调度服务器地址 store-strategy: disk # 持久化方式 ``` - 添加一个单机处理器`StandaloneProcessor`,只需继承`BasicProcessor`接口并实现`process`方法即可; ```java package com.macro.mall.tiny.job; @Slf4j @Component public class StandaloneProcessor implements BasicProcessor { @Override public ProcessResult process(TaskContext context){ //OmsLogger可以直接将日志上报到powerjob-server OmsLogger omsLogger = context.getOmsLogger(); omsLogger.info("StandaloneProcessor start process,context is {}.", context); log.info("jobParams is {}", context.getJobParams()); return new ProcessResult(true, "Process success!"); } } ``` - 打包上传好镜像后,在Docker容器中运行SpringBoot应用服务,注意配置好时区要和调度中心一致。 ```bash docker run -p 8080:8080 --name mall-tiny-powerjob \ --link mysql:db \ -v /etc/localtime:/etc/localtime \ -v /mydata/app/mall-tiny-powerjob/logs:/var/logs \ -e TZ="Asia/Shanghai" \ -d mall-tiny/mall-tiny-powerjob:1.0-SNAPSHOT ``` ## 任务的配置与执行 > 有了执行器和调度中心,我们只需在调度中心中配置好任务即可实现任务调度。 - 首先我们需要在调度中心注册好应用(集成执行器的),应用名称为`application.yml`中的`powerjob.worker.app-name`属性,这里使用的是`mall-tiny-powerjob:123456`; ![](../images/power_job_start_02.png) - 之后我们可以在首页看见一台机器信息; ![](../images/power_job_start_03.png) - 之后在`任务管理`功能中添加一个任务,这里我们用`CRON`方式设置`每20秒`执行执行器中的处理方法; ![](../images/power_job_start_04.png) - 在任务列表中点击`运行`开始执行任务; ![](../images/power_job_start_05.png) - 在任务列表中点击`更多->运行记录`可以查看任务的运行日志; ![](../images/power_job_start_06.png) - 点击`日志`可以查看处理器中上报的日志,`jobParams`为之前我们创建任务时设置的参数; ![](../images/power_job_start_07.png) - 点击`详情`可以查看此次触发任务的结果,即我们在`ProcessResult`中返回的信息。 ![](../images/power_job_start_08.png) ## 参考资料 官方文档:https://github.com/KFCFans/PowerJob ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-powerjob ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/quartz_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # SpringBoot官方支持任务调度框架,轻量级用起来也挺香! > 之前我们讲过一个分布式任务调度框架PowerJob,可以通过可视化的方式来进行任务调度。但是有时候我们只是需要一个轻量级的任务调度功能,而PowerJob需要搭建调度中心未免有些重,这时候SpringBoot官方支持的任务调度框架Quartz就派上用场了!本文主要介绍Quartz在SpringBoot中的使用,让你在实现任务调度上有更多的选择! ## Quartz简介 Quartz是一款功能强大的开源任务调度框架,几乎可以集成到任何Java应用程序中(小到单机应用,大到分布式应用)。Quartz可用于创建简单或复杂的任务调度,用以执行数以万计的任务。任务被定义为标准化的Java组件,Java编写的任务都可以被执行。 ## 核心概念 > Quartz中有一些比较核心的概念,理解它们对使用Quartz很有帮助! - Scheduler(调度器):Quartz中的任务调度器,通过Trigger和JobDetail可以用来调度、暂停和删除任务。 - Trigger(触发器):Quartz中的触发器,可以通过CRON表达式来指定任务执行的时间,时间到了会自动触发任务执行。 - JobDetail(任务详情):Quartz中需要执行的任务详情,包括了任务的唯一标识和具体要执行的任务,可以通过JobDataMap往任务中传递数据。 - Job(任务):Quartz中具体的任务,包含了执行任务的具体方法。 ## CRON表达式 > Cron表达式是一个字符串,包括6~7个时间元素,在Quartz中可以用于指定任务的执行时间。 ### 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日触发一次 | ### 在线CRON表达式生成器 其实CRON表达式无需多记,需要使用的时候直接使用在线生成器就可以了,地址:https://cron.qqe2.com/ ![](../images/quartz_start_01.png) ## 整合SpringBoot使用 > 接下来我们讲下如何在SpringBoot中使用Quartz来实现任务调度,在电商系统中往往会有需要定时发送邮件或者站内信的需求,我们以此为场景来实现下! - Quartz存储任务信息有两种方式,使用内存或者使用数据库来存储,这里我们采用数据库存储的方式,首先需要新建Quartz的相关表,建表脚本在项目的`resources`目录下,名称为`tables_mysql.sql`,创建成功后数据库中多出11张表; ![](../images/quartz_start_02.png) - 接下来在`pom.xml`中添加Quartz的相关依赖即可,SpringBoot官方已经给我们提供好了相关Starter; ```xml org.springframework.boot spring-boot-starter-quartz ``` - 在`application.yml`中添加Quartz相关配置,配置说明直接看注释就好了,主要是对`scheduler`、`jobStore`和`threadPool`进行配置; ```yaml spring: quartz: job-store-type: jdbc # quartz任务存储类型:jdbc或memory wait-for-jobs-to-complete-on-shutdown: true # 关闭时等待任务完成 overwrite-existing-jobs: true # 可以覆盖已有的任务 properties: # quartz原生配置 org: quartz: scheduler: instanceName: scheduler # 调度器实例名称 instanceId: AUTO # 调度器实例ID自动生成 jobStore: class: org.quartz.impl.jdbcjobstore.JobStoreTX # 调度信息存储处理类 driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 使用完全兼容JDBC的驱动 tablePrefix: QRTZ_ # quartz相关表前缀 useProperties: false # 是否将JobDataMap中的属性转为字符串存储 threadPool: class: org.quartz.simpl.SimpleThreadPool # 指定线程池实现类,对调度器提供固定大小的线程池 threadCount: 10 # 设置并发线程数量 threadPriority: 5 # 指定线程优先级 ``` - 创建任务调度业务接口,定义好三个方法,分别为通过CRON表达式调度任务、调度指定时间的任务和取消定时任务; ```java /** * Quartz定时任务操作类 * Created by macro on 2020/9/27. */ public interface ScheduleService { /** * 通过CRON表达式调度任务 */ String scheduleJob(Class jobBeanClass, String cron, String data); /** * 调度指定时间的任务 */ String scheduleFixTimeJob(Class jobBeanClass, Date startTime, String data); /** * 取消定时任务 */ Boolean cancelScheduleJob(String jobName); } ``` - 创建任务调度业务实现类,通过`Scheduler`、`CronTrigger`、`JobDetail`的API实现相关方法; ```java /** * Quartz定时任务操作实现类 * Created by macro on 2020/9/27. */ @Slf4j @Service public class ScheduleServiceImpl implements ScheduleService { @Autowired private Scheduler scheduler; private String defaultGroup = "default_group"; @Override public String scheduleJob(Class jobBeanClass, String cron, String data) { // 创建需要执行的任务 String jobName = UUID.fastUUID().toString(); JobDetail jobDetail = JobBuilder.newJob(jobBeanClass) .withIdentity(jobName, defaultGroup) .usingJobData("data", data) .build(); //创建触发器,指定任务执行时间 CronTrigger cronTrigger = TriggerBuilder.newTrigger() .withIdentity(jobName, defaultGroup) .withSchedule(CronScheduleBuilder.cronSchedule(cron)) .build(); //使用调度器进行任务调度 try { scheduler.scheduleJob(jobDetail, cronTrigger); } catch (SchedulerException e) { e.printStackTrace(); log.info("创建定时任务失败!"); } return jobName; } @Override public String scheduleFixTimeJob(Class jobBeanClass, Date startTime, String data) { //日期转CRON表达式 String startCron = String.format("%d %d %d %d %d ? %d", DateUtil.second(startTime), DateUtil.minute(startTime), DateUtil.hour(startTime, true), DateUtil.dayOfMonth(startTime), DateUtil.month(startTime) + 1, DateUtil.year(startTime)); return scheduleJob(jobBeanClass, startCron, data); } @Override public Boolean cancelScheduleJob(String jobName) { boolean success = false; try { // 暂停触发器 scheduler.pauseTrigger(new TriggerKey(jobName, defaultGroup)); // 移除触发器中的任务 scheduler.unscheduleJob(new TriggerKey(jobName, defaultGroup)); // 删除任务 scheduler.deleteJob(new JobKey(jobName, defaultGroup)); success = true; } catch (SchedulerException e) { e.printStackTrace(); } return success; } } ``` - 定义好需要执行的任务,继承`QuartzJobBean`类,实现`executeInternal`方法即可,这里定义了三个任务,定时发送邮件、定时发送站内信和执行CRON表达式任务; ```java /** * 发送邮件定时任务执行器 * Created by macro on 2020/9/27. */ @Slf4j @Component public class SendEmailJob extends QuartzJobBean { @Autowired private ScheduleService scheduleService; @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { Trigger trigger = jobExecutionContext.getTrigger(); JobDetail jobDetail = jobExecutionContext.getJobDetail(); JobDataMap jobDataMap = jobDetail.getJobDataMap(); String data = jobDataMap.getString("data"); log.info("定时发送邮件操作:{}",data); //完成后删除触发器和任务 scheduleService.cancelScheduleJob(trigger.getKey().getName()); } } ``` ```java /** * 发送站内信定时任务执行器 * Created by macro on 2020/9/27. */ @Slf4j @Component public class SendMessageJob extends QuartzJobBean { @Autowired private ScheduleService scheduleService; @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { Trigger trigger = jobExecutionContext.getTrigger(); JobDetail jobDetail = jobExecutionContext.getJobDetail(); JobDataMap jobDataMap = jobDetail.getJobDataMap(); String data = jobDataMap.getString("data"); log.info("定时发送站内信操作:{}",data); //完成后删除触发器和任务 scheduleService.cancelScheduleJob(trigger.getKey().getName()); } } ``` ```java /** * 使用CRON表达式的任务执行器 * Created by macro on 2020/9/29. */ @Slf4j @Component public class CronProcessJob extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { JobDetail jobDetail = jobExecutionContext.getJobDetail(); JobDataMap jobDataMap = jobDetail.getJobDataMap(); String data = jobDataMap.getString("data"); log.info("CRON表达式任务执行:{}",data); } } ``` - 最后创建好任务调度相关接口,调用任务调度业务类即可。 ```java /** * 定时任务调度相关接口 * Created by macro on 2020/9/29. */ @Api(tags = "ScheduleController", description = "定时任务调度相关接口") @RestController @RequestMapping("/schedule") public class ScheduleController { @Autowired private ScheduleService scheduleService; @ApiOperation("定时发送邮件") @PostMapping("/sendEmail") public CommonResult sendEmail(@RequestParam String startTime,@RequestParam String data) { Date date = DateUtil.parse(startTime, DatePattern.NORM_DATETIME_FORMAT); String jobName = scheduleService.scheduleFixTimeJob(SendEmailJob.class, date, data); return CommonResult.success(jobName); } @ApiOperation("定时发送站内信") @PostMapping("/sendMessage") public CommonResult sendMessage(@RequestParam String startTime,@RequestParam String data) { Date date = DateUtil.parse(startTime, DatePattern.NORM_DATETIME_FORMAT); String jobName = scheduleService.scheduleFixTimeJob(SendMessageJob.class, date, data); return CommonResult.success(jobName); } @ApiOperation("通过CRON表达式调度任务") @PostMapping("/scheduleJob") public CommonResult scheduleJob(@RequestParam String cron, @RequestParam String data) { String jobName = scheduleService.scheduleJob(CronProcessJob.class, cron, data); return CommonResult.success(jobName); } @ApiOperation("取消定时任务") @PostMapping("/cancelScheduleJob") public CommonResult cancelScheduleJob(@RequestParam String jobName) { Boolean success = scheduleService.cancelScheduleJob(jobName); return CommonResult.success(success); } } ``` ## 运行测试 - 调用定时发送邮件接口测试; ![](../images/quartz_start_03.png) - 到点之后发现控制台已经打印任务执行信息; ```bash 2020-09-30 11:23:00.035 INFO 10160 --- [eduler_Worker-1] com.macro.mall.tiny.job.SendEmailJob : 定时发送邮件操作:发送邮件内容 ``` - 使用CRON表达式来启动一个定时任务,从0s开始,每隔10s执行一次; ![](../images/quartz_start_04.png) - 控制台每隔10s打印任务执行信息; ```bash 2020-09-30 11:26:30.024 INFO 10160 --- [eduler_Worker-2] com.macro.mall.tiny.job.CronProcessJob : CRON表达式任务执行:CRON消息内容 2020-09-30 11:26:40.025 INFO 10160 --- [eduler_Worker-3] com.macro.mall.tiny.job.CronProcessJob : CRON表达式任务执行:CRON消息内容 2020-09-30 11:26:50.017 INFO 10160 --- [eduler_Worker-4] com.macro.mall.tiny.job.CronProcessJob : CRON表达式任务执行:CRON消息内容 2020-09-30 11:27:00.023 INFO 10160 --- [eduler_Worker-5] com.macro.mall.tiny.job.CronProcessJob : CRON表达式任务执行:CRON消息内容 2020-09-30 11:27:10.019 INFO 10160 --- [eduler_Worker-6] com.macro.mall.tiny.job.CronProcessJob : CRON表达式任务执行:CRON消息内容 ``` - 我们可以通过启动任务返回的`jobName`,调用取消定时任务的接口来取消任务,调用成功后定时任务不在执行。 ![](../images/quartz_start_05.png) ## 参考资料 官方文档:http://www.quartz-scheduler.org/documentation/ ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-quartz ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/rabbitmq_mqtt_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # RabbitMQ实现即时通讯居然如此简单!连后端代码都省得写了? > 有时候我们的项目中会用到`即时通讯`功能,比如电商系统中的客服聊天功能,还有在支付过程中,当用户支付成功后,第三方支付服务会回调我们的回调接口,此时我们需要通知前端支付成功。最近发现RabbitMQ可以很方便的实现`即时通讯`功能,如果你没有特殊的业务需求,甚至可以不写后端代码,今天给大家讲讲如何使用RabbitMQ来实现`即时通讯`! ## MQTT协议 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的`轻量级`通讯协议,该协议构建于`TCP/IP`协议上。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。 ![](../images/rabbitmq_mqtt_start_11.jpg) ## MQTT相关概念 - Publisher(发布者):消息的发出者,负责发送消息。 - Subscriber(订阅者):消息的订阅者,负责接收并处理消息。 - Broker(代理):消息代理,位于消息发布者和订阅者之间,各类支持MQTT协议的消息中间件都可以充当。 - Topic(主题):可以理解为消息队列中的路由,订阅者订阅了主题之后,就可以收到发送到该主题的消息。 - Payload(负载);可以理解为发送消息的内容。 - QoS(消息质量):全称Quality of Service,即消息的发送质量,主要有`QoS 0`、`QoS 1`、`QoS 2`三个等级,下面分别介绍下: - QoS 0(Almost Once):至多一次,只发送一次,会发生消息丢失或重复; - QoS 1(Atleast Once):至少一次,确保消息到达,但消息重复可能会发生; - QoS 2(Exactly Once):只有一次,确保消息只到达一次。 ## RabbitMQ启用MQTT功能 > RabbitMQ启用MQTT功能,需要先安装然RabbitMQ然后再启用MQTT插件。 - 首先我们需要安装并启动RabbitMQ,对RabbitMQ不了解的朋友可以参考[《花了3天总结的RabbitMQ实用技巧,有点东西!》](https://mp.weixin.qq.com/s/qGg3etLnI38i-G8aFbulWw); - 接下来就是启用RabbitMQ的MQTT插件了,默认是不启用的,使用如下命令开启即可; ```bash rabbitmq-plugins enable rabbitmq_mqtt ``` - 开启成功后,查看管理控制台,我们可以发现MQTT服务运行在`1883`端口上了。 ![](../images/rabbitmq_mqtt_start_01.png) ## MQTT客户端 > 我们可以使用MQTT客户端来测试MQTT的即时通讯功能,这里使用的是`MQTTBox`这个客户端工具。 - 首先下载并安装好`MQTTBox`,下载地址:http://workswithweb.com/mqttbox.html ![](../images/rabbitmq_mqtt_start_02.png) - 点击`Create MQTT Client`按钮来创建一个MQTT客户端; ![](../images/rabbitmq_mqtt_start_03.png) - 接下来对MQTT客户端进行配置,主要是配置好协议端口、连接用户名密码和QoS即可; ![](../images/rabbitmq_mqtt_start_04.png) - 再配置一个订阅者,订阅者订阅`testTopicA`这个主题,我们会向这个主题发送消息; ![](../images/rabbitmq_mqtt_start_05.png) - 发布者向主题中发布消息,订阅者可以实时接收到。 ![](../images/rabbitmq_mqtt_start_06.gif) ## 前端直接实现即时通讯 > 既然`MQTTBox`客户端可以直接通过RabbitMQ实现即时通讯,那我们是不是直接使用前端技术也可以实现即时通讯?答案是肯定的!下面我们将通过`html+javascript`实现一个简单的聊天功能,真正不写一行后端代码实现即时通讯! - 由于RabbitMQ与Web端交互底层使用的是WebSocket,所以我们需要开启RabbitMQ的MQTT WEB支持,使用如下命令开启即可; ```bash rabbitmq-plugins enable rabbitmq_web_mqtt ``` - 开启成功后,查看管理控制台,我们可以发现MQTT的WEB服务运行在`15675`端口上了; ![](../images/rabbitmq_mqtt_start_07.png) - WEB端与MQTT服务进行通讯需要使用一个叫`MQTT.js`的库,项目地址:https://github.com/mqttjs/MQTT.js ![](../images/rabbitmq_mqtt_start_08.png) - 实现的功能非常简单,一个单聊功能,需要注意的是配置好MQTT服务的访问地址为:ws://localhost:15675/ws ```html Title


``` - 接下来我们订阅不同的主题开启两个页面测试下功能(页面放在了SpringBoot应用的resource目录下了,需要先启动应用再访问): - 第一个订阅主题`testTopicA`,访问地址:http://localhost:8088/page/index?topic=testTopicA - 第二个订阅主题`testTopicB`,访问地址:http://localhost:8088/page/index?topic=testTopicB - 之后互相发送消息,让我们来看看效果吧! ![](../images/rabbitmq_mqtt_start_09.gif) ## 在SpringBoot中使用 > 没有特殊业务需求的时候,前端可以直接和RabbitMQ对接实现即时通讯。但是有时候我们需要通过服务端去通知前端,此时就需要在应用中集成MQTT了,接下来我们来讲讲如何在SpringBoot应用中使用MQTT。 - 首先我们需要在`pom.xml`中添加MQTT相关依赖; ```xml org.springframework.integration spring-integration-mqtt ``` - 在`application.yml`中添加MQTT相关配置,主要是访问地址、用户名密码、默认主题信息; ```yaml rabbitmq: mqtt: url: tcp://localhost:1883 username: guest password: guest defaultTopic: testTopic ``` - 编写一个Java配置类从配置文件中读取配置便于使用; ```java /** * MQTT相关配置 * Created by macro on 2020/9/15. */ @Data @EqualsAndHashCode(callSuper = false) @Component @ConfigurationProperties(prefix = "rabbitmq.mqtt") public class MqttConfig { /** * RabbitMQ连接用户名 */ private String username; /** * RabbitMQ连接密码 */ private String password; /** * RabbitMQ的MQTT默认topic */ private String defaultTopic; /** * RabbitMQ的MQTT连接地址 */ private String url; } ``` - 添加MQTT消息订阅者相关配置,使用`@ServiceActivator`注解声明一个服务激活器,通过`MessageHandler`来处理订阅消息; ```java /** * MQTT消息订阅者相关配置 * Created by macro on 2020/9/15. */ @Slf4j @Configuration public class MqttInboundConfig { @Autowired private MqttConfig mqttConfig; @Bean public MessageChannel mqttInputChannel() { return new DirectChannel(); } @Bean public MessageProducer inbound() { MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(mqttConfig.getUrl(), "subscriberClient", mqttConfig.getDefaultTopic()); adapter.setCompletionTimeout(5000); adapter.setConverter(new DefaultPahoMessageConverter()); //设置消息质量:0->至多一次;1->至少一次;2->只有一次 adapter.setQos(1); adapter.setOutputChannel(mqttInputChannel()); return adapter; } @Bean @ServiceActivator(inputChannel = "mqttInputChannel") public MessageHandler handler() { return new MessageHandler() { @Override public void handleMessage(Message message) throws MessagingException { //处理订阅消息 log.info("handleMessage : {}",message.getPayload()); } }; } } ``` - 添加MQTT消息发布者相关配置; ```java /** * MQTT消息发布者相关配置 * Created by macro on 2020/9/15. */ @Configuration public class MqttOutboundConfig { @Autowired private MqttConfig mqttConfig; @Bean public MqttPahoClientFactory mqttClientFactory() { DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); MqttConnectOptions options = new MqttConnectOptions(); options.setServerURIs(new String[] { mqttConfig.getUrl()}); options.setUserName(mqttConfig.getUsername()); options.setPassword(mqttConfig.getPassword().toCharArray()); factory.setConnectionOptions(options); return factory; } @Bean @ServiceActivator(inputChannel = "mqttOutboundChannel") public MessageHandler mqttOutbound() { MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler("publisherClient", mqttClientFactory()); messageHandler.setAsync(true); messageHandler.setDefaultTopic(mqttConfig.getDefaultTopic()); return messageHandler; } @Bean public MessageChannel mqttOutboundChannel() { return new DirectChannel(); } } ``` - 添加MQTT网关,用于向主题中发送消息; ```java /** * MQTT网关,通过接口将数据传递到集成流 * Created by macro on 2020/9/15. */ @Component @MessagingGateway(defaultRequestChannel = "mqttOutboundChannel") public interface MqttGateway { /** * 发送消息到默认topic */ void sendToMqtt(String payload); /** * 发送消息到指定topic */ void sendToMqtt(String payload, @Header(MqttHeaders.TOPIC) String topic); /** * 发送消息到指定topic并设置QOS */ void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload); } ``` - 添加MQTT测试接口,使用MQTT网关向特定主题中发送消息; ```java /** * MQTT测试接口 * Created by macro on 2020/9/15. */ @Api(tags = "MqttController", description = "MQTT测试接口") @RestController @RequestMapping("/mqtt") public class MqttController { @Autowired private MqttGateway mqttGateway; @PostMapping("/sendToDefaultTopic") @ApiOperation("向默认主题发送消息") public CommonResult sendToDefaultTopic(String payload) { mqttGateway.sendToMqtt(payload); return CommonResult.success(null); } @PostMapping("/sendToTopic") @ApiOperation("向指定主题发送消息") public CommonResult sendToTopic(String payload, String topic) { mqttGateway.sendToMqtt(payload, topic); return CommonResult.success(null); } } ``` - 调用接口向主题中发送消息进行测试; ![](../images/rabbitmq_mqtt_start_10.png) - 后台成功接收到消息并进行打印。 ```bash 2020-09-17 14:29:01.689 INFO 11192 --- [ubscriberClient] c.m.mall.tiny.config.MqttInboundConfig : handleMessage : 来自网页上的消息 2020-09-17 14:29:06.101 INFO 11192 --- [ubscriberClient] c.m.mall.tiny.config.MqttInboundConfig : handleMessage : 来自网页上的消息 2020-09-17 14:29:07.384 INFO 11192 --- [ubscriberClient] c.m.mall.tiny.config.MqttInboundConfig : handleMessage : 来自网页上的消息 ``` ## 总结 消息中间件应用越来越广泛,不仅可以实现可靠的异步通信,还可以实现即时通讯,掌握一个消息中间件还是很有必要的。如果没有特殊业务需求,客户端或者前端直接使用MQTT对接消息中间件即可实现即时通讯,有特殊需求的时候也可以使用SpringBoot集成MQTT的方式来实现,总之消息中间件是实现即时通讯的一个好选择! ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-mqtt ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/rabbitmq_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 连RabbitMQ的5种核心消息模式都不懂,也敢说自己会用消息队列! > 以前看过的关于RabbitMQ核心消息模式的文章都是基于Java API的,最近看了下官方文档,发现这些核心消息模式都可以通过Spring AMQP来实现。于是总结了下RabbitMQ的核心知识点,包括RabbitMQ在Windows和Linux下的安装、5种核心消息模式的Spring AMQP实现,相信对于想要学习和回顾RabbitMQ的朋友都会有所帮助。 ## 简介 RabbitMQ是最受欢迎的开源消息中间件之一,在全球范围内被广泛应用。RabbitMQ是轻量级且易于部署的,能支持多种消息协议。RabbitMQ可以部署在分布式系统中,以满足大规模、高可用的要求。 ## 相关概念 我们先来了解下RabbitMQ中的相关概念,这里以5种消息模式中的`路由模式`为例。 ![](../images/rabbitmq_start_01.png) 标志 | 中文名 | 英文名| 描述 ----|----|----|---- P |生产者 |Producer |消息的发送者,可以将消息发送到交换机 C |消费者 |Consumer |消息的接收者,从队列中获取消息并进行消费 X |交换机 |Exchange |接收生产者发送的消息,并根据路由键发送给指定队列 Q |队列 |Queue |存储从交换机发来的消息 type | 交换机类型 |type | 不同类型的交换机转发消息方式不同 fanout | 发布/订阅模式 |fanout | 广播消息给所有绑定交换机的队列 direct | 路由模式 |direct | 根据路由键发送消息 topic | 通配符模式 |topic | 根据路由键的匹配规则发送消息 ## 安装及配置 > 接下来我们介绍下RabbitMQ的安装和配置,提供Windows和Linux两种安装方式。 ### Windows下的安装 - 安装Erlang,下载地址:http://erlang.org/download/otp_win64_21.3.exe ![](../images/rabbitmq_start_02.png) - 安装RabbitMQ,下载地址:https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe ![](../images/rabbitmq_start_03.png) - 安装完成后,进入RabbitMQ安装目录下的sbin目录; ![](../images/rabbitmq_start_04.png) - 在地址栏输入cmd并回车启动命令行,然后输入以下命令启动管理功能。 ``` rabbitmq-plugins enable rabbitmq_management ``` ![](../images/rabbitmq_start_05.png) ### Linux下的安装 - 下载`rabbitmq 3.7.15`的Docker镜像; ```bash docker pull rabbitmq:3.7.15 ``` - 使用Docker命令启动服务; ```bash docker run -p 5672:5672 -p 15672:15672 --name rabbitmq \ -d rabbitmq:3.7.15 ``` - 进入容器并开启管理功能; ```bash docker exec -it rabbitmq /bin/bash rabbitmq-plugins enable rabbitmq_management ``` ![](../images/rabbitmq_start_11.png) - 开启防火墙便于外网访问。 ```bash firewall-cmd --zone=public --add-port=15672/tcp --permanent firewall-cmd --zone=public --add-port=5672/tcp --permanent firewall-cmd --reload ``` ### 访问及配置 - 访问RabbitMQ管理页面地址,查看是否安装成功(Linux下使用服务器IP访问即可):http://localhost:15672/ ![](../images/rabbitmq_start_06.png) - 输入账号密码并登录,这里使用默认账号密码登录:guest guest - 创建帐号并设置其角色为管理员:mall mall ![](../images/rabbitmq_start_07.png) - 创建一个新的虚拟host为:/mall ![](../images/rabbitmq_start_08.png) - 点击mall用户进入用户配置页面; ![](../images/rabbitmq_start_09.png) - 给mall用户配置该虚拟host的权限; ![](../images/rabbitmq_start_10.png) - 至此,RabbitMQ的配置完成。 ## 5种消息模式 > 这5种消息模式是构建基于RabbitMQ的消息应用的基础,一定要牢牢掌握它们。学过RabbitMQ的朋友应该了解过这些消息模式的Java实现,这里我们使用Spring AMQP的形式来实现它们。 ### 简单模式 > 简单模式是最简单的消息模式,它包含一个生产者、一个消费者和一个队列。生产者向队列里发送消息,消费者从队列中获取消息并消费。 #### 模式示意图 ![](../images/rabbitmq_start_12.png) #### Spring AMQP实现 - 首先需要在`pom.xml`中添加Spring AMQP的相关依赖; ```xml org.springframework.boot spring-boot-starter-amqp ``` - 然后修改`application.yml`,添加RabbitMQ的相关配置; ```yaml spring: rabbitmq: host: localhost port: 5672 virtual-host: /mall username: mall password: mall publisher-confirms: true #消息发送到交换器确认 publisher-returns: true #消息发送到队列确认 ``` - 添加`简单模式`相关Java配置,创建一个名为`simple.hello`的队列、一个生产者和一个消费者; ```java /** * Created by macro on 2020/5/19. */ @Configuration public class SimpleRabbitConfig { @Bean public Queue hello() { return new Queue("simple.hello"); } @Bean public SimpleSender simpleSender(){ return new SimpleSender(); } @Bean public SimpleReceiver simpleReceiver(){ return new SimpleReceiver(); } } ``` - 生产者通过`send方法`向队列`simple.hello`中发送消息; ```java /** * Created by macro on 2020/5/19. */ public class SimpleSender { private static final Logger LOGGER = LoggerFactory.getLogger(SimpleSender.class); @Autowired private RabbitTemplate template; private static final String queueName="simple.hello"; public void send() { String message = "Hello World!"; this.template.convertAndSend(queueName, message); LOGGER.info(" [x] Sent '{}'", message); } } ``` - 消费者从队列`simple.hello`中获取消息; ```java /** * Created by macro on 2020/5/19. */ @RabbitListener(queues = "simple.hello") public class SimpleReceiver { private static final Logger LOGGER = LoggerFactory.getLogger(SimpleReceiver.class); @RabbitHandler public void receive(String in) { LOGGER.info(" [x] Received '{}'", in); } } ``` - 在controller中添加测试接口,调用该接口开始发送消息; ```java /** * Created by macro on 2020/5/19. */ @Api(tags = "RabbitController", description = "RabbitMQ功能测试") @Controller @RequestMapping("/rabbit") public class RabbitController { @Autowired private SimpleSender simpleSender; @ApiOperation("简单模式") @RequestMapping(value = "/simple", method = RequestMethod.GET) @ResponseBody public CommonResult simpleTest() { for(int i=0;i<10;i++){ simpleSender.send(); ThreadUtil.sleep(1000); } return CommonResult.success(null); } } ``` - 运行后结果如下,可以发现生产者往队列中发送消息,消费者从队列中获取消息并消费。 ![](../images/rabbitmq_start_13.png) ![](../images/rabbitmq_start_14.png) ### 工作模式 > 工作模式是指向多个互相竞争的消费者发送消息的模式,它包含一个生产者、两个消费者和一个队列。两个消费者同时绑定到一个队列上去,当消费者获取消息处理耗时任务时,空闲的消费者从队列中获取并消费消息。 #### 模式示意图 ![](../images/rabbitmq_start_15.png) #### Spring AMQP实现 - 添加`工作模式`相关Java配置,创建一个名为`work.hello`的队列、一个生产者和两个消费者; ```java /** * Created by macro on 2020/5/19. */ @Configuration public class WorkRabbitConfig { @Bean public Queue workQueue() { return new Queue("work.hello"); } @Bean public WorkReceiver workReceiver1() { return new WorkReceiver(1); } @Bean public WorkReceiver workReceiver2() { return new WorkReceiver(2); } @Bean public WorkSender workSender() { return new WorkSender(); } } ``` - 生产者通过`send方法`向队列`work.hello`中发送消息,消息中包含一定数量的`.`号; ```java /** * Created by macro on 2020/5/19. */ public class WorkSender { private static final Logger LOGGER = LoggerFactory.getLogger(WorkSender.class); @Autowired private RabbitTemplate template; private static final String queueName = "work.hello"; public void send(int index) { StringBuilder builder = new StringBuilder("Hello"); int limitIndex = index % 3+1; for (int i = 0; i < limitIndex; i++) { builder.append('.'); } builder.append(index+1); String message = builder.toString(); template.convertAndSend(queueName, message); LOGGER.info(" [x] Sent '{}'", message); } } ``` - 两个消费者从队列`work.hello`中获取消息,名称分别为`instance 1`和`instance 2`,消息中包含`.`号越多,耗时越长; ```java /** * Created by macro on 2020/5/19. */ @RabbitListener(queues = "work.hello") public class WorkReceiver { private static final Logger LOGGER = LoggerFactory.getLogger(WorkReceiver.class); private final int instance; public WorkReceiver(int i) { this.instance = i; } @RabbitHandler public void receive(String in) { StopWatch watch = new StopWatch(); watch.start(); LOGGER.info("instance {} [x] Received '{}'", this.instance, in); doWork(in); watch.stop(); LOGGER.info("instance {} [x] Done in {}s", this.instance, watch.getTotalTimeSeconds()); } private void doWork(String in) { for (char ch : in.toCharArray()) { if (ch == '.') { ThreadUtil.sleep(1000); } } } } ``` - 在controller中添加测试接口,调用该接口开始发送消息; ```java /** * Created by macro on 2020/5/19. */ @Api(tags = "RabbitController", description = "RabbitMQ功能测试") @Controller @RequestMapping("/rabbit") public class RabbitController { @Autowired private WorkSender workSender; @ApiOperation("工作模式") @RequestMapping(value = "/work", method = RequestMethod.GET) @ResponseBody public CommonResult workTest() { for(int i=0;i<10;i++){ workSender.send(i); ThreadUtil.sleep(1000); } return CommonResult.success(null); } } ``` - 运行后结果如下,可以发现生产者往队列中发送包含不同数量`.`号的消息,`instance 1`和`instance 2`消费者互相竞争,分别消费了一部分消息。 ![](../images/rabbitmq_start_16.png) ![](../images/rabbitmq_start_17.png) ![](../images/rabbitmq_start_18.png) ### 发布/订阅模式 > 发布/订阅模式是指同时向多个消费者发送消息的模式(类似广播的形式),它包含一个生产者、两个消费者、两个队列和一个交换机。两个消费者同时绑定到不同的队列上去,两个队列绑定到交换机上去,生产者通过发送消息到交换机,所有消费者接收并消费消息。 #### 模式示意图 ![](../images/rabbitmq_start_19.png) #### Spring AMQP实现 - 添加`发布/订阅模式`相关Java配置,创建一个名为`exchange.fanout`的交换机、一个生产者、两个消费者和两个匿名队列,将两个匿名队列都绑定到交换机; ```java /** * Created by macro on 2020/5/19. */ @Configuration public class FanoutRabbitConfig { @Bean public FanoutExchange fanout() { return new FanoutExchange("exchange.fanout"); } @Bean public Queue fanoutQueue1() { return new AnonymousQueue(); } @Bean public Queue fanoutQueue2() { return new AnonymousQueue(); } @Bean public Binding fanoutBinding1(FanoutExchange fanout, Queue fanoutQueue1) { return BindingBuilder.bind(fanoutQueue1).to(fanout); } @Bean public Binding fanoutBinding2(FanoutExchange fanout, Queue fanoutQueue2) { return BindingBuilder.bind(fanoutQueue2).to(fanout); } @Bean public FanoutReceiver fanoutReceiver() { return new FanoutReceiver(); } @Bean public FanoutSender fanoutSender() { return new FanoutSender(); } } ``` - 生产者通过`send方法`向交换机`exchange.fanout`中发送消息,消息中包含一定数量的`.`号; ```java /** * Created by macro on 2020/5/19. */ public class FanoutSender { private static final Logger LOGGER = LoggerFactory.getLogger(FanoutSender.class); @Autowired private RabbitTemplate template; private static final String exchangeName = "exchange.fanout"; public void send(int index) { StringBuilder builder = new StringBuilder("Hello"); int limitIndex = index % 3 + 1; for (int i = 0; i < limitIndex; i++) { builder.append('.'); } builder.append(index + 1); String message = builder.toString(); template.convertAndSend(exchangeName, "", message); LOGGER.info(" [x] Sent '{}'", message); } } ``` - 消费者从绑定的匿名队列中获取消息,消息中包含`.`号越多,耗时越长,由于该消费者可以从两个队列中获取并消费消息,可以看做两个消费者,名称分别为`instance 1`和`instance 2`; ```java /** * Created by macro on 2020/5/19. */ public class FanoutReceiver { private static final Logger LOGGER = LoggerFactory.getLogger(FanoutReceiver.class); @RabbitListener(queues = "#{fanoutQueue1.name}") public void receive1(String in) { receive(in, 1); } @RabbitListener(queues = "#{fanoutQueue2.name}") public void receive2(String in) { receive(in, 2); } private void receive(String in, int receiver) { StopWatch watch = new StopWatch(); watch.start(); LOGGER.info("instance {} [x] Received '{}'", receiver, in); doWork(in); watch.stop(); LOGGER.info("instance {} [x] Done in {}s", receiver, watch.getTotalTimeSeconds()); } private void doWork(String in) { for (char ch : in.toCharArray()) { if (ch == '.') { ThreadUtil.sleep(1000); } } } } ``` - 在controller中添加测试接口,调用该接口开始发送消息; ```java /** * Created by macro on 2020/5/19. */ @Api(tags = "RabbitController", description = "RabbitMQ功能测试") @Controller @RequestMapping("/rabbit") public class RabbitController { @Autowired private FanoutSender fanoutSender; @ApiOperation("发布/订阅模式") @RequestMapping(value = "/fanout", method = RequestMethod.GET) @ResponseBody public CommonResult fanoutTest() { for(int i=0;i<10;i++){ fanoutSender.send(i); ThreadUtil.sleep(1000); } return CommonResult.success(null); } } ``` - 运行后结果如下,可以发现生产者往队列中发送包含不同数量`.`号的消息,`instance 1`和`instance 2`同时获取并消费了消息。 ![](../images/rabbitmq_start_20.png) ![](../images/rabbitmq_start_21.png) ![](../images/rabbitmq_start_22.png) ### 路由模式 > 路由模式是可以根据`路由键`选择性给多个消费者发送消息的模式,它包含一个生产者、两个消费者、两个队列和一个交换机。两个消费者同时绑定到不同的队列上去,两个队列通过`路由键`绑定到交换机上去,生产者发送消息到交换机,交换机通过`路由键`转发到不同队列,队列绑定的消费者接收并消费消息。 #### 模式示意图 ![](../images/rabbitmq_start_23.png) #### Spring AMQP实现 - 添加`路由模式`相关Java配置,创建一个名为`exchange.direct`的交换机、一个生产者、两个消费者和两个匿名队列,队列通过`路由键`都绑定到交换机,`队列1`的路由键为`orange`和`black`,`队列2`的路由键为`green`和`black`; ```java /** * Created by macro on 2020/5/19. */ @Configuration public class DirectRabbitConfig { @Bean public DirectExchange direct() { return new DirectExchange("exchange.direct"); } @Bean public Queue directQueue1() { return new AnonymousQueue(); } @Bean public Queue directQueue2() { return new AnonymousQueue(); } @Bean public Binding directBinding1a(DirectExchange direct, Queue directQueue1) { return BindingBuilder.bind(directQueue1).to(direct).with("orange"); } @Bean public Binding directBinding1b(DirectExchange direct, Queue directQueue1) { return BindingBuilder.bind(directQueue1).to(direct).with("black"); } @Bean public Binding directBinding2a(DirectExchange direct, Queue directQueue2) { return BindingBuilder.bind(directQueue2).to(direct).with("green"); } @Bean public Binding directBinding2b(DirectExchange direct, Queue directQueue2) { return BindingBuilder.bind(directQueue2).to(direct).with("black"); } @Bean public DirectReceiver receiver() { return new DirectReceiver(); } @Bean public DirectSender directSender() { return new DirectSender(); } } ``` - 生产者通过`send方法`向交换机`exchange.direct`中发送消息,发送时使用不同的`路由键`,根据`路由键`会被转发到不同的队列; ```java /** * Created by macro on 2020/5/19. */ public class DirectSender { @Autowired private RabbitTemplate template; private static final String exchangeName = "exchange.direct"; private final String[] keys = {"orange", "black", "green"}; private static final Logger LOGGER = LoggerFactory.getLogger(DirectSender.class); public void send(int index) { StringBuilder builder = new StringBuilder("Hello to "); int limitIndex = index % 3; String key = keys[limitIndex]; builder.append(key).append(' '); builder.append(index+1); String message = builder.toString(); template.convertAndSend(exchangeName, key, message); LOGGER.info(" [x] Sent '{}'", message); } } ``` - 消费者从自己绑定的匿名队列中获取消息,由于该消费者可以从两个队列中获取并消费消息,可以看做两个消费者,名称分别为`instance 1`和`instance 2`; ```java /** * Created by macro on 2020/5/19. */ public class DirectReceiver { private static final Logger LOGGER = LoggerFactory.getLogger(DirectReceiver.class); @RabbitListener(queues = "#{directQueue1.name}") public void receive1(String in){ receive(in, 1); } @RabbitListener(queues = "#{directQueue2.name}") public void receive2(String in){ receive(in, 2); } private void receive(String in, int receiver){ StopWatch watch = new StopWatch(); watch.start(); LOGGER.info("instance {} [x] Received '{}'", receiver, in); doWork(in); watch.stop(); LOGGER.info("instance {} [x] Done in {}s", receiver, watch.getTotalTimeSeconds()); } private void doWork(String in){ for (char ch : in.toCharArray()) { if (ch == '.') { ThreadUtil.sleep(1000); } } } } ``` - 在controller中添加测试接口,调用该接口开始发送消息; ```java /** * Created by macro on 2020/5/19. */ @Api(tags = "RabbitController", description = "RabbitMQ功能测试") @Controller @RequestMapping("/rabbit") public class RabbitController { @Autowired private DirectSender directSender; @ApiOperation("路由模式") @RequestMapping(value = "/direct", method = RequestMethod.GET) @ResponseBody public CommonResult directTest() { for(int i=0;i<10;i++){ directSender.send(i); ThreadUtil.sleep(1000); } return CommonResult.success(null); } } ``` - 运行后结果如下,可以发现生产者往队列中发送包含不同`路由键`的消息,`instance 1`获取到了`orange`和`black`消息,`instance 2`获取到了`green`和`black`消息。 ![](../images/rabbitmq_start_24.png) ![](../images/rabbitmq_start_25.png) ![](../images/rabbitmq_start_26.png) ### 通配符模式 > 通配符模式是可以根据`路由键匹配规则`选择性给多个消费者发送消息的模式,它包含一个生产者、两个消费者、两个队列和一个交换机。两个消费者同时绑定到不同的队列上去,两个队列通过`路由键匹配规则`绑定到交换机上去,生产者发送消息到交换机,交换机通过`路由键匹配规则`转发到不同队列,队列绑定的消费者接收并消费消息。 #### 特殊匹配符号 - `*`:只能匹配一个单词; - `#`:可以匹配零个或多个单词。 #### 模式示意图 ![](../images/rabbitmq_start_27.png) #### Spring AMQP实现 - 添加`通配符模式`相关Java配置,创建一个名为`exchange.topic`的交换机、一个生产者、两个消费者和两个匿名队列,匹配`*.orange.*`和`*.*.rabbit`发送到`队列1`,匹配`lazy.#`发送到`队列2`; ```java /** * Created by macro on 2020/5/19. */ @Configuration public class TopicRabbitConfig { @Bean public TopicExchange topic() { return new TopicExchange("exchange.topic"); } @Bean public Queue topicQueue1() { return new AnonymousQueue(); } @Bean public Queue topicQueue2() { return new AnonymousQueue(); } @Bean public Binding topicBinding1a(TopicExchange topic, Queue topicQueue1) { return BindingBuilder.bind(topicQueue1).to(topic).with("*.orange.*"); } @Bean public Binding topicBinding1b(TopicExchange topic, Queue topicQueue1) { return BindingBuilder.bind(topicQueue1).to(topic).with("*.*.rabbit"); } @Bean public Binding topicBinding2a(TopicExchange topic, Queue topicQueue2) { return BindingBuilder.bind(topicQueue2).to(topic).with("lazy.#"); } @Bean public TopicReceiver topicReceiver() { return new TopicReceiver(); } @Bean public TopicSender topicSender() { return new TopicSender(); } } ``` - 生产者通过`send方法`向交换机`exchange.topic`中发送消息,消息中包含不同的`路由键`; ```java /** * Created by macro on 2020/5/19. */ public class TopicSender { @Autowired private RabbitTemplate template; private static final String exchangeName = "exchange.topic"; private static final Logger LOGGER = LoggerFactory.getLogger(TopicSender.class); private final String[] keys = {"quick.orange.rabbit", "lazy.orange.elephant", "quick.orange.fox", "lazy.brown.fox", "lazy.pink.rabbit", "quick.brown.fox"}; public void send(int index) { StringBuilder builder = new StringBuilder("Hello to "); int limitIndex = index%keys.length; String key = keys[limitIndex]; builder.append(key).append(' '); builder.append(index+1); String message = builder.toString(); template.convertAndSend(exchangeName, key, message); LOGGER.info(" [x] Sent '{}'",message); System.out.println(" [x] Sent '" + message + "'"); } } ``` - 消费者从自己绑定的匿名队列中获取消息,由于该消费者可以从两个队列中获取并消费消息,可以看做两个消费者,名称分别为`instance 1`和`instance 2`; ```java /** * Created by macro on 2020/5/19. */ public class TopicReceiver { private static final Logger LOGGER = LoggerFactory.getLogger(TopicReceiver.class); @RabbitListener(queues = "#{topicQueue1.name}") public void receive1(String in){ receive(in, 1); } @RabbitListener(queues = "#{topicQueue2.name}") public void receive2(String in){ receive(in, 2); } public void receive(String in, int receiver){ StopWatch watch = new StopWatch(); watch.start(); LOGGER.info("instance {} [x] Received '{}'", receiver, in); doWork(in); watch.stop(); LOGGER.info("instance {} [x] Done in {}s", receiver, watch.getTotalTimeSeconds()); } private void doWork(String in){ for (char ch : in.toCharArray()) { if (ch == '.') { ThreadUtil.sleep(1000); } } } } ``` - 在controller中添加测试接口,调用该接口开始发送消息; ```java /** * Created by macro on 2020/5/19. */ @Api(tags = "RabbitController", description = "RabbitMQ功能测试") @Controller @RequestMapping("/rabbit") public class RabbitController { @Autowired private TopicSender topicSender; @ApiOperation("通配符模式") @RequestMapping(value = "/topic", method = RequestMethod.GET) @ResponseBody public CommonResult topicTest() { for(int i=0;i<10;i++){ topicSender.send(i); ThreadUtil.sleep(1000); } return CommonResult.success(null); } } ``` - 运行后结果如下,可以发现生产者往队列中发送包含不同`路由键`的消息,`instance 1`和`instance 2`分别获取到了匹配的消息。 ![](../images/rabbitmq_start_28.png) ![](../images/rabbitmq_start_29.png) ![](../images/rabbitmq_start_30.png) ## 参考资料 RabbitMQ Tutorials:https://www.rabbitmq.com/getstarted.html ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-rabbit ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/redis_cluster.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Docker环境下秒建Redis集群,连SpringBoot也整上了! > 为了提高Redis的存储容量和响应速度,有时候我们需要搭建Redis集群。本文主要讲述Redis集群环境的搭建步骤以及如何在SpringBoot中整合使用Redis集群。 ## Redis集群搭建 > 这里我们使用最方便的搭建方式,使用Docker Compose来搭建,对Docker Compose不了解的朋友可以参考下[《使用Docker Compose部署SpringBoot应用》](https://mp.weixin.qq.com/s/iMl9bJ4SxUsNHBbiS5VUcw)。我们将搭建一个6节点的Redis集群,包括3个主节点和3个从节点。 - 在搭建Redis集群之前,我们需要修改下Redis的配置文件`redis.conf`,该文件的下载地址:https://github.com/antirez/redis/blob/5.0/redis.conf - 需要修改的属性如下,主要是修改了一些集群配置和运行端口,端口号需要按需修改为6391~6396: ``` # 开启集群功能 cluster-enabled yes # 设置运行端口 port 6391 # 设置节点超时时间,单位毫秒 cluster-node-timeout 15000 # 集群内部配置文件 cluster-config-file "nodes-6391.conf" ``` - 然后我们需要编写docker-compose.yml文件用于编排6个Redis容器,具体属性的作用可以参考下面的注释; ```yaml version: "3" services: redis-master1: image: redis:5.0 # 基础镜像 container_name: redis-master1 # 容器名称 working_dir: /config # 切换工作目录 environment: # 环境变量 - PORT=6391 # 会使用config/nodes-${PORT}.conf这个配置文件 ports: # 映射端口,对外提供服务 - 6391:6391 # redis的服务端口 - 16391:16391 # redis集群监控端口 stdin_open: true # 标准输入打开 tty: true # 后台运行不退出 network_mode: host # 使用host模式 privileged: true # 拥有容器内命令执行的权限 volumes: - /mydata/redis-cluster/config:/config #配置文件目录映射到宿主机 entrypoint: # 设置服务默认的启动程序 - /bin/bash - redis.sh redis-master2: image: redis:5.0 working_dir: /config container_name: redis-master2 environment: - PORT=6392 ports: - 6392:6392 - 16392:16392 stdin_open: true network_mode: host tty: true privileged: true volumes: - /mydata/redis-cluster/config:/config entrypoint: - /bin/bash - redis.sh redis-master3: image: redis:5.0 container_name: redis-master3 working_dir: /config environment: - PORT=6393 ports: - 6393:6393 - 16393:16393 stdin_open: true network_mode: host tty: true privileged: true volumes: - /mydata/redis-cluster/config:/config entrypoint: - /bin/bash - redis.sh redis-slave1: image: redis:5.0 container_name: redis-slave1 working_dir: /config environment: - PORT=6394 ports: - 6394:6394 - 16394:16394 stdin_open: true network_mode: host tty: true privileged: true volumes: - /mydata/redis-cluster/config:/config entrypoint: - /bin/bash - redis.sh redis-slave2: image: redis:5.0 working_dir: /config container_name: redis-slave2 environment: - PORT=6395 ports: - 6395:6395 - 16395:16395 stdin_open: true network_mode: host tty: true privileged: true volumes: - /mydata/redis-cluster/config:/config entrypoint: - /bin/bash - redis.sh redis-slave3: image: redis:5.0 container_name: redis-slave3 working_dir: /config environment: - PORT=6396 ports: - 6396:6396 - 16396:16396 stdin_open: true network_mode: host tty: true privileged: true volumes: - /mydata/redis-cluster/config:/config entrypoint: - /bin/bash - redis.sh ``` - 从docker-compose.yml文件中我们可以看到,我们的Redis容器分别运行在6391~6396这6个端口之上, 将容器中的`/config`配置目录映射到了宿主机的`/mydata/redis-cluster/config`目录,同时还以`redis.sh`脚本作为该容器的启动脚本; - `redis.sh`脚本的作用是根据environment环境变量中的`PORT`属性,以指定配置文件来启动Redis容器; ```bash redis-server /config/nodes-${PORT}.conf ``` - 接下来我们需要把Redis的配置文件和`redis.sh`上传到Linux服务器的`/mydata/redis-cluster/config`目录下; ![](../images/redis_cluster_01.png) - 接下来上传我们的docker-compose.yml文件到Linux服务器,并使用docker-compose命令来启动所有容器; ```bash docker-compose up -d ``` - 启动过程中会输出如下信息; ![](../images/redis_cluster_02.png) - 此时进入其中一个Redis容器之中,初始化Redis集群; ```bash # 进入Redis容器 docker exec -it redis-master1 /bin/bash # 初始化Redis集群命令 redis-cli --cluster create \ 192.168.6.139:6391 192.168.6.139:6392 192.168.6.139:6393 \ 192.168.6.139:6394 192.168.6.139:6395 192.168.6.139:6396 \ --cluster-replicas 1 ``` - 集群创建过程中会让你确认配置,输入`yes`确认即可; ![](../images/redis_cluster_03.png) - Redis集群创建成功后会输出如下信息; ![](../images/redis_cluster_04.png) - 创建成功后我们可以使用`redis-cli`命令连接到其中一个Redis服务; ```bash # 单机模式启动 redis-cli -h 127.0.0.1 -p 6391 # 集群模式启动 redis-cli -c -h 127.0.0.1 -p 6391 ``` - 之后通过`cluster nodes`命令可以查看节点信息,发现符合原来3主3从的预期。 ![](../images/redis_cluster_05.png) ## SpringBoot中使用Redis集群 > 我们在[《Spring Data Redis 最佳实践!》](https://mp.weixin.qq.com/s/9j3exBtZ9FWWlyZkxlWaOA)中讲到了在SpringBoot中如何使用Redis,用的是单节点的Redis服务,这次我们讲下如何使用Redis集群服务。 - 我们在原来代码的基础上进行改造,修改application.yml配置文件,添加Redis集群配置; ```yaml spring: redis: # host: 192.168.6.139 # Redis服务器地址 # database: 0 # Redis数据库索引(默认为0) # port: 6379 # Redis服务器连接端口 password: # Redis服务器连接密码(默认为空) timeout: 3000ms # 连接超时时间 lettuce: pool: max-active: 8 # 连接池最大连接数 max-idle: 8 # 连接池最大空闲连接数 min-idle: 0 # 连接池最小空闲连接数 max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制 cluster: nodes: - 192.168.6.139:6391 - 192.168.6.139:6392 - 192.168.6.139:6393 - 192.168.6.139:6394 - 192.168.6.139:6395 - 192.168.6.139:6396 ``` - 此时我们再次调用获取品牌详情的接口,就会把品牌信息缓存到Redis集群中去了; - 由于Redis容器`redis-master1`和`redis-slave2`互为主从,所以里面都缓存了相同的品牌详情信息。 ![](../images/redis_cluster_06.png) ## 配置文件地址 [https://github.com/macrozheng/mall-learning/tree/master/document/redis-cluster](https://github.com/macrozheng/mall-learning/tree/master/document/redis-cluster) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-redis](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-redis) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/redis_desktop_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Github标星 8K+,免费又好用的Redis客户端工具! > 最近在寻找一款免费又好用的Redis客户端工具,于是找到了`AnotherRedisDesktopManager`,界面漂亮而且支持Redis集群,推荐给大家! ## RedisDesktopManager 以前一直使用的是RedisDesktopManager这款Redis客户端工具,由于很久没更新界面有点古老,最近想更新升级下,进到官网一看,发现收费了...... ![](../images/redis_desktop_start_01.png) ## AnotherRedisDesktopManager 于是就去Github上找了下,发现了`另一个`RedisDesktopManager,界面漂亮而且免费,一看Star数量8K+,有点厉害!就决定用它了。 ![](../images/redis_desktop_start_02.png) ## 使用 ### 安装 - 首先我们需要下载安装包,然后双击安装即可,下载地址:https://github.com/qishibo/AnotherRedisDesktopManager/releases ![](../images/redis_desktop_start_03.png) - 安装完成后,点击`新建连接`可以连接到Redis,可以发现`Cluster`这个选项,之前使用的旧版RedisDesktopManager并不支持Redis集群,这个工具支持了很不错! ![](../images/redis_desktop_start_04.png) ### 深色模式 我们现在使用的界面模式为浅色模式,可以从设置中打开深色模式,还是很炫酷的! ![](../images/redis_desktop_start_05.png) ### 命令行 支持使用Redis命令行,点击`Redis控制台`按钮即可打开。 ![](../images/redis_desktop_start_06.png) ### Redis数据操作 - 使用`新增Key`功能可以往Redis中存储键值对数据,目前支持5种数据结构; ![](../images/redis_desktop_start_07.png) - 我们先来存储`String`类型的键值对数据,可以发现支持文本、JSON、反序列化三种显示,而且JSON支持效果不错; ![](../images/redis_desktop_start_08.png) - 再来存储`List`类型的键值对,发现可以像操作表格一样操作List中的数据; ![](../images/redis_desktop_start_09.png) - 再来存储`Hash`类型的键值对,依然可以像操作表格一样操作HashMap中的数据。 ![](../images/redis_desktop_start_10.png) ### 集群模式 - 既然该客户端支持了集群模式,那我们也来试试吧,首先需要搭建一个Redis集群,搭建方式可以参考[《Docker环境下秒建Redis集群,连SpringBoot也整上了!》](https://mp.weixin.qq.com/s/Vg8WCsyA1arLUoKENoNJQw); - 创建好Redis集群之后,连接任意一个Redis服务即可访问集群,注意我们的Redis服务运行端口为`6391~6396`,我们先连接到`6391`的服务; ![](../images/redis_desktop_start_11.png) - 往Redis集群中存储一个键值对数据后,连接另一个Redis服务`6392`,发现依然可以查看到该数据; ![](../images/redis_desktop_start_12.png) - 删除该数据后,两个连接都已经看不见该数据了,证明可以正常操作Redis集群; ![](../images/redis_desktop_start_13.png) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/spring_data_redis.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Spring Data Redis 最佳实践! > Spring Data Redis 是Spring 框架提供的用于操作Redis的方式,最近整理了下它的用法,解决了使用过程中遇到的一些难点与坑点,希望对大家有所帮助。本文涵盖了Redis的安装、Spring Cache结合Redis的使用、Redis连接池的使用和RedisTemplate的使用等内容。 ## Redis安装 > 这里提供Linux和Windows两种安装方式,由于Windows下的版本最高只有3.2版本,所以推荐使用Linux下的版本,目前最新稳定版本为5.0,也是本文中使用的版本。 ### Linux > 这里我们使用Docker环境下的安装方式。 - 下载Redis5.0的Docker镜像; ```bash docker pull redis:5.0 ``` - 使用Docker命令启动Redis容器; ```bash docker run -p 6379:6379 --name redis \ -v /mydata/redis/data:/data \ -d redis:5.0 redis-server --appendonly yes ``` ### Windows > 想使用Windows版本的朋友可以使用以下安装方式。 - 下载Windows版本的Redis,下载地址:https://github.com/MicrosoftArchive/redis/releases ![](../images/spring_boot_redis_01.png) - 下载完后解压到指定目录; ![](../images/spring_boot_redis_02.png) - 在当前地址栏输入cmd后,执行redis的启动命令:redis-server.exe redis.windows.conf ![](../images/spring_boot_redis_03.png) - 将Redis注册为服务的命令: ```bash # 注册服务 redis-server --service-install redis.windows.conf # 启动服务 redis-server --service-start # 停止服务 redis-server --service-stop # 删除服务 redis-server --service-uninstall ``` ## Spring Cache 操作Redis ### Spring Cache 简介 > 当Spring Boot 结合Redis来作为缓存使用时,最简单的方式就是使用Spring Cache了,使用它我们无需知道Spring中对Redis的各种操作,仅仅通过它提供的@Cacheable 、@CachePut 、@CacheEvict 、@EnableCaching等注解就可以实现缓存功能。 ### 常用注解 #### @EnableCaching 开启缓存功能,一般放在启动类上。 #### @Cacheable 使用该注解的方法当缓存存在时,会从缓存中获取数据而不执行方法,当缓存不存在时,会执行方法并把返回结果存入缓存中。`一般使用在查询方法上`,可以设置如下属性: - value:缓存名称(必填),指定缓存的命名空间; - key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义; - unless:条件符合则不缓存; - condition:条件符合则缓存。 #### @CachePut 使用该注解的方法每次执行时都会把返回结果存入缓存中。`一般使用在新增方法上`,可以设置如下属性: - value:缓存名称(必填),指定缓存的命名空间; - key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义; - unless:条件符合则不缓存; - condition:条件符合则缓存。 #### @CacheEvict 使用该注解的方法执行时会清空指定的缓存。`一般使用在更新或删除方法上`,可以设置如下属性: - value:缓存名称(必填),指定缓存的命名空间; - key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义; - condition:条件符合则缓存。 ### 使用步骤 - 在pom.xml中添加项目依赖: ```xml org.springframework.boot spring-boot-starter-data-redis ``` - 修改配置文件application.yml,添加Redis的连接配置; ```yaml spring: redis: host: 192.168.6.139 # Redis服务器地址 database: 0 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 password: # Redis服务器连接密码(默认为空) timeout: 1000ms # 连接超时时间 ``` - 在启动类上添加@EnableCaching注解启动缓存功能; ```java @EnableCaching @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ``` - 接下来在PmsBrandServiceImpl类中使用相关注解来实现缓存功能,可以发现我们获取品牌详情的方法中使用了@Cacheable注解,在修改和删除品牌的方法上使用了@CacheEvict注解; ```java /** * PmsBrandService实现类 * Created by macro on 2019/4/19. */ @Service public class PmsBrandServiceImpl implements PmsBrandService { @Autowired private PmsBrandMapper brandMapper; @CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id") @Override public int update(Long id, PmsBrand brand) { brand.setId(id); return brandMapper.updateByPrimaryKeySelective(brand); } @CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id") @Override public int delete(Long id) { return brandMapper.deleteByPrimaryKey(id); } @Cacheable(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id", unless = "#result==null") @Override public PmsBrand getItem(Long id) { return brandMapper.selectByPrimaryKey(id); } } ``` - 我们可以调用获取品牌详情的接口测试下效果,此时发现Redis中存储的数据有点像乱码,并且没有设置过期时间; ![](../images/spring_boot_redis_04.png) ## 存储JSON格式数据 > 此时我们就会想到有没有什么办法让Redis中存储的数据变成标准的JSON格式,然后可以设置一定的过期时间,不设置过期时间容易产生很多不必要的缓存数据。 - 我们可以通过给RedisTemplate设置JSON格式的序列化器,并通过配置RedisCacheConfiguration设置超时时间来实现以上需求,此时别忘了去除启动类上的@EnableCaching注解,具体配置类RedisConfig代码如下; ```java /** * Redis配置类 * Created by macro on 2020/3/2. */ @EnableCaching @Configuration public class RedisConfig extends CachingConfigurerSupport { /** * redis数据库自定义key */ public static final String REDIS_KEY_DATABASE="mall"; @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisSerializer serializer = redisSerializer(); RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(serializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(serializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public RedisSerializer redisSerializer() { //创建JSON序列化器 Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(objectMapper); return serializer; } @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); //设置Redis缓存有效期为1天 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1)); return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); } } ``` - 此时我们再次调用获取商品详情的接口进行测试,会发现Redis中已经缓存了标准的JSON格式数据,并且超时时间被设置为了1天。 ![](../images/spring_boot_redis_05.png) ## 使用Redis连接池 > SpringBoot 1.5.x版本Redis客户端默认是Jedis实现的,SpringBoot 2.x版本中默认客户端是用Lettuce实现的,我们先来了解下Jedis和Lettuce客户端。 ### Jedis vs Lettuce Jedis在实现上是直连Redis服务,多线程环境下非线程安全,除非使用连接池,为每个 RedisConnection 实例增加物理连接。 Lettuce是一种可伸缩,线程安全,完全非阻塞的Redis客户端,多个线程可以共享一个RedisConnection,它利用Netty NIO框架来高效地管理多个连接,从而提供了异步和同步数据访问方式,用于构建非阻塞的反应性应用程序。 ### 使用步骤 - 修改application.yml添加Lettuce连接池配置,用于配置线程数量和阻塞等待时间; ```yaml spring: redis: lettuce: pool: max-active: 8 # 连接池最大连接数 max-idle: 8 # 连接池最大空闲连接数 min-idle: 0 # 连接池最小空闲连接数 max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制 ``` - 由于SpringBoot 2.x中默认并没有使用Redis连接池,所以需要在pom.xml中添加commons-pool2的依赖; ```xml org.apache.commons commons-pool2 ``` - 如果你没添加以上依赖的话,启动应用的时候就会产生如下错误; ```bash Caused by: java.lang.NoClassDefFoundError: org/apache/commons/pool2/impl/GenericObjectPoolConfig at org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration$LettucePoolingClientConfigurationBuilder.(LettucePoolingClientConfiguration.java:84) ~[spring-data-redis-2.1.5.RELEASE.jar:2.1.5.RELEASE] at org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration.builder(LettucePoolingClientConfiguration.java:48) ~[spring-data-redis-2.1.5.RELEASE.jar:2.1.5.RELEASE] at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration$PoolBuilderFactory.createBuilder(LettuceConnectionConfiguration.java:149) ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE] at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration.createBuilder(LettuceConnectionConfiguration.java:107) ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE] at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration.getLettuceClientConfiguration(LettuceConnectionConfiguration.java:93) ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE] at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration.redisConnectionFactory(LettuceConnectionConfiguration.java:74) ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE] at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration$$EnhancerBySpringCGLIB$$5caa7e47.CGLIB$redisConnectionFactory$0() ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE] at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration$$EnhancerBySpringCGLIB$$5caa7e47$$FastClassBySpringCGLIB$$b8ae2813.invoke() ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE] at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.1.5.RELEASE.jar:5.1.5.RELEASE] at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE] at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration$$EnhancerBySpringCGLIB$$5caa7e47.redisConnectionFactory() ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_91] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_91] at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_91] at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE] ... 111 common frames omitted ``` ## 自由操作Redis > Spring Cache 给我们提供了操作Redis缓存的便捷方法,但是也有很多局限性。比如说我们想单独设置一个缓存值的有效期怎么办?我们并不想缓存方法的返回值,我们想缓存方法中产生的中间值怎么办?此时我们就需要用到RedisTemplate这个类了,接下来我们来讲下如何通过RedisTemplate来自由操作Redis中的缓存。 ### RedisService > 定义Redis操作业务类,在Redis中有几种数据结构,比如普通结构(对象),Hash结构、Set结构、List结构,该接口中定义了大多数常用操作方法。 ```java /** * redis操作Service * Created by macro on 2020/3/3. */ public interface RedisService { /** * 保存属性 */ void set(String key, Object value, long time); /** * 保存属性 */ void set(String key, Object value); /** * 获取属性 */ Object get(String key); /** * 删除属性 */ Boolean del(String key); /** * 批量删除属性 */ Long del(List keys); /** * 设置过期时间 */ Boolean expire(String key, long time); /** * 获取过期时间 */ Long getExpire(String key); /** * 判断是否有该属性 */ Boolean hasKey(String key); /** * 按delta递增 */ Long incr(String key, long delta); /** * 按delta递减 */ Long decr(String key, long delta); /** * 获取Hash结构中的属性 */ Object hGet(String key, String hashKey); /** * 向Hash结构中放入一个属性 */ Boolean hSet(String key, String hashKey, Object value, long time); /** * 向Hash结构中放入一个属性 */ void hSet(String key, String hashKey, Object value); /** * 直接获取整个Hash结构 */ Map hGetAll(String key); /** * 直接设置整个Hash结构 */ Boolean hSetAll(String key, Map map, long time); /** * 直接设置整个Hash结构 */ void hSetAll(String key, Map map); /** * 删除Hash结构中的属性 */ void hDel(String key, Object... hashKey); /** * 判断Hash结构中是否有该属性 */ Boolean hHasKey(String key, String hashKey); /** * Hash结构中属性递增 */ Long hIncr(String key, String hashKey, Long delta); /** * Hash结构中属性递减 */ Long hDecr(String key, String hashKey, Long delta); /** * 获取Set结构 */ Set sMembers(String key); /** * 向Set结构中添加属性 */ Long sAdd(String key, Object... values); /** * 向Set结构中添加属性 */ Long sAdd(String key, long time, Object... values); /** * 是否为Set中的属性 */ Boolean sIsMember(String key, Object value); /** * 获取Set结构的长度 */ Long sSize(String key); /** * 删除Set结构中的属性 */ Long sRemove(String key, Object... values); /** * 获取List结构中的属性 */ List lRange(String key, long start, long end); /** * 获取List结构的长度 */ Long lSize(String key); /** * 根据索引获取List中的属性 */ Object lIndex(String key, long index); /** * 向List结构中添加属性 */ Long lPush(String key, Object value); /** * 向List结构中添加属性 */ Long lPush(String key, Object value, long time); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Object... values); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Long time, Object... values); /** * 从List结构中移除属性 */ Long lRemove(String key, long count, Object value); } ``` ### RedisServiceImpl > RedisService的实现类,使用RedisTemplate来自由操作Redis中的缓存数据。 ```java /** * redis操作实现类 * Created by macro on 2020/3/3. */ @Service public class RedisServiceImpl implements RedisService { @Autowired private RedisTemplate redisTemplate; @Override public void set(String key, Object value, long time) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } @Override public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); } @Override public Object get(String key) { return redisTemplate.opsForValue().get(key); } @Override public Boolean del(String key) { return redisTemplate.delete(key); } @Override public Long del(List keys) { return redisTemplate.delete(keys); } @Override public Boolean expire(String key, long time) { return redisTemplate.expire(key, time, TimeUnit.SECONDS); } @Override public Long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } @Override public Boolean hasKey(String key) { return redisTemplate.hasKey(key); } @Override public Long incr(String key, long delta) { return redisTemplate.opsForValue().increment(key, delta); } @Override public Long decr(String key, long delta) { return redisTemplate.opsForValue().increment(key, -delta); } @Override public Object hGet(String key, String hashKey) { return redisTemplate.opsForHash().get(key, hashKey); } @Override public Boolean hSet(String key, String hashKey, Object value, long time) { redisTemplate.opsForHash().put(key, hashKey, value); return expire(key, time); } @Override public void hSet(String key, String hashKey, Object value) { redisTemplate.opsForHash().put(key, hashKey, value); } @Override public Map hGetAll(String key) { return redisTemplate.opsForHash().entries(key); } @Override public Boolean hSetAll(String key, Map map, long time) { redisTemplate.opsForHash().putAll(key, map); return expire(key, time); } @Override public void hSetAll(String key, Map map) { redisTemplate.opsForHash().putAll(key, map); } @Override public void hDel(String key, Object... hashKey) { redisTemplate.opsForHash().delete(key, hashKey); } @Override public Boolean hHasKey(String key, String hashKey) { return redisTemplate.opsForHash().hasKey(key, hashKey); } @Override public Long hIncr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, delta); } @Override public Long hDecr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, -delta); } @Override public Set sMembers(String key) { return redisTemplate.opsForSet().members(key); } @Override public Long sAdd(String key, Object... values) { return redisTemplate.opsForSet().add(key, values); } @Override public Long sAdd(String key, long time, Object... values) { Long count = redisTemplate.opsForSet().add(key, values); expire(key, time); return count; } @Override public Boolean sIsMember(String key, Object value) { return redisTemplate.opsForSet().isMember(key, value); } @Override public Long sSize(String key) { return redisTemplate.opsForSet().size(key); } @Override public Long sRemove(String key, Object... values) { return redisTemplate.opsForSet().remove(key, values); } @Override public List lRange(String key, long start, long end) { return redisTemplate.opsForList().range(key, start, end); } @Override public Long lSize(String key) { return redisTemplate.opsForList().size(key); } @Override public Object lIndex(String key, long index) { return redisTemplate.opsForList().index(key, index); } @Override public Long lPush(String key, Object value) { return redisTemplate.opsForList().rightPush(key, value); } @Override public Long lPush(String key, Object value, long time) { Long index = redisTemplate.opsForList().rightPush(key, value); expire(key, time); return index; } @Override public Long lPushAll(String key, Object... values) { return redisTemplate.opsForList().rightPushAll(key, values); } @Override public Long lPushAll(String key, Long time, Object... values) { Long count = redisTemplate.opsForList().rightPushAll(key, values); expire(key, time); return count; } @Override public Long lRemove(String key, long count, Object value) { return redisTemplate.opsForList().remove(key, count, value); } } ``` ### RedisController > 测试RedisService中缓存操作的Controller,大家可以调用测试下。 ```java /** * Redis测试Controller * Created by macro on 2020/3/3. */ @Api(tags = "RedisController", description = "Redis测试") @Controller @RequestMapping("/redis") public class RedisController { @Autowired private RedisService redisService; @Autowired private PmsBrandService brandService; @ApiOperation("测试简单缓存") @RequestMapping(value = "/simpleTest", method = RequestMethod.GET) @ResponseBody public CommonResult simpleTest() { List brandList = brandService.list(1, 5); PmsBrand brand = brandList.get(0); String key = "redis:simple:" + brand.getId(); redisService.set(key, brand); PmsBrand cacheBrand = (PmsBrand) redisService.get(key); return CommonResult.success(cacheBrand); } @ApiOperation("测试Hash结构的缓存") @RequestMapping(value = "/hashTest", method = RequestMethod.GET) @ResponseBody public CommonResult hashTest() { List brandList = brandService.list(1, 5); PmsBrand brand = brandList.get(0); String key = "redis:hash:" + brand.getId(); Map value = BeanUtil.beanToMap(brand); redisService.hSetAll(key, value); Map cacheValue = redisService.hGetAll(key); PmsBrand cacheBrand = BeanUtil.mapToBean(cacheValue, PmsBrand.class, true); return CommonResult.success(cacheBrand); } @ApiOperation("测试Set结构的缓存") @RequestMapping(value = "/setTest", method = RequestMethod.GET) @ResponseBody public CommonResult> setTest() { List brandList = brandService.list(1, 5); String key = "redis:set:all"; redisService.sAdd(key, (Object[]) ArrayUtil.toArray(brandList, PmsBrand.class)); redisService.sRemove(key, brandList.get(0)); Set cachedBrandList = redisService.sMembers(key); return CommonResult.success(cachedBrandList); } @ApiOperation("测试List结构的缓存") @RequestMapping(value = "/listTest", method = RequestMethod.GET) @ResponseBody public CommonResult> listTest() { List brandList = brandService.list(1, 5); String key = "redis:list:all"; redisService.lPushAll(key, (Object[]) ArrayUtil.toArray(brandList, PmsBrand.class)); redisService.lRemove(key, 1, brandList.get(0)); List cachedBrandList = redisService.lRange(key, 0, 3); return CommonResult.success(cachedBrandList); } } ``` ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-redis](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-redis) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/springboot_docker_plugin.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 还在使用第三方Docker插件?SpringBoot官方插件真香! > 为了方便为SpringBoot应用构建Docker镜像,我们经常会使用Maven插件来打包镜像。之前一直使用的是第三方插件,有`spotify`和`fabric8`出品的两种`docker-maven-plugin`。最近SpringBoot 2.4.0发布了,官方插件也增加了对Docker的支持,体验了一把发现也很好用,推荐给大家! ## 第三方插件使用 > 我们先了解下第三方插件的使用,方便和官方插件做对比,`fabric8`插件使用具体可以参考[《还在手动部署SpringBoot应用?试试这个自动化插件!》](https://mp.weixin.qq.com/s/3X6vVdWmjmWCyiLm35jpVw)。 - 值得注意的是,在我们使用插件时,需要自己定义镜像构建过程,比如在`pom.xml`中使用如下配置,``标签下的配置为镜像构建过程的配置; ```xml io.fabric8 docker-maven-plugin 0.33.0 http://192.168.3.101:2375 http://192.168.3.101:5000 192.168.3.101:5000/mall-tiny/${project.name}:${project.version} java:8 ${project.build.finalName}.jar / artifact ["java", "-jar","/${project.build.finalName}.jar"] macrozheng ``` - 或者先在Dockerfile文件中定义好镜像构建过程; ```dockerfile # 该镜像需要依赖的基础镜像 FROM java:8 # 将当前maven目录生成的文件复制到docker容器的/目录下 COPY maven / # 声明服务运行在8080端口 EXPOSE 8080 # 指定docker容器启动时运行jar包 ENTRYPOINT ["java", "-jar","/mall-tiny-fabric-0.0.1-SNAPSHOT.jar"] # 指定维护者的名字 MAINTAINER macrozheng ``` - 然后在插件中引用Dockerfile文件,用于构建镜像; ```xml ${project.basedir} ``` - 其实对于SpringBoot应用来说,如何从应用Jar包构建Docker镜像,做法基本是差不多的,为什么非要自己定义镜像的构建过程呢? ## 官方插件使用 > SpringBoot官方插件解决了上面的问题,无需自己编写Docker镜像构建过程,直接自动构建,是不是很方便!接下来我们来体验下它的强大之处! - 由于我们需要把镜像推送到镜像仓库,首先我们安装好私有镜像仓库`Registry`和可视化镜像管理工具`docker-registry-ui`,具体可以参考[《还在手动部署SpringBoot应用?试试这个自动化插件!》](https://mp.weixin.qq.com/s/3X6vVdWmjmWCyiLm35jpVw); ```bash [root@linux-local ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9ffec08467ac joxit/docker-registry-ui:static "/bin/sh -c entrypoi…" 2 hours ago Up 2 hours 0.0.0.0:8280->80/tcp registry-ui a809535ee2a2 registry:2 "/entrypoint.sh /etc…" 3 months ago Up 7 hours 0.0.0.0:5000->5000/tcp registry2 ``` - 然后我们需要把应用的版本升级到SpringBoot 2.4.0,之前的版本Docker支持没有这个完善; ```xml org.springframework.boot spring-boot-starter-parent 2.4.0 ``` - 然后修改`pom.xml`文件,对官方Maven插件进行配置,主要是对Docker相关功能进行配置; ```xml org.springframework.boot spring-boot-maven-plugin 192.168.3.101:5000/mall-tiny/${project.name}:${project.version} true http://192.168.3.101:2375 false test test http://192.168.3.101:5000 ``` - 如果你使用的是IDEA的话,直接双击SpringBoot插件的`build-image`命令即可一键打包并推送到镜像仓库; ![](../images/sb_maven_plugin_docker_01.png) - 也可以在命令行使用如下Maven命令来打包构建镜像; ```bash mvn spring-boot:build-image ``` - 镜像构建过程中会输出如下信息,由于很多依赖会从Github上下载,网络不好的情况下会下载失败,多试几次就好: ```bash [INFO] > Pulling builder image 'docker.io/paketobuildpacks/builder:base' 100% [INFO] > Pulled builder image 'paketobuildpacks/builder@sha256:9d377230ba8ee74d8619178fd318b1b87a7da1a88bdb198afd14dd7de9e8ea6a' [INFO] > Pulling run image 'docker.io/paketobuildpacks/run:base-cnb' 100% [INFO] > Pulled run image 'paketobuildpacks/run@sha256:33d37fc9ba16e220f071805eaeed881a508ceee5c8909db5710aaed7e97e4fc2' [INFO] > Executing lifecycle version v0.9.3 [INFO] > Using build cache volume 'pack-cache-5641f846df6.build' [INFO] [INFO] > Running creator [INFO] [creator] ===> DETECTING [INFO] [creator] 5 of 18 buildpacks participating [INFO] [creator] paketo-buildpacks/ca-certificates 1.0.1 [INFO] [creator] paketo-buildpacks/bellsoft-liberica 5.2.1 [INFO] [creator] paketo-buildpacks/executable-jar 3.1.3 [INFO] [creator] paketo-buildpacks/dist-zip 2.2.2 [INFO] [creator] paketo-buildpacks/spring-boot 3.5.0 [INFO] [creator] ===> ANALYZING [INFO] [creator] Restoring metadata for "paketo-buildpacks/ca-certificates:helper" from app image [INFO] [creator] Restoring metadata for "paketo-buildpacks/bellsoft-liberica:helper" from app image [INFO] [creator] Restoring metadata for "paketo-buildpacks/bellsoft-liberica:java-security-properties" from app image [INFO] [creator] Restoring metadata for "paketo-buildpacks/bellsoft-liberica:jre" from app image [INFO] [creator] Restoring metadata for "paketo-buildpacks/bellsoft-liberica:jvmkill" from app image [INFO] [creator] Restoring metadata for "paketo-buildpacks/executable-jar:class-path" from app image [INFO] [creator] Restoring metadata for "paketo-buildpacks/spring-boot:helper" from app image [INFO] [creator] Restoring metadata for "paketo-buildpacks/spring-boot:spring-cloud-bindings" from app image [INFO] [creator] Restoring metadata for "paketo-buildpacks/spring-boot:web-application-type" from app image [INFO] [creator] ===> RESTORING [INFO] [creator] ===> BUILDING [INFO] [creator] [INFO] [creator] Paketo CA Certificates Buildpack 1.0.1 [INFO] [creator] https://github.com/paketo-buildpacks/ca-certificates [INFO] [creator] Launch Helper: Reusing cached layer [INFO] [creator] [INFO] [creator] Paketo BellSoft Liberica Buildpack 5.2.1 [INFO] [creator] https://github.com/paketo-buildpacks/bellsoft-liberica [INFO] [creator] Build Configuration: [INFO] [creator] $BP_JVM_VERSION 8.* the Java version [INFO] [creator] Launch Configuration: [INFO] [creator] $BPL_JVM_HEAD_ROOM 0 the headroom in memory calculation [INFO] [creator] $BPL_JVM_LOADED_CLASS_COUNT 35% of classes the number of loaded classes in memory calculation [INFO] [creator] $BPL_JVM_THREAD_COUNT 250 the number of threads in memory calculation [INFO] [creator] $JAVA_TOOL_OPTIONS the JVM launch flags [INFO] [creator] BellSoft Liberica JRE 8.0.275: Reusing cached layer [INFO] [creator] Launch Helper: Reusing cached layer [INFO] [creator] JVMKill Agent 1.16.0: Reusing cached layer [INFO] [creator] Java Security Properties: Reusing cached layer [INFO] [creator] [INFO] [creator] Paketo Executable JAR Buildpack 3.1.3 [INFO] [creator] https://github.com/paketo-buildpacks/executable-jar [INFO] [creator] Process types: [INFO] [creator] executable-jar: java org.springframework.boot.loader.JarLauncher [INFO] [creator] task: java org.springframework.boot.loader.JarLauncher [INFO] [creator] web: java org.springframework.boot.loader.JarLauncher [INFO] [creator] [INFO] [creator] Paketo Spring Boot Buildpack 3.5.0 [INFO] [creator] https://github.com/paketo-buildpacks/spring-boot [INFO] [creator] Creating slices from layers index [INFO] [creator] dependencies [INFO] [creator] spring-boot-loader [INFO] [creator] snapshot-dependencies [INFO] [creator] application [INFO] [creator] Launch Helper: Reusing cached layer [INFO] [creator] Web Application Type: Contributing to layer [INFO] [creator] Servlet web application detected [INFO] [creator] Writing env.launch/BPL_JVM_THREAD_COUNT.default [INFO] [creator] Spring Cloud Bindings 1.7.0: Reusing cached layer [INFO] [creator] 4 application slices [INFO] [creator] Image labels: [INFO] [creator] org.opencontainers.image.title [INFO] [creator] org.opencontainers.image.version [INFO] [creator] org.springframework.boot.spring-configuration-metadata.json [INFO] [creator] org.springframework.boot.version [INFO] [creator] ===> EXPORTING [INFO] [creator] Reusing layer 'paketo-buildpacks/ca-certificates:helper' [INFO] [creator] Reusing layer 'paketo-buildpacks/bellsoft-liberica:helper' [INFO] [creator] Reusing layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties' [INFO] [creator] Reusing layer 'paketo-buildpacks/bellsoft-liberica:jre' [INFO] [creator] Reusing layer 'paketo-buildpacks/bellsoft-liberica:jvmkill' [INFO] [creator] Reusing layer 'paketo-buildpacks/executable-jar:class-path' [INFO] [creator] Reusing layer 'paketo-buildpacks/spring-boot:helper' [INFO] [creator] Reusing layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings' [INFO] [creator] Reusing layer 'paketo-buildpacks/spring-boot:web-application-type' [INFO] [creator] Reusing 4/5 app layer(s) [INFO] [creator] Adding 1/5 app layer(s) [INFO] [creator] Reusing layer 'launcher' [INFO] [creator] Reusing layer 'config' [INFO] [creator] Reusing layer 'process-types' [INFO] [creator] Adding label 'io.buildpacks.lifecycle.metadata' [INFO] [creator] Adding label 'io.buildpacks.build.metadata' [INFO] [creator] Adding label 'io.buildpacks.project.metadata' [INFO] [creator] Adding label 'org.opencontainers.image.title' [INFO] [creator] Adding label 'org.opencontainers.image.version' [INFO] [creator] Adding label 'org.springframework.boot.spring-configuration-metadata.json' [INFO] [creator] Adding label 'org.springframework.boot.version' [INFO] [creator] Setting default process type 'web' [INFO] [creator] *** Images (d5e1771dac7b): [INFO] [creator] 192.168.3.101:5000/mall-tiny/mall-tiny-docker-plugin:0.0.1-SNAPSHOT [INFO] [INFO] Successfully built image '192.168.3.101:5000/mall-tiny/mall-tiny-docker-plugin:0.0.1-SNAPSHOT' [INFO] [INFO] > Pushed image '192.168.3.101:5000/mall-tiny/mall-tiny-docker-plugin:0.0.1-SNAPSHOT' [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 01:06 min [INFO] Finished at: 2020-11-27T15:07:46+08:00 [INFO] Final Memory: 37M/359M [INFO] ------------------------------------------------------------------------ ``` - 镜像构建成功后,可以从镜像仓库查看到我们的镜像: ```bash [root@linux-local ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE paketobuildpacks/run base-cnb a717358311fc 9 days ago 87.2MB java 8 d23bdf5b1b1b 3 years ago 643MB 192.168.3.101:5000/mall-tiny/mall-tiny-docker-plugin 0.0.1-SNAPSHOT d5e1771dac7b 40 years ago 244MB pack.local/builder/fewqajyqsc latest f15fad05a5ba 40 years ago 558MB pack.local/builder/kirivtcqtu latest f15fad05a5ba 40 years ago 558MB paketobuildpacks/builder base 511452064e06 40 years ago 558MB ``` - 我们可以从`Docker Registry UI`中查看镜像仓库中的镜像,访问地址:http://192.168.3.101:8280/ ![](../images/sb_maven_plugin_docker_02.png) - 接着使用如下命令启动我们的SpringBoot应用: ```bash docker run -p 8080:8080 --name mall-tiny-docker-plugin \ --link mysql:db \ -v /etc/localtime:/etc/localtime \ -v /mydata/app/mall-tiny-docker-plugin/logs:/var/logs \ -d 192.168.3.101:5000/mall-tiny/mall-tiny-docker-plugin:0.0.1-SNAPSHOT ``` - 启动成功后,可以成功访问到SpringBoot应用的Swagger页面,访问地址:http://192.168.3.101:8080/swagger-ui.html ![](../images/sb_maven_plugin_docker_03.png) ## 总结 SpringBoot官方Maven插件避免了编写Docker镜像构建过程,同时充分利用了SpringBoot 2.3以后的Jar分层技术,但对于需要自定义构建镜像的场景造成了一定的麻烦。 ## 参考资料 官方文档:https://docs.spring.io/spring-boot/docs/2.4.0/maven-plugin/reference/htmlsingle/#build-image ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-docker-plugin ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/springboot_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 肝了一周总结的SpringBoot实战教程,太实用了! > 天天在用SpringBoot,但有些SpringBoot的实用知识点却不是很清楚!最近又对SpringBoot中的实用知识点做了个总结,相信对从Spring过渡到SpringBoot的朋友会很有帮助! ## 前言 首先我们来了解下为什么要有SpringBoot? Spring作为J2EE的轻量级代替品,让我们无需开发重量级的Enterprise JavaBean(EJB),通过依赖注入和面向切面编程,使用简单的Java对象(POJO)即可实现EJB的功能。 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。即使后来Spring引入了基于注解的组件扫描和基于Java的配置,让它看上去简洁不少,但Spring还是需要不少配置。除此之外,项目的依赖管理也很麻烦,我们无法确保各个版本的依赖都能兼容。 为了简化Spring中的配置和统一各种依赖的版本,SpringBoot诞生了! ## 简介 SpringBoot从本质上来说就是Spring,它通过了一些自己的特性帮助我们简化了Spring应用程序的开发。主要有以下三个核心特性: - 自动配置:对于很多Spring应用程序常见的应用功能,SpringBoot能自动提供相关配置,集成功能开发者仅需很少的配置。 - 起步依赖:告诉SpringBoot需要什么功能,它就能引入对应的库,无需考虑该功能依赖库的版本问题。 - Actuator:可以深入了解SpringBoot应用程序内部情况,比如创建了哪些Bean、自动配置的决策、应用程序的状态信息等。 ## 开始使用 ### 创建应用 > 创建SpringBoot应用的方式有很多种,这里使用最流行的开发工具IDEA来创建应用。 - 首先通过`File->New Project`来创建一个项目; ![](../images/springboot_start_01.png) - 然后选择通过`Spring Initializr`来创建一个SpringBoot应用; ![](../images/springboot_start_02.png) - 填写好Maven项目的`groupId`和`artifactId`及选择好Java版本; ![](../images/springboot_start_03.png) - 选择好起步依赖,这里选择的是开启Web功能的起步依赖; ![](../images/springboot_start_04.png) - 选择好项目的存放位置即可顺利创建一个SpringBoot应用。 ![](../images/springboot_start_05.png) ### 查看应用 #### 项目结构 一个新创建的SpringBoot应用基本结构如下。 ``` mall-tiny-boot ├─pom.xml # Maven构建文件 └─src ├─main │ ├─java │ │ └─MallTinyApplication.java # 应用程序启动类 │ └─resources │ └─application.yml # SpringBoot配置文件 └─test └─java └─MallTinyApplicationTests.java # 基本的集成测试类 ``` #### 应用启动类 `MallTinyApplication`在SpringBoot应用中有配置和引导的作用,通过`@SpringBootApplication`注解开启组件扫描和自动配置,通过`SpringApplication.run()`引导应用程序启动; ```java //开启组件扫描和应用装配 @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { //负责引导应用程序启动 SpringApplication.run(MallTinyApplication.class, args); } } ``` `@SpringBootApplication`注解是三个注解的结合体,拥有以下三个注解的功能: - `@Configuration`:用于声明Spring中的Java配置; - `@ComponentScan`:启用组件扫描,当我们声明组件时,会自动发现并注册为Spring应用上下文中的Bean; - `@EnableAutoConfiguration`:开启SpringBoot自动配置功能,简化配置编写。 #### 测试应用 可以使用`@RunWith`和`@SpringBootTest`来创建Spring应用上下文,通过`@Test`注解来声明一个测试方法。 ```java @RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class MallTinyApplicationTests { @Autowired private PmsBrandService pmsBrandService; @Test public void contextLoads() { } @Test public void testMethod() { List brandList = pmsBrandService.listAllBrand(); log.info("testMethod:{}", brandList); } } ``` #### 编写应用配置 当我们需要微调自动配置的参数时,可以在`application.yml`文件中进行配置,比如微调下端口号。 ```yaml server: port: 8088 ``` #### 项目构建过程 SpringBoot项目可以使用Maven进行构建,首先我们需要继承`spring-boot-starter-parent`这个父依赖,父依赖可以控制所有SpringBoot官方起步依赖的版本,接下来当我们使用官方起步依赖时,就不用指定版本号了。我们还需要使用SpringBoot的插件,该插件主要用于将应用打包为可执行Jar。 ```xml 4.0.0 com.macro.mall mall-tiny-boot 1.0-SNAPSHOT mall-tiny-boot Demo project for Spring Boot UTF-8 UTF-8 1.8 true org.springframework.boot spring-boot-starter-parent 2.3.0.RELEASE org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin ``` ### 使用起步依赖 #### 使用起步依赖的好处 在使用起步依赖之前,我们先来了解下使用起步依赖的好处,当我们使用SpringBoot需要整合Web相关功能时,只需在`pom.xml`中添加一个起步依赖即可。 ```xml org.springframework.boot spring-boot-starter-web ``` 如果是Spring项目的话,我们需要添加很多依赖,还需要考虑各个依赖版本的兼容性问题,是个相当麻烦的事情。 ![](../images/springboot_start_06.png) #### 指定基于功能的依赖 当我们需要开发一个Web应用,需要使用MySQL数据库进行存储,使用Swagger生成API文档,添加如下起步依赖即可。需要注意的是只有官方的起步依赖不需要指定版本号,其他的还是需要自行指定的。 ```xml org.springframework.boot spring-boot-starter-web 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 mysql mysql-connector-java 8.0.15 io.springfox springfox-boot-starter 3.0.0 ``` #### 覆盖起步依赖中的库 其实起步依赖和你平时使用的依赖没什么区别,你可以使用Maven的方式来排除不想要的依赖。比如你不想使用tomcat容器,想使用undertow容器,可以在Web功能依赖中排除掉tomcat。 ```xml org.springframework.boot spring-boot-starter-web spring-boot-starter-tomcat org.springframework.boot org.springframework.boot spring-boot-starter-undertow ``` ### 使用自动配置 SpringBoot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。 举个例子,当我们使用Spring整合MyBatis的时候,需要完成如下几个步骤: - 根据数据库连接配置,配置一个dataSource对象; - 根据dataSource对象和SqlMapConfig.xml文件(其中包含mapper.xml文件路径和mapper接口路径配置),配置一个sqlSessionFactory对象。 当我们使用SpringBoot整合MyBatis的时候,会自动创建dataSource和sqlSessionFactory对象,只需我们在`application.yml`和Java配置中添加一些自定义配置即可。 在`application.yml`中配置好数据库连接信息及mapper.xml文件路径。 ```yaml 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 ``` 使用Java配置,配置好mapper接口路径。 ```java /** * MyBatis配置类 * Created by macro on 2019/4/8. */ @Configuration @MapperScan("com.macro.mall.tiny.mbg.mapper") public class MyBatisConfig { } ``` 使用自动配置以后,我们整合其他功能的配置大大减少了,可以更加专注程序功能的开发了。 ## 自定义配置 ### 自定义Bean覆盖自动配置 虽然自动配置很好用,但有时候自动配置的Bean并不能满足你的需要,我们可以自己定义相同的Bean来覆盖自动配置中的Bean。 例如当我们使用Spring Security来保护应用安全时,由于自动配置并不能满足我们的需求,我们需要自定义基于WebSecurityConfigurerAdapter的配置。这里我们自定义了很多配置,比如将基于Session的认证改为使用JWT令牌、配置了一些路径的无授权访问,自定义了登录接口路径,禁用了csrf功能等。 ```java /** * 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; @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Override protected void configure(HttpSecurity httpSecurity) throws Exception { List urls = ignoreUrlsConfig.getUrls(); String[] urlArray = ArrayUtil.toArray(urls, String.class); httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf .disable() .sessionManagement()// 基于token,所以不需要session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(HttpMethod.GET,urlArray) // 允许对于网站静态资源的无授权访问 .permitAll() .antMatchers("/admin/login")// 对登录注册要允许匿名访问 .permitAll() .antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求 .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 -> { AdminUserDetails admin = adminService.getAdminByUsername(username); if (admin != null) { return admin; } throw new UsernameNotFoundException("用户名或密码错误"); }; } @Bean public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() { return new JwtAuthenticationTokenFilter(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } } ``` ### 自动配置微调 有时候我们只需要微调下自动配置就能满足需求,并不需要覆盖自动配置的Bean,此时我们可以在`application.yml`属性文件中进行配置。 比如微调下应用运行的端口。 ```yaml server: port: 8088 ``` 比如修改下数据库连接信息。 ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root ``` ### 读取配置文件的自定义属性 有时候我们会在属性文件中自定义一些属性,然后在程序中使用。此时可以将这些自定义属性映射到一个属性类里来使用。 比如说我们想给Spring Security配置一个白名单,访问这些路径无需授权,我们可以先在`application.yml`中添添加如下配置。 ```yaml secure: ignored: urls: - / - /swagger-ui/ - /*.html - /favicon.ico - /**/*.html - /**/*.css - /**/*.js - /swagger-resources/** - /v2/api-docs/** ``` 之后创建一个属性类,使用`@ConfigurationProperties`注解配置好这些属性的前缀,再定义一个`urls`属性与属性文件相对应即可。 ```java /** * 用于配置白名单资源路径 * Created by macro on 2018/11/5. */ @Getter @Setter @Component @ConfigurationProperties(prefix = "secure.ignored") public class IgnoreUrlsConfig { private List urls = new ArrayList<>(); } ``` ## Actuator SpringBoot Actuator的关键特性是在应用程序里提供众多Web端点,通过它们了解应用程序运行时的内部状况。 ### 端点概览 Actuator提供了大概20个端点,常用端点路径及描述如下: | 路径 | 请求方式 | 描述 | | --------------- | -------- | ----------------------------------------------------------- | | /beans | GET | 描述应用程序上下文里全部的Bean,以及它们之间关系 | | /conditions | GET | 描述自动配置报告,记录哪些自动配置生效了,哪些没生效 | | /env | GET | 获取全部环境属性 | | /env/{name} | GET | 根据名称获取特定的环境属性 | | /mappings | GET | 描述全部的URI路径和控制器或过滤器的映射关系 | | /configprops | GET | 描述配置属性(包含默认值)如何注入Bean | | /metrics | GET | 获取应用程序度量指标,比如JVM和进程信息 | | /metrics/{name} | GET | 获取指定名称的应用程序度量值 | | loggers | GET | 查看应用程序中的日志级别 | | /threaddump | GET | 获取线程活动的快照 | | /health | GET | 报告应用程序的健康指标,这些值由HealthIndicator的实现类提供 | | /shutdown | POST | 关闭应用程序 | | /info | GET | 获取应用程序的定制信息,这些信息由info打头的属性提供 | ### 查看配置明细 - 直接访问根端点,可以获取到所有端点访问路径,根端点访问地址:http://localhost:8088/actuator ```json { "_links": { "self": { "href": "http://localhost:8088/actuator", "templated": false }, "beans": { "href": "http://localhost:8088/actuator/beans", "templated": false }, "caches-cache": { "href": "http://localhost:8088/actuator/caches/{cache}", "templated": true }, "caches": { "href": "http://localhost:8088/actuator/caches", "templated": false }, "health": { "href": "http://localhost:8088/actuator/health", "templated": false }, "health-path": { "href": "http://localhost:8088/actuator/health/{*path}", "templated": true }, "info": { "href": "http://localhost:8088/actuator/info", "templated": false }, "conditions": { "href": "http://localhost:8088/actuator/conditions", "templated": false }, "configprops": { "href": "http://localhost:8088/actuator/configprops", "templated": false }, "env": { "href": "http://localhost:8088/actuator/env", "templated": false }, "env-toMatch": { "href": "http://localhost:8088/actuator/env/{toMatch}", "templated": true }, "loggers": { "href": "http://localhost:8088/actuator/loggers", "templated": false }, "loggers-name": { "href": "http://localhost:8088/actuator/loggers/{name}", "templated": true }, "heapdump": { "href": "http://localhost:8088/actuator/heapdump", "templated": false }, "threaddump": { "href": "http://localhost:8088/actuator/threaddump", "templated": false }, "metrics-requiredMetricName": { "href": "http://localhost:8088/actuator/metrics/{requiredMetricName}", "templated": true }, "metrics": { "href": "http://localhost:8088/actuator/metrics", "templated": false }, "scheduledtasks": { "href": "http://localhost:8088/actuator/scheduledtasks", "templated": false }, "mappings": { "href": "http://localhost:8088/actuator/mappings", "templated": false } } } ``` - 通过`/beans`端点,可以获取到Spring应用上下文中的Bean的信息,比如Bean的类型和依赖属性等,访问地址:http://localhost:8088/actuator/beans ```json { "contexts": { "application": { "beans": { "sqlSessionFactory": { "aliases": [], "scope": "singleton", "type": "org.apache.ibatis.session.defaults.DefaultSqlSessionFactory", "resource": "class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]", "dependencies": [ "dataSource" ] }, "jdbcTemplate": { "aliases": [], "scope": "singleton", "type": "org.springframework.jdbc.core.JdbcTemplate", "resource": "class path resource [org/springframework/boot/autoconfigure/jdbc/JdbcTemplateConfiguration.class]", "dependencies": [ "dataSource", "spring.jdbc-org.springframework.boot.autoconfigure.jdbc.JdbcProperties" ] } } } } } ``` - 通过`/conditions`端点,可以获取到当前应用的自动配置报告,`positiveMatches`表示生效的自动配置,`negativeMatches`表示没有生效的自动配置。 ```json { "contexts": { "application": { "positiveMatches": { "DruidDataSourceAutoConfigure": [{ "condition": "OnClassCondition", "message": "@ConditionalOnClass found required class 'com.alibaba.druid.pool.DruidDataSource'" }] }, "negativeMatches": { "RabbitAutoConfiguration": { "notMatched": [{ "condition": "OnClassCondition", "message": "@ConditionalOnClass did not find required class 'com.rabbitmq.client.Channel'" }], "matched": [] } } } } } ``` - 通过`/env`端点,可以获取全部配置属性,包括环境变量、JVM属性、命令行参数和`application.yml`中的属性。 ```json { "activeProfiles": [], "propertySources": [{ "name": "systemProperties", "properties": { "java.runtime.name": { "value": "Java(TM) SE Runtime Environment" }, "java.vm.name": { "value": "Java HotSpot(TM) 64-Bit Server VM" }, "java.runtime.version": { "value": "1.8.0_91-b14" } } }, { "name": "applicationConfig: [classpath:/application.yml]", "properties": { "server.port": { "value": 8088, "origin": "class path resource [application.yml]:2:9" }, "spring.datasource.url": { "value": "jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai", "origin": "class path resource [application.yml]:6:10" }, "spring.datasource.username": { "value": "root", "origin": "class path resource [application.yml]:7:15" }, "spring.datasource.password": { "value": "******", "origin": "class path resource [application.yml]:8:15" } } } ] } ``` - 通过`/mappings`端点,可以查看全部的URI路径和控制器或过滤器的映射关系,这里可以看到我们自己定义的`PmsBrandController`和`JwtAuthenticationTokenFilter`的映射关系。 ```json { "contexts": { "application": { "mappings": { "dispatcherServlets": { "dispatcherServlet": [{ "handler": "com.macro.mall.tiny.controller.PmsBrandController#createBrand(PmsBrand)", "predicate": "{POST /brand/create}", "details": { "handlerMethod": { "className": "com.macro.mall.tiny.controller.PmsBrandController", "name": "createBrand", "descriptor": "(Lcom/macro/mall/tiny/mbg/model/PmsBrand;)Lcom/macro/mall/tiny/common/api/CommonResult;" }, "requestMappingConditions": { "consumes": [], "headers": [], "methods": [ "POST" ], "params": [], "patterns": [ "/brand/create" ], "produces": [] } } }] } }, "servletFilters": [{ "servletNameMappings": [], "urlPatternMappings": [ "/*", "/*", "/*", "/*", "/*" ], "name": "jwtAuthenticationTokenFilter", "className": "com.macro.mall.tiny.component.JwtAuthenticationTokenFilter" }] } } } ``` ### 查看运行时度量 - 通过`/metrics`端点,可以获取应用程序度量指标,不过只能获取度量的名称; ```json { "names": [ "http.server.requests", "jvm.buffer.count", "jvm.buffer.memory.used", "jvm.buffer.total.capacity", "jvm.classes.loaded", "jvm.classes.unloaded", "jvm.gc.live.data.size", "jvm.gc.max.data.size", "jvm.gc.memory.allocated", "jvm.gc.memory.promoted", "jvm.gc.pause", "jvm.memory.committed", "jvm.memory.max", "jvm.memory.used", "jvm.threads.daemon", "jvm.threads.live", "jvm.threads.peak", "jvm.threads.states", "logback.events", "process.cpu.usage", "process.start.time", "process.uptime", "system.cpu.count", "system.cpu.usage" ] } ``` - 需要添加指标名称才能获取对应的值,比如获取当前JVM使用的内存信息,访问地址:http://localhost:8088/actuator/metrics/jvm.memory.used ```json { "name": "jvm.memory.used", "description": "The amount of used memory", "baseUnit": "bytes", "measurements": [ { "statistic": "VALUE", "value": 3.45983088E8 } ], "availableTags": [ { "tag": "area", "values": [ "heap", "nonheap" ] }, { "tag": "id", "values": [ "Compressed Class Space", "PS Survivor Space", "PS Old Gen", "Metaspace", "PS Eden Space", "Code Cache" ] } ] } ``` - 通过`loggers`端点,可以查看应用程序中的日志级别信息,可以看出我们把`ROOT`范围日志设置为了INFO,而`com.macro.mall.tiny`包范围的设置为了DEBUG。 ```json { "levels": [ "OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE" ], "loggers": { "ROOT": { "configuredLevel": "INFO", "effectiveLevel": "INFO" }, "com.macro.mall.tiny": { "configuredLevel": "DEBUG", "effectiveLevel": "DEBUG" } } } ``` - 通过`/health`端点,可以查看应用的健康指标。 ```json { "status": "UP" } ``` ### 关闭应用 通过POST请求`/shutdown`端点可以直接关闭应用,但是需要将`endpoints.shutdown.enabled`属性设置为true才可以使用。 ```json { "message": "Shutting down, bye..." } ``` ### 定制Actuator 有的时候,我们需要自定义一下Actuator的端点才能满足我们的需求。 - 比如说Actuator有些端点默认是关闭的,我们想要开启所有端点,可以这样设置; ```yaml management: endpoints: web: exposure: include: '*' ``` - 比如说我们想自定义Actuator端点的基础路径,比如改为`/monitor`,这样我们我们访问地址就变成了这个:http://localhost:8088/monitor ```yaml management: endpoints: web: base-path: /monitor ``` ## 常用起步依赖 起步依赖不仅能让构建应用的依赖配置更简单,还能根据提供给应用程序的功能将它们组织到一起,这里整理了一些常用的起步依赖。 ### 官方依赖 ```xml 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 org.springframework.boot spring-boot-configuration-processor true org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-data-elasticsearch org.springframework.boot spring-boot-starter-data-mongodb org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-quartz org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-mail ``` ### 第三方依赖 ```xml org.mybatis.spring.boot mybatis-spring-boot-starter ${mybatis-version.version} com.github.pagehelper pagehelper-spring-boot-starter ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} io.springfox springfox-boot-starter ${springfox-version} com.baomidou mybatis-plus-boot-starter ${mybatis-plus-version} com.github.xiaoymin knife4j-spring-boot-starter ${knife4j-version} ``` ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-boot ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/swagger_postman.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Swagger界面丑、功能弱怎么破?用Postman增强下就给力了! > 在使用Swagger的时候,你是否会有这种感觉:提交参数为JSON没法格式化,参数错了查找麻烦,返回结果没法折叠,太长了没法看!Swagger结合Postman使用后这一情况有很大改变,今天我们来讲下如何使用Postman增强Swagger的功能,希望对大家有所帮助! ## Postman使用 Postman是一款非常好用的接口调试工具,界面漂亮且功能强大,对Postman不了解的可以看下[《Postman:API接口调试利器》](https://mp.weixin.qq.com/s/MORhwiRmDd0c44mYn5ZrXA)。 ## Swagger结合Postman使用 > 下面介绍下如何将Swagger API导入到Postman中去,然后使用Postman来调试接口。 ### Swagger API导入Postman - 首先我们需要启动使用了Swagger的应用项目,这里以之前的`mall-tiny-swagger`项目为例子,找到`api-docs`路径,访问地址:http://localhost:8088/swagger-ui/ ![](../images/swagger_postman_01.png) - 什么是Swagger的`api-docs`访问路径?该路径会返回JSON格式数据,应用中Swagger渲染API文档页面的所有数据就是来源于此,Postman也是可以通过这些数据来渲染API文档页面,这里的`api-docs`地址为:http://localhost:8088/v2/api-docs ![](../images/swagger_postman_02.png) - 在postman中点击`import`按钮,选择`Link`,输入Swagger的`api-docs`路径即可将Swagger生成的接口导入到Postman中去了; ![](../images/swagger_postman_03.png) - 直接使用默认选项导入即可,无需修改; ![](../images/swagger_postman_04.png) - 导入成功后,Swagger中的接口就会出现在Postman中了,之后就只需要在Postman中改改参数就可以开始调试接口了,是不是很方便! ![](../images/swagger_postman_05.png) ### Postman设置环境变量 - 我们随意找个接口访问下,会发现访问出错了,那是因为`baseUrl`这个环境变量设置有问题,导致了接口无法访问; ![](../images/swagger_postman_06.png) - 我们只要在Postman中设置`baseUrl`这个环境变量,就可以正常访问了; ![](../images/swagger_postman_07.png) - 添加好环境变量之后,需要选择好环境才会被应用。 ![](../images/swagger_postman_08.png) ### 访问需要登录认证的接口 - 这时候再次访问接口会发现我们的接口需要登录认证才能正常访问; ![](../images/swagger_postman_09.png) - 我们可以先调用登录接口来获取Token; ![](../images/swagger_postman_10.png) - 给需要登录认证的接口添加`Bearer Token`类型的Token,我们可以发现需要配置一个叫`token`的环境变量; ![](../images/swagger_postman_11.png) - 在环境变量中添加`token`; ![](../images/swagger_postman_12.png) - 添加完环境变量后即可正常访问需要登录认证的接口了。 ![](../images/swagger_postman_13.png) ## 有何缺点 > 此种方法使用Postman来调试接口是非常强大的,但是对于文档展示能力有点偏弱。 - 比如说Swagger中的接口对于请求和返回参数都有非常完善的注释信息; ![](../images/swagger_postman_14.png) - 而在Postman中,这些文档信息得不到体现,Postman毕竟还只是一个接口调试工具。 ![](../images/swagger_postman_15.png) ## 总结 Swagger和Postman结合使用比较好,Swagger用来看接口文档信息,Postman用于调试,将Swagger中的接口导入到Postman中可以弥补Swagger在接口调试方面的不足。 ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-swagger ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/swagger_starter.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 还在手动整合Swagger?Swagger官方Starter是真的香! > 之前项目中整合Swagger都是直接通过依赖`springfox-swagger`、`springfox-swagger-ui`两个jar包来实现的,最近发现springfox 3.0.0版本已经有了自己的SpringBoot Starter,使用起来更契合SpringBoot项目,非常方便,推荐给大家! ## 使用官方Starter > 我们先使用官方Starter来整合Swagger看看是否够简单! - 首先在`pom.xml`中添加springfox官方Swagger依赖; ```xml io.springfox springfox-boot-starter 3.0.0 ``` - 添加Swagger的Java配置,配置好Api信息和需要生成接口文档的类扫描路径即可; ```java /** * Swagger2API文档的配置 */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("SwaggerUI演示") .description("mall-tiny") .contact(new Contact("macro", null, null)) .version("1.0") .build(); } } ``` - 访问API文档信息,访问地址:http://localhost:8088/swagger-ui/ ![](../images/swagger_boot_starter_01.png) - 两步即可搞定SpringBoot集成Swagger,是不是很简单! ## 与之前版本相比 > 之前我们使用的是springfox 2.9.2版本,接下来对比下3.0.0的SpringBoot Starter使用,看看有何不同! - 旧版本需要依赖`springfox-swagger2`和`springfox-swagger-ui`两个配置,新版本一个Starter就搞定了,而且之前的版本如果不使用新版本的`swagger-models`和`swagger-annotations`依赖,访问接口会出现`NumberFormatException`问题; ````xml io.springfox springfox-swagger2 io.swagger swagger-annotations io.swagger swagger-models io.springfox springfox-swagger-ui io.swagger swagger-models 1.6.0 io.swagger swagger-annotations 1.6.0 ```` - 新版本去除了一些第三方依赖,包括`guava`,之前使用旧版本时就由于`guava`版本问题导致过依赖冲突,具体可以看下[《给Swagger升级了新版本,没想到居然有这么多坑!》](https://mp.weixin.qq.com/s/GWQRTEBRLGsL7um795ufbQ); - 新版本和旧版本文档访问路径发生了变化,新版本为:http://localhost:8088/swagger-ui/ ,旧版本为:http://localhost:8088/swagger-ui.html - 新版本中新增了一些SpringBoot配置,`springfox.documentation.enabled`配置可以控制是否启用Swagger文档生成功能; ![](../images/swagger_boot_starter_02.png) - 比如说我们只想在`dev`环境下启用Swagger文档,而在`prod`环境下不想启用,旧版本我们可以通过`@Profile`注解实现; ```java @Configuration @EnableSwagger2 @Profile(value = {"dev"}) public class Swagger2Config { } ``` - 新版本我们在SpringBoot配置文件中进行配置即可,`springfox.documentation.enabled`在`application-dev.yml`配置为true,在`application-prod.yml`中配置为false。 ## 整合Spring Security使用 > 我们经常会在项目中使用Spring Security实现登录认证,接下来我们来讲下如何使用Swagger整合Spring Security,实现访问需要登录认证的接口。 - 如何访问需要登录认证的接口?只需在访问接口时添加一个合法的`Authorization`请求头即可,下面是Swagger相关配置; ```java /** * Swagger2API文档的配置 */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .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(new Contact("macro", null, null)) .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; } } ``` - 我们需要在Spring Security中配置好Swagger静态资源的无授权访问,比如首页访问路径`/swagger-ui/`; ```java /** * 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, // 允许对于网站静态资源的无授权访问 "/", "/swagger-ui/", "/*.html", "/favicon.ico", "/**/*.html", "/**/*.css", "/**/*.js", "/swagger-resources/**", "/v2/api-docs/**" ) .permitAll() .antMatchers("/admin/login")// 对登录注册要允许匿名访问 .permitAll() .antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求 .permitAll() .anyRequest()// 除上面外的所有请求全部需要鉴权认证 .authenticated(); // 省略若干配置...... } } ``` - 调用登录接口获取token,账号密码为`admin:123456`; ![](../images/swagger_boot_starter_03.png) - 点击`Authorize`按钮后输入`Authorization`请求头,之后就可以访问需要登录认证的接口了。 ![](../images/swagger_boot_starter_04.png) ## 总结 Swagger官方Starter解决了之前整合Swagger的一系列问题,简化了SpringBoot整合Swagger的过程,使用起来更加方便了。同时对于一些复杂的配置使用基本没有变化,一些之前的使用方式依然可以使用! ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-swagger ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/yapi_start.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 当Swagger遇上YApi,瞬间高大上了! > Swagger经常被人吐槽界面不够好看、功能不够强大,其实有很多工具可以和Swagger结合使用,结合之后就会变得非常好用。之前写过一篇文章[《Swagger界面丑、功能弱怎么破?用Postman增强下就给力了!》](https://mp.weixin.qq.com/s/rbKUJAhv6WorFWgDNUDWTg),有朋友留言说YApi也很好用。最近体验了一把YApi,发现确实不错,推荐给大家! ## YApi简介 YApi是高效、易用、功能强大的API管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。YApi在Github上已累计获得了18K+Star,具有优秀的交互体验,YApi不仅提供了常用的接口管理功能,还提供了权限管理、Mock数据、Swagger数据导入等功能,总之功能很强大! ## 安装 ### 环境准备 > 本地部署YApi需要先安装nodejs和MongoDB,我们先把它们安装好。 - 安装nodejs,直接下载安装包双击安装即可,这里安装的是`12.14.0`版本,下载地址:https://nodejs.org/dist/v12.14.0/node-v12.14.0-x64.msi; - 安装MongoDB,参考[《MongoDB快速入门,掌握这些刚刚好!》](https://mp.weixin.qq.com/s/E8sDVWkxaLBdmZPj-6om2A)中的安装即可,这里安装的是`4.2.5`版本。 ### 安装yapi-cli > `yapi-cli`是YApi官方提供的安装工具,可以通过可视化界面来部署YApi服务,非常方便! - 使用npm命令来安装`yapi-cli`: ```bash npm install -g yapi-cli --registry https://registry.npm.taobao.org ``` - 安装成功后控制台输出如下内容; ![](../images/yapi_start_01.png) - 安装成功后使用`yapi server`命令来启动YApi的可视化部署界面。 ```bash yapi server ``` ### 安装YApi - 通过可视化部署界面安装YApi服务,访问地址:http://localhost:9090 ![](../images/yapi_start_02.png) - 安装完成后会输出如下信息,提示YApi的默认管理员账号密码及访问地址; ![](../images/yapi_start_03.png) - 进入YApi的安装目录,使用node命令启动YApi服务: ```bash node vendors/server/app.js ``` - 启动成功后控制台输出信息如下; ![](../images/yapi_start_04.png) - 通过页面访问Yapi,默认账号密码为`admin@admin.com:ymfe.org`,访问地址:http://localhost:3000 ![](../images/yapi_start_05.png) ## 使用 ### 从Swagger导入数据 - 使用管理员账号登录成功后,先创建一个`mall-tiny-group`分组; ![](../images/yapi_start_06.png) - 创建分组成功后,在该分组下点击`创建项目`,添加`mall-tiny-swagger`项目; ![](../images/yapi_start_07.png) - 之后启动我们之前的`mall-tiny-swagger`项目,启动成功后Swagger接口文档访问地址:http://localhost:8088/swagger-ui/ ![](../images/yapi_start_08.png) - 选择好YApi的`数据管理`功能,配置好Swagger的api-docs路径,然后进行数据导入; ![](../images/yapi_start_09.png) - 至此Swagger中的API接口已成功导入到YApi,点击`接口`标签查看所有导入接口。 ![](../images/yapi_start_10.png) ### 接口管理 - 打开`添加商品`的接口看看,可以看到非常完善的接口文档信息,注释都有了; ![](../images/yapi_start_11.png) - 来试试接口运行功能,我们会发现默认的接口请求地址并不符合我们的要求,需要在`环境配置`中设置; ![](../images/yapi_start_12.png) - 由于是跨域请求,Chrome浏览器需要安装跨域请求插件,下载地址:https://github.com/YMFE/cross-request/archive/master.zip ![](../images/yapi_start_13.png) - 由于我们的部分接口在请求头中添加token才能访问,所以我们先调用登录接口获取token; ![](../images/yapi_start_14.png) - 之后在`设置->环境配置`中添加Authorization头; ![](../images/yapi_start_15.png) - 再次调用需要登录的接口,可以正常获取到数据,返回数据虽然格式化了,但是没有折叠功能,数据太长的话就不太好看了; ![](../images/yapi_start_16.png) ### Mock功能 - 在我们调用POST接口提交JSON数据时,默认Mock的JSON数据有点不太符合我们的要求; ![](../images/yapi_start_17.png) - 可以通过接口信息中的`编辑->高级设置`进行修改; ![](../images/yapi_start_18.png) - 我们可以发现每一个接口信息中都有个Mock地址,当我们后台接口已经定义好格式,但是没有实现时,前端可以使用该地址来Mock数据进行调试; ![](../images/yapi_start_19.png) - 调用Mock地址可以获取到一些测试数据,数据取值返回可以通过上面的Mock设置自行修改; ![](../images/yapi_start_20.png) ### 从Swagger自动同步 - 当我们的接口修改了,API文档如何同步呢,我们可以通过`设置->Swagger自动同步`来开启自动同步功能,有三种数据同步模式可以选择; ![](../images/yapi_start_21.png) ### 权限管理 > 如果有新的成员加入进来,需要查看API文档怎么办? - 首先可以通过注册界面注册一个成员账号,此处账号为`test@qq.com:123456`; ![](../images/yapi_start_22.png) - 之后使用管理员账号登录,然后通过`成员列表->添加成员`,将用户添加到相应分组; ![](../images/yapi_start_23.png) - 最后使用成员账号登录即可访问相应API文档了。 ![](../images/yapi_start_24.png) ## 总结 YApi结合Swagger使用果然很强大!之前使用Postman结合Swagger使用时,文档查看、自动同步的问题都得到了解决,为了保证我们API文档访问的安全性还提供了权限管理功能。当API数据格式定义好后,Mock功能让前端无需后台实现也可以调试接口。不过对于JSON格式支持有点偏弱,要是能对JSON数据进行折叠显示就更好了! ## 参考资料 官方文档:https://hellosean1025.github.io/yapi/documents/index.html ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-swagger ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/reference/zentao.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 推荐一个项目管理工具,落地基于Scrum的敏捷开发! > 作为一个开发人员,我们也该懂一些项目管理的知识,今天我们来讲一个基于Scrum的项目管理工具`禅道`。本文将从禅道的安装部署开始讲起,然后讲讲Scrum的核心概念,最后通过禅道的一套操作来实践下Scrum的开发流程。 ## 禅道简介 禅道由青岛易软天创网络科技有限公司开发,国产开源项目管理软件。它集产品管理、项目管理、质量管理、文档管理、组织管理和事务管理于一体,是一款专业的研发项目管理软件,完整覆盖了研发项目管理的核心流程。禅道项目管理软件的主要管理思想基于国际流行的敏捷项目管理方法—Scrum。Scrum方法注重实效,操作性强,非常适合软件研发项目的快速迭代开发。禅道在遵循其管理方式基础上,结合国内研发现状,整合了Bug管理,测试用例管理,发布管理,文档管理等功能,完整的覆盖了软件研发项目的整个生命周期。 ## 安装及部署 > 禅道的安装方式有很多,这里我们使用它在Docker环境下的安装方式。 - 下载禅道的Docker镜像: ```bash docker pull idoop/zentao:latest ``` - 在Docker容器中运行禅道: ```bash docker run -d -p 80:80 -p 4306:3306 --name zentao-server \ -e ADMINER_USER="admin" -e ADMINER_PASSWD="123456" \ -e BIND_ADDRESS="false" \ -v /mydata/zbox/:/opt/zbox/ \ --add-host smtp.exmail.qq.com:163.177.90.125 \ -d idoop/zentao:latest ``` - 启动参数说明: - ADMINER_USER:管理员账号; - ADMINER_PASSWD:管理员密码; - BIND_ADDRESS:若设置参数为"false",禅道数据库启动后允许远程访问,选填; - SMTP_HOST:设置smtp服务IP和主机名,用于解决无法发送邮件的问题。 - 安装成功后,访问该地址即可登录禅道系统,登录用户名和密码为`admin:123456`:http://192.168.6.132/ ![](../images/zentao_use_01.png) ## Scrum的核心概念 ### 敏捷开发的产生 我们比较熟知的软件项目管理方法是瀑布,其基本流程是需求->设计->开发->测试。基本假设只要每个环节都做正确,那么终得到的结果也是正确的。但从总体来讲,瀑布项目失败率比较高。国外的软件先行者们针对瀑布开发中暴露出来的问题进行了一系列的探索、思考和总结,最终提出了敏捷开发的概念。敏捷开发有很多种方式,其中Scrum是比较流行的一种。 ### Scrum中的角色 Scrum是由产品经理(product owner)、项目经理(scrum master)和研发团队(dev team)组成的。 - 其中产品经理负责整理用户故事(user story),定义其商业价值,对其进行排序,制定发布计划,对产品负责; - 项目经理负责召开各种会议,协调项目,为研发团队服务; - 研发团队则由不同技能的成员组成,通过紧密协同,完成每一次迭代的目标,交付产品。 这里我们讲下什么是用户故事:所谓用户故事,就是来描述一件事情,作为什么用户,希望如何,这样做的目的或者价值何在,这样有用户角色,有行为,也有目的和价值所在,非常方便与团队成员进行沟通。 ### Scrum中的迭代开发 与瀑布不同,Scrum将产品的开发分解为若干个小迭代(sprint),其周期从1周到4周不等,但不会超过4周。 参与的团队成员一般是5到9人,每期迭代要完成的用户故事是固定的,每次迭代会产生一定的交付。 ### Scrum的基本流程 ![](../images/zentao_use_02.png) Scrum的基本流程如上图所示: - 产品经理负责整理用户故事,形成左侧的产品订单(product backlog); - 发布计划会议:项目经理负责讲解用户故事,对其进行估算和排序,发布计划会议的产出就是制定出这一期迭代要完成的用户故事列表,即迭代订单(sprint backlog); - 迭代计划会议:项目团队对每一个用户故事进行任务分解,分解的标准是完成该用户故事的所有任务,最终每个任务都有明确的负责人,并完成工时的初估计; - 每日例会:每天项目经理召集站立会议,团队成员回答昨天做了什么,今天计划做什么,遇到了什么问题; - 演示会议:迭代结束之后,召开演示会议,相关人员都受邀参加,团队负责向大家展示本次迭代取得的成果。期间大家的反馈记录下来,由产品经理整理,形成新的用户故事; - 回顾会议:项目团队对本期迭代进行总结,发现不足,制定改进计划,下一次迭代继续改进,已达到持续改进的效果。 ## 禅道使用 > 接下来我们将按角色来讲讲如何使用禅道来实现基于Scrum的项目管理。 ### 管理员 > 禅道安装成功之后,管理员的第一件要做的事情就是设置部门结构,并添加用户账号。 - 通过`组织->用户->维护部门`可以为企业添加部门结构: ![](../images/zentao_use_03.png) - 通过`组织->用户->添加用户`可以为企业添加用户: ![](../images/zentao_use_04.png) - 注意添加用户是需要添加职位和权限分组的: ![](../images/zentao_use_05.png) - 这里我们添加了产品经理、项目经理、研发主管、测试主管四个账号以便下面使用。 ![](../images/zentao_use_06.png) ### 产品经理 > 产品经理对于公司来讲,至关重要。只有做出好的产品或者服务出来,才能赢得市场,谋求发展和生存。 下面我们用产品经理的账号登录,来演示下产品经理在敏捷开发中所要做的事情。 - 通过产品左上角的下拉菜单可以添加产品: ![](../images/zentao_use_07.png) - 添加产品时需要完善相关信息: ![](../images/zentao_use_08.png) - 添加完产品后产品经理可以通过`产品->需求->维护模块`来创建产品的模块: ![](../images/zentao_use_09.png) - 在相应模块中通过`产品->需求->提需求`可以创建需求: ![](../images/zentao_use_10.png) - 之后可以完善需求的信息并进行创建: ![](../images/zentao_use_11.png) - 创建完需求后还需要对需求进行评审操作,只有评审通过的需求才会由项目经理进行任务分解,从而转为为开发任务指派给开发团队: ![](../images/zentao_use_12.png) - 评审时选择评审结果为确认通过后该需求就会被激活了: ![](../images/zentao_use_13.png) - 当然产品经理也可以对当前的需求进行变更操作,但是变更完的需求需要开发团队确认后才能进行后续开发; ![](../images/zentao_use_14.png) - 产品经理还可以创建计划,规定需求的完成时间: ![](../images/zentao_use_15.png) - 完善计划信息时,主要是要完善计划的开始和截止时间: ![](../images/zentao_use_16.png) - 可以通过关联需求,指定此次产品计划需要完成的需求: ![](../images/zentao_use_17.png) ### 项目经理 > 项目经理主要负责管理开发团队,将产品经理的需求讲解给开发团队听,确定项目要完成的需求列表,对需求进行任务分解并指派给开发团队,以及各种会议的组织。下面我们用项目经理的账号登录,来演示下项目经理在敏捷开发中所要做的事情。 - 在禅道中项目其实对应的是敏捷开发里面的迭代的概念,项目经理首先需要创建一个项目: ![](../images/zentao_use_18.png) - 创建时需要完善项目信息,设定项目开发时间以及关联相关产品与计划: ![](../images/zentao_use_19.png) - 接下来项目经理要做的就是创建项目团队,可以通过`项目->团队->团队管理`来为项目团队添加成员: ![](../images/zentao_use_20.png) - 项目团队组建完毕之后,项目经理通过关联产品即可将项目和产品进行关联: ![](../images/zentao_use_21.png) - 然后通过关联需求即可确定当前项目要做的需求,可以选择关联需求或按计划关联需求: ![](../images/zentao_use_22.png) - 需求确定之后,项目中几个关键的因素都有了:周期确定、资源确定、需求确定。下面项目经理要做的事情就是为每一个需求做任务分解: ![](../images/zentao_use_23.png) - 任务分解时需要完善任务详情,明确任务的执行时间: ![](../images/zentao_use_24.png) - 这里把商品管理功能这个需求分解为了商品列表、添加商品和编辑商品三个任务并指派给了开发人员。 ![](../images/zentao_use_25.png) ### 开发团队 > 项目的任务分解完毕之后,开发团队成员需要领取自己的任务,开始每天的开发。除了日常的编码工作之外,还应当每天花点时间在禅道里面更新下任务的状态以及消耗情况。下面我们用开发人员的账号登录,来演示下开发人员在敏捷开发中所要做的事情。 - 首先开发人员需要找到自己需要完成的任务,从`项目->任务`中可以查看到指派给自己的任务: ![](../images/zentao_use_26.png) - 开发人员开始做任务时点击开始按钮,完成任务时点击完成按钮: ![](../images/zentao_use_27.png) - 任务开始时需要填写自己的预计剩余时间,最初预计工时可以在编辑任务里设置: ![](../images/zentao_use_28.png) - 任务完成时需要填写自己的本次消耗时间: ![](../images/zentao_use_29.png) - 物理介质的看板比较直观,是Scrum标准的管理工具,禅道里面也有一个电子看板: ![](../images/zentao_use_30.png) - 当项目的任务都完成以后,开发人员可以创建版本: ![](../images/zentao_use_31.png) - 完善完版本信息后即可创建版本: ![](../images/zentao_use_32.png) - 有了版本以后,才可以根据当前版本创建测试单: ![](../images/zentao_use_33.png) - 创建测试单需要完善版本、负责人、名称等信息: ![](../images/zentao_use_34.png) ### 测试团队 > 测试团队是项目质量的保证,测试团队主要负责对项目的版本进行测试,提出Bug指派给开发人员,开发人员解决Bug后对Bug进行验证并关闭。下面我们用测试人员的账号登录,来演示下测试人员在敏捷开发中所要做的事情。 - 测试人员开始测试时,需要把测试单状态设置为进行中: ![](../images/zentao_use_35.png) - 测试人员可以在`测试->Bug`中提出测试过程中发现的Bug: ![](../images/zentao_use_36.png) - 需要完善BUG信息并指派给相应开发人员: ![](../images/zentao_use_37.png) - 当开发人员解决完Bug后可以把Bug标记为已经解决: ![](../images/zentao_use_38.png) - 此时该Bug会自动指派给测试人员,测试人员确认已经解决后可以关闭该问题,如未解决,可以激活该问题。 ![](../images/zentao_use_39.png) ## 总结 我们通过在禅道里面的一系列操作完整地演示了一套基于Scrum敏捷开发流程,其实所有角色的职责可以用下图来概况。 ![](../images/zentao_use_40.png) ## 参考资料 更多资料可以参考官方文档:https://www.zentao.net/book/zentaopmshelp/38.html ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/aop_log.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # SpringBoot应用中使用AOP记录接口访问日志 > 本文主要讲述AOP在mall项目中的应用,通过在controller层建一个切面来实现接口访问的统一日志记录。 ## AOP > AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。 ### AOP的相关术语 #### 通知(Advice) 通知描述了切面要完成的工作以及何时执行。比如我们的日志切面需要记录每个接口调用时长,就需要在接口调用前后分别记录当前时间,再取差值。 - 前置通知(Before):在目标方法调用前调用通知功能; - 后置通知(After):在目标方法调用之后调用通知功能,不关心方法的返回结果; - 返回通知(AfterReturning):在目标方法成功执行之后调用通知功能; - 异常通知(AfterThrowing):在目标方法抛出异常后调用通知功能; - 环绕通知(Around):通知包裹了目标方法,在目标方法调用之前和之后执行自定义的行为。 #### 连接点(JoinPoint) 通知功能被应用的时机。比如接口方法被调用的时候就是日志切面的连接点。 #### 切点(Pointcut) 切点定义了通知功能被应用的范围。比如日志切面的应用范围就是所有接口,即所有controller层的接口方法。 #### 切面(Aspect) 切面是通知和切点的结合,定义了何时、何地应用通知功能。 #### 引入(Introduction) 在无需修改现有类的情况下,向现有的类添加新方法或属性。 #### 织入(Weaving) 把切面应用到目标对象并创建新的代理对象的过程。 ## Spring中使用注解创建切面 ### 相关注解 - @Aspect:用于定义切面 - @Before:通知方法会在目标方法调用之前执行 - @After:通知方法会在目标方法返回或抛出异常后执行 - @AfterReturning:通知方法会在目标方法返回后执行 - @AfterThrowing:通知方法会在目标方法抛出异常后执行 - @Around:通知方法会将目标方法封装起来 - @Pointcut:定义切点表达式 ### 切点表达式 指定了通知被应用的范围,表达式格式: ```java execution(方法修饰符 返回类型 方法所属的包.类名.方法名称(方法参数) ``` ```java //com.macro.mall.tiny.controller包中所有类的public方法都应用切面里的通知 execution(public * com.macro.mall.tiny.controller.*.*(..)) //com.macro.mall.tiny.service包及其子包下所有类中的所有方法都应用切面里的通知 execution(* com.macro.mall.tiny.service..*.*(..)) //com.macro.mall.tiny.service.PmsBrandService类中的所有方法都应用切面里的通知 execution(* com.macro.mall.tiny.service.PmsBrandService.*(..)) ``` ## 添加AOP切面实现接口日志记录 ### 添加日志信息封装类WebLog > 用于封装需要记录的日志信息,包括操作的描述、时间、消耗时间、url、请求参数和返回结果等信息。 ```java package com.macro.mall.tiny.dto; /** * Controller层的日志封装类 * Created by macro on 2018/4/26. */ public class WebLog { /** * 操作描述 */ private String description; /** * 操作用户 */ private String username; /** * 操作时间 */ private Long startTime; /** * 消耗时间 */ private Integer spendTime; /** * 根路径 */ private String basePath; /** * URI */ private String uri; /** * URL */ private String url; /** * 请求类型 */ private String method; /** * IP地址 */ private String ip; /** * 请求参数 */ private Object parameter; /** * 请求返回的结果 */ private Object result; //省略了getter,setter方法 } ``` ### 添加切面类WebLogAspect > 定义了一个日志切面,在环绕通知中获取日志需要的信息,并应用到controller层中所有的public方法中去。 ```java package com.macro.mall.tiny.component; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; import cn.hutool.json.JSONUtil; import com.macro.mall.tiny.dto.WebLog; import io.swagger.annotations.ApiOperation; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 统一日志处理切面 * Created by macro on 2018/4/26. */ @Aspect @Component @Order(1) public class WebLogAspect { private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class); @Pointcut("execution(public * com.macro.mall.tiny.controller.*.*(..))") public void webLog() { } @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { } @AfterReturning(value = "webLog()", returning = "ret") public void doAfterReturning(Object ret) throws Throwable { } @Around("webLog()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); //获取当前请求对象 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //记录请求信息 WebLog webLog = new WebLog(); Object result = joinPoint.proceed(); Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); if (method.isAnnotationPresent(ApiOperation.class)) { ApiOperation apiOperation = method.getAnnotation(ApiOperation.class); webLog.setDescription(apiOperation.value()); } long endTime = System.currentTimeMillis(); String urlStr = request.getRequestURL().toString(); webLog.setBasePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath())); webLog.setIp(request.getRemoteUser()); webLog.setMethod(request.getMethod()); webLog.setParameter(getParameter(method, joinPoint.getArgs())); webLog.setResult(result); webLog.setSpendTime((int) (endTime - startTime)); webLog.setStartTime(startTime); webLog.setUri(request.getRequestURI()); webLog.setUrl(request.getRequestURL().toString()); LOGGER.info("{}", JSONUtil.parse(webLog)); return result; } /** * 根据方法和传入的参数获取请求参数 */ private Object getParameter(Method method, Object[] args) { List argList = new ArrayList<>(); Parameter[] parameters = method.getParameters(); for (int i = 0; i < parameters.length; i++) { //将RequestBody注解修饰的参数作为请求参数 RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class); if (requestBody != null) { argList.add(args[i]); } //将RequestParam注解修饰的参数作为请求参数 RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class); if (requestParam != null) { Map map = new HashMap<>(); String key = parameters[i].getName(); if (!StringUtils.isEmpty(requestParam.value())) { key = requestParam.value(); } map.put(key, args[i]); argList.add(map); } } if (argList.size() == 0) { return null; } else if (argList.size() == 1) { return argList.get(0); } else { return argList; } } } ``` ## 进行接口测试 运行项目并访问:http://localhost:8080/swagger-ui.html ![](../images/refer_screen_107.png) 可以看到控制住台中会打印如下日志信息: ```json { "result": { "code": 200, "data": { "total": 11, "totalPage": 11, "pageSize": 1, "list": [{ "productCommentCount": 100, "name": "万和", "bigPic": "", "logo": "http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/timg(5).jpg", "showStatus": 1, "id": 1, "sort": 0, "productCount": 100, "firstLetter": "W", "factoryStatus": 1 }], "pageNum": 1 }, "message": "操作成功" }, "basePath": "http://localhost:8080", "method": "GET", "parameter": [{ "pageNum": 1 }, { "pageSize": 1 }], "description": "分页查询品牌列表", "startTime": 1561273191861, "uri": "/brand/list", "url": "http://localhost:8080/brand/list", "spendTime": 101 } ``` ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-aop](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-aop) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/elasticsearch_upgrade.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Elasticsearch 升级 7.x 版本后,我感觉掉坑里了! > 最近想把我的`mall`项目升级下,支持SpringBoot 2.3.0 版本。升级过程中发现需要升级Elasticsearch到`7.x`版本,学习过我的`mall`项目的朋友应该知道, 我用的Elasticsearch是`6.x`版本,升级到`7.x`以后ElasticsearchTemplate都不让用了。本文记录了Elasticsearch从`6.x`升级到`7.x`所遇到的一些问题,给大家排排坑! ## 版本选择 > 既然我们要升级到Elasticsearch`7.x`版本,首先要选择合适的版本。如何选择合适的版本,这里有个小技巧分享给大家。 - 首先我们可以在`pom.xml`中修改SpringBoot依赖的版本为`2.3.0`; ```xml org.springframework.boot spring-boot-starter-parent 2.3.0.RELEASE ``` - 然后在项目的`External Libraries`中搜索`elasticsearch`,可以发现`elasticsearch-7.6.2.jar`这个依赖; ![](../images/elasticsearch_upgrade_01.png) - 然后打开其中的`MANIFEST.MF`文件,通过jar包中的`X-Compile-Elasticsearch-Version`属性,我们可以找到兼容的Elasticsearch版本号为`7.6.2`; ![](../images/elasticsearch_upgrade_02.png) - 之前还有试过两个版本`6.2.2`版本和`7.4.0`版本,发现与SpringBoot 2.3.0 都有兼容性问题,所以选择合适的版本很重要! - 还有一点值得注意的是,如果你使用了中文分词器(IK Analysis),也要选择对应的版本`7.6.2`,对于使用Kibana和Logstash也是如此。 ## 遇到的问题 > 选择好了合适的Elasticsearch版本后,接下来我们来讲讲升级版本遇到的问题了! - 在`application.yml`中,原来我们用来配置Elasticsearch访问路径和集群名称的配置已经不建议使用了; ![](../images/elasticsearch_upgrade_03.png) - 取而代之的是直接配置Elasticsearch的rest访问地址; ```yaml spring: elasticsearch: rest: uris: http://localhost:9200 ``` - 其实最大的问题还是ElasticsearchTemplate已经过时了,不建议使用了,之前复杂的数据操作用到了它; ![](../images/elasticsearch_upgrade_04.png) - 推荐使用的是ElasticsearchRestTemplate,这大概就是修改`application.yml`中那两个配置的原因了,修改为使用ElasticsearchRestTemplate后,我们可以发现原来ElasticsearchTemplate的`query()`方法已经没有了; ![](../images/elasticsearch_upgrade_05.png) - 可以使用ElasticsearchRestTemplate的`search()`方法来代替,原来的复杂查询将有以下改进; ```java // 使用ElasticsearchTemplate进行复杂查询 return elasticsearchTemplate.query(searchQuery, response -> { LOGGER.info("DSL:{}",searchQuery.getQuery().toString()); return convertProductRelatedInfo(response); }); // 使用ElasticsearchRestTemplate进行复杂查询 SearchHits searchHits = elasticsearchRestTemplate.search(searchQuery, EsProduct.class); return convertProductRelatedInfo(searchHits); ``` - 我们转换聚合结果对象的方法`convertProductRelatedInfo`也改进下,只是改变了方法参数类型而已; ```java //改进前 private EsProductRelatedInfo convertProductRelatedInfo(SearchResponse response) { //省略方法体代码... } //改进后 private EsProductRelatedInfo convertProductRelatedInfo(SearchHits response) { //省略方法体代码... } ``` - 如果你觉得这样就行了,那你调用下接口就会发现,报了个类型转换异常; ```bash 2020-07-21 14:40:48.154 ERROR 11616 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ClassCastException: org.elasticsearch.search.aggregations.bucket.nested.ParsedNested cannot be cast to org.elasticsearch.search.aggregations.bucket.nested.InternalNested] with root cause java.lang.ClassCastException: org.elasticsearch.search.aggregations.bucket.nested.ParsedNested cannot be cast to org.elasticsearch.search.aggregations.bucket.nested.InternalNested at com.macro.mall.tiny.service.impl.EsProductServiceImpl.convertProductRelatedInfo(EsProductServiceImpl.java:254) ~[classes/:na] at com.macro.mall.tiny.service.impl.EsProductServiceImpl.searchRelatedInfo(EsProductServiceImpl.java:229) ~[classes/:na] at com.macro.mall.tiny.controller.EsProductController.searchRelatedInfo(EsProductController.java:104) ~[classes/:na] ``` - 我们对该问题进行修复,主要就是原来的`Terms`对象都被改为了`ParsedTerms`相关对象,比如说StringTerms被改为了ParsedStringTerms对象,具体对比如下; ![](../images/elasticsearch_upgrade_06.png) - 我们还发现原来使用的ElasticsearchRepository的`search()`方法也过时了,不建议使用了,我们以前用它做了一些复杂查询; ![](../images/elasticsearch_upgrade_07.png) - 我们可以改用ElasticsearchRestTemplate的`search()`方法来实现,具体实现对比如下; ```java // ElasticsearchRepository实现复杂搜索 return productRepository.search(searchQuery) // ElasticsearchRestTemplate实现复杂搜索 SearchHits searchHits = elasticsearchRestTemplate.search(searchQuery, EsProduct.class); if(searchHits.getTotalHits()<=0){ return new PageImpl<>(null,pageable,0); } List searchProductList = searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList()); return new PageImpl<>(searchProductList,pageable,searchHits.getTotalHits()); ``` ## 总结 Elasticsearch从`6.x`升级到`7.x`改动还真不是一般的大,ElasticsearchTemplate不建议使用了,改为使用ElasticsearchRestTemplate,ElasticsearchRepository实现复杂查询的方法也不建议使用了。从此我们简单的数据操作可以使用ElasticsearchRepository,而复杂的数据操作只能使用ElasticsearchRestTemplate了。 ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-elasticsearch ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/gateway_cors.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 前后端分离项目,引入Spring Cloud Gateway遇到的一个问题! > 随着项目架构的越来越复杂,引入了新的技术,新的问题也在产生,本文将讲述一个由于网关引起的前端调用问题。 ## 问题产生 我的`mall`项目升级到微服务架构以后,加入了基于Spring Cloud Gateway的网关系统,前端调用相关服务时应该统一从网关进行调用,本以为前端直接调用网关没啥问题,后来发现会产生无法调用的情况,下面我们来记录下这个问题以及解决思路。 ## 问题重现与解决 > 这里我们以`mall-swarm`中的代码为例来演示下该问题的产生与解决。 - 首先我们先把`mall-registry`、`mall-config`、`mall-gateway`、`mall-admin`这些服务依次启动起来; - 再启动前端项目`mall-admin-web`; - 访问登录页面进行登录操作,发现无法登录,`OPTIONS`请求返回状态码`403`,到此我们还看不来是啥问题; ![](../images/gateway_cors_01.png) - 我们点开`Console`来看看到底报了啥错,发现了`CORS`这个关键信息,可以确定是产生了跨域问题,网关没有支持跨域; ![](../images/gateway_cors_02.png) - 接下来只要让网关支持跨域就可以了,在`mall-gateway`中添加全局跨域配置即可: ```java /** * 全局跨域配置 * 注意:前端从网关进行调用时需要配置 * Created by macro on 2019/7/27. */ @Configuration public class GlobalCorsConfig { @Bean public CorsWebFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedMethod("*"); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser()); source.registerCorsConfiguration("/**", config); return new CorsWebFilter(source); } } ``` - 重启`mall-gateway`服务,再次进行登录操作,发现`OPTIONS`请求虽然通过了,但是`POST`请求在`Console`中却报错了: ![](../images/gateway_cors_03.png) - 分析下该问题,应该是`mall-admin`服务中重复设置了允许跨域的过滤器的问题,只要去除`mall-admin`的全局跨域配置即可; ```java /** * 全局跨域配置 * 注意:前端从网关进行调用时不需要配置 * Created by macro on 2019/7/27. */ //@Configuration public class GlobalCorsConfig { /** * 允许跨域调用的过滤器 */ @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); //允许所有域名进行跨域调用 config.addAllowedOrigin("*"); //允许跨越发送cookie config.setAllowCredentials(true); //放行全部原始头信息 config.addAllowedHeader("*"); //允许所有请求方法跨域调用 config.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } } ``` - 重新启动`mall-admin`服务后,发现已经可以正常登录了。 ![](../images/gateway_cors_04.png) ## 总结 当前端应用通过网关调用服务时会产生跨域问题,解决方法是在网关服务中进行全局跨域配置,同时相关服务中如果有跨域配置应该去除。 ## 项目地址 [https://github.com/macrozheng/mall-swarm](https://github.com/macrozheng/mall-swarm) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/java_stream.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Java 8都出那么久了,Stream API了解下? > Java 8 引入了全新的 Stream API,可以使用声明的方式来处理数据,极大地方便了集合操作,让我们可以使用更少的代码来实现更为复杂的逻辑,本文主要对一些常用的Stream API进行介绍。 ## 什么是Stream? Stream(流)是一个来自数据源的元素队列,它可以支持聚合操作。 - 数据源:流的数据来源,构造Stream对象的数据源,比如通过一个List来构造Stream对象,这个List就是数据源; - 聚合操作:对Stream对象进行处理后使得Stream对象返回指定规则数据的操作称之为聚合操作,比如filter、map、limit、sorted等都是聚合操作。 ## Stream 聚合操作 ### 背景介绍 本文将以mall中的UmsPermission对象为例来介绍Stream API的常用操作。UmsPermission是一个权限对象,主要分为三种权限,目录、菜单以及按钮,对象定义如下。 ```java public class UmsPermission implements Serializable { private Long id; @ApiModelProperty(value = "父级权限id") private Long pid; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "权限值") private String value; @ApiModelProperty(value = "图标") private String icon; @ApiModelProperty(value = "权限类型:0->目录;1->菜单;2->按钮(接口绑定权限)") private Integer type; @ApiModelProperty(value = "前端资源路径") private String uri; @ApiModelProperty(value = "启用状态;0->禁用;1->启用") private Integer status; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "排序") private Integer sort; private static final long serialVersionUID = 1L; //省略所有getter及setter方法 } ``` ### Stream对象的创建 Stream对象分为两种,一种串行的流对象,一种并行的流对象。 ```java // permissionList指所有权限列表 // 为集合创建串行流对象 Stream stream = permissionList.stream(); // 为集合创建并行流对象 tream parallelStream = permissionList.parallelStream(); ``` ### filter 对Stream中的元素进行过滤操作,当设置条件返回true时返回相应元素。 ```java // 获取权限类型为目录的权限 List dirList = permissionList.stream() .filter(permission -> permission.getType() == 0) .collect(Collectors.toList()); ``` ### map 对Stream中的元素进行转换处理后获取。比如可以将UmsPermission对象转换成Long对象。 我们经常会有这样的需求:需要把某些对象的id提取出来,然后根据这些id去查询其他对象,这时可以使用此方法。 ```java // 获取所有权限的id组成的集合 List idList = permissionList.stream() .map(permission -> permission.getId()) .collect(Collectors.toList()); ``` ### limit 从Stream中获取指定数量的元素。 ```java // 获取前5个权限对象组成的集合 List firstFiveList = permissionList.stream() .limit(5) .collect(Collectors.toList()); ``` ### count 仅获取Stream中元素的个数。 ```java // count操作:获取所有目录权限的个数 long dirPermissionCount = permissionList.stream() .filter(permission -> permission.getType() == 0) .count(); ``` ### sorted 对Stream中元素按指定规则进行排序。 ```java // 将所有权限按先目录后菜单再按钮的顺序排序 List sortedList = permissionList.stream() .sorted((permission1,permission2)->{return permission1.getType().compareTo(permission2.getType());}) .collect(Collectors.toList()); ``` ### skip 跳过指定个数的Stream中元素,获取后面的元素。 ```java // 跳过前5个元素,返回后面的 List skipList = permissionList.stream() .skip(5) .collect(Collectors.toList()); ``` ### 用collect方法将List转成map 有时候我们需要反复对List中的对象根据id进行查询,我们可以先把该List转换为以id为key的map结构,然后再通过map.get(id)来获取对象,这样比较方便。 ```java // 将权限列表以id为key,以权限对象为值转换成map Map permissionMap = permissionList.stream() .collect(Collectors.toMap(permission -> permission.getId(), permission -> permission)); ``` ## 应用 > 我们经常会有返回树形结构数据的需求。比如这里的权限,第一层是目录权限,目录权限之下有菜单权限,菜单权限之下有按钮权限。如果我们要返回一个集合,包含目录权限,目录权限下面嵌套菜单权限,菜单权限下嵌套按钮权限。使用Stream API可以很方便的解决这个问题。 **注意:这里我们的权限上下级之间以pid来关联,pid是指上一级权限的id,顶级权限的id为0。** ### 定义包含下级权限的对象 继承自UmsPermission对象,之增加了一个children属性,用于存储下级权限。 ```java /** * Created by macro on 2018/9/30. */ public class UmsPermissionNode extends UmsPermission { private List children; public List getChildren() { return children; } public void setChildren(List children) { this.children = children; } } ``` ### 定义获取树形结构的方法 我们先过滤出pid为0的顶级权限,然后给每个顶级权限设置其子级权限,covert方法的主要用途就是从所有权限中找出相应权限的子级权限。 ```java @Override public List treeList() { List permissionList = permissionMapper.selectByExample(new UmsPermissionExample()); List result = permissionList.stream() .filter(permission -> permission.getPid().equals(0L)) .map(permission -> covert(permission, permissionList)).collect(Collectors.toList()); return result; } ``` ### 为每个权限设置子级权限 这里我们使用filter操作来过滤出每个权限的子级权限,由于子级权限下面可能还会有子级权限,这里我们使用递归来解决。但是递归操作什么时候停止,这里把递归调用方法放到了map操作中去,当没有子级权限时filter下的map操作便不会再执行,从而停止递归。 ```java /** * 将权限转换为带有子级的权限对象 * 当找不到子级权限的时候map操作不会再递归调用covert */ private UmsPermissionNode covert(UmsPermission permission, List permissionList) { UmsPermissionNode node = new UmsPermissionNode(); BeanUtils.copyProperties(permission, node); List children = permissionList.stream() .filter(subPermission -> subPermission.getPid().equals(permission.getId())) .map(subPermission -> covert(subPermission, permissionList)).collect(Collectors.toList()); node.setChildren(children); return node; } ``` ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-stream](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-stream) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/mall_permission_question.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 搞定Mall项目中的权限管理功能,弄懂这些问题就妥了! > 最近发现很多朋友问我权限管理功能相关的问题,这里整理了下问的比较多的问题,统一答复下! ### mall项目中的权限管理功能是如何实现的? 之前写过几篇文章,包括了权限管理功能介绍、后端实现和前端实现,看一下基本就清楚了! - [《大家心心念念的权限管理功能,这次安排上了!》](https://mp.weixin.qq.com/s/3TNrPNmxHpFTcAhfjnuP0g) - [《手把手教你搞定权限管理,结合Spring Security实现接口的动态权限控制!》](https://mp.weixin.qq.com/s/nvKKNSJuIrGuHeJkUeO7rw) - [《手把手教你搞定权限管理,结合Vue实现菜单的动态权限控制!》](https://mp.weixin.qq.com/s/UXPeJtx-mIvCPjJW3__c6g) ### `ums_permission`表还在使用么? `ums_permission`表已经不再使用了,不再使用的表还包括`ums_admin_permission_relation`和`ums_role_permission_relation`表,最新版已经移除了相关使用代码。 ### mall项目升级代码后`ums_resource`表找不到? 升级代码以后需要同时导入最新版本的SQL脚本,否则会找不到新创建的表,SQL脚本在项目的`document\sql`文件夹下面。 ### 只实现了管理后台的权限,移动端权限如何处理的? 移动端只实现了登录认证,暂时不做权限处理。 ### 在管理后台添加了一个菜单,为什么前端没有显示? 只有在前端路由中配置了的菜单,在管理后台添加后才会显示,否则没有效果。 ![](../images/mall_permission_question_01.png) ### 前端路由中修改了菜单名称,为什么还是原来的名称? 菜单名称、图标、是否隐藏都是由管理后台配置的,当管理后台配置好后,前端修改是无效的。 ![](../images/mall_permission_question_02.png) ### mall-swarm项目中的权限管理功能是如何实现的? 采用了基于Oauth2的的统一认证鉴权方式,通过认证服务进行统一认证,然后通过网关来统一校验认证和鉴权。具体可以参考:[《微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!》](https://mp.weixin.qq.com/s/npyZsa4p30PLULxjskxKSA) ### mall和mall-swarm项目中权限管理的实现有何不同? mall项目实现方式是Spring Security,相当于把安全功能封装成了一个工具包`mall-security`,然后其他模块通过依赖该工具包来实现权限管理,比如`mall-admin`模块。 mall-swarm项目实现方式是Oauth2+Gateway,采用的是统一的认证和鉴权,mall-swarm中的其他模块只需关注自己的相关业务,而无需依赖任何安全工具包,更加适合微服务架构。 ### mall-swarm项目对接前端项目时为什么会提示你已经被登出? 一种情况是前端访问后端接口没有走网关,首先需要修改`mall-admin-web`项目的后端接口访问基础路径,修改文件为项目`config`目录下的`dev.env.js`,将`BASE_API`改为从网关访问`mall-admin`服务的路径。 ```javascript 'use strict' const merge = require('webpack-merge') const prodEnv = require('./prod.env') module.exports = merge(prodEnv, { NODE_ENV: '"development"', BASE_API: '"http://localhost:8201/mall-admin"' }) ``` 另一种情况是没有更新到最新代码,需要更新到最新代码并导入最新版本的SQL。之前在`mall-gateway`项目中配置白名单的时候把`/mall-admin/admin/info`接口配置进去了,会导致无法登录的情况,需要去除掉,目前已经修复了。 ```yaml secure: ignore: urls: #配置白名单路径 - "/mall-admin/admin/info" ``` 还有一点需要注意的是由于`/mall-admin/admin/info`接口配置已经不在白名单中了,所以需要登录的用户需要配置该接口的相应资源,否则会无法登录。 ![](../images/mall_permission_question_03.png) ### mall-swarm项目如何访问需要登录认证的接口? 由于项目中存在两套不同的用户体系,后台用户和前台用户,认证中心对多用户体系也有所支持。如何访问需要登录的接口,先调用认证中心接口获取token,不同体系下的用户需要传入不同的`client_id`和`client_secret`,后台用户为`admin-app:123456`,前台用户为`portal-app:123456`。 ![](../images/mall_permission_question_04.png) 然后将token添加到请求头中,即可访问需要权限的接口了。 ![](../images/mall_permission_question_05.png) 当然对原来的登录接口也做了兼容处理,分别会从内部调用认证中心获取Token,依然可以使用。 - 后台用户登录接口:http://localhost:8201/mall-admin/admin/login - 前台用户登录接口:http://localhost:8201/mall-portal/sso/login ### 只想学习权限管理功能,有没有什么简单的项目可以学习下? 可以直接学习下`mall-tiny`项目,`mall-tiny`是一款基于SpringBoot+MyBatis-Plus的快速开发脚手架,拥有完整的权限管理功能,可对接Vue前端,开箱即用。具体参考[《还在从零开始搭建项目?手撸了款快速开发脚手架!》](https://mp.weixin.qq.com/s/tN3zjoKQxg1U19D4Slih8w) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/mall_tiny_elk.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # SpringBoot应用整合ELK实现日志收集 > ELK即Elasticsearch、Logstash、Kibana,组合起来可以搭建线上日志系统,本文主要讲解使用ELK来收集SpringBoot应用产生的日志。 ## 学习前需要了解的内容 - [开发者必备Docker命令](https://mp.weixin.qq.com/s/d_CuljDTJq680NTndAay8g) - [使用Docker Compose部署SpringBoot应用](https://mp.weixin.qq.com/s/iMl9bJ4SxUsNHBbiS5VUcw) - [SpringBoot应用中使用AOP记录接口访问日志](https://mp.weixin.qq.com/s/mNujRjejQ1bITveFI6gkcg) ## ELK中各个服务的作用 - Elasticsearch:用于存储收集到的日志信息; - Logstash:用于收集日志,SpringBoot应用整合了Logstash以后会把日志发送给Logstash,Logstash再把日志转发给Elasticsearch; - Kibana:通过Web端的可视化界面来查看日志。 ## 使用Docker Compose 搭建ELK环境 ### 需要下载的Docker镜像 ```shell docker pull elasticsearch:6.4.0 docker pull logstash:6.4.0 docker pull kibana:6.4.0 ``` ### 搭建前准备 - elasticsearch 启动成功需要特殊配置,具体参考[mall在Linux环境下的部署(基于Docker Compose)](https://mp.weixin.qq.com/s/JYkvdub9DP5P9ULX4mehUw)中的elasticsearch部分; - docker-compose.yml文件地址:https://github.com/macrozheng/mall-learning/blob/master/mall-tiny-elk/src/main/docker/docker-compose.yml - logstash-springboot.conf配置文件地址:https://github.com/macrozheng/mall-learning/blob/master/mall-tiny-elk/src/main/docker/logstash-springboot.conf ### 开始搭建 #### 创建一个存放logstash配置的目录并上传配置文件 ##### logstash-springboot.conf文件内容 ``` input { tcp { mode => "server" host => "0.0.0.0" port => 4560 codec => json_lines } } output { elasticsearch { hosts => "es:9200" index => "springboot-logstash-%{+YYYY.MM.dd}" } } ``` ##### 创建配置文件存放目录并上传配置文件到该目录 ```shell mkdir /mydata/logstash ``` #### 使用docker-compose.yml脚本启动ELK服务 ##### docker-compose.yml内容 ```yml version: '3' services: elasticsearch: image: elasticsearch:6.4.0 container_name: elasticsearch environment: - "cluster.name=elasticsearch" #设置集群名称为elasticsearch - "discovery.type=single-node" #以单一节点模式启动 - "ES_JAVA_OPTS=-Xms512m -Xmx512m" #设置使用jvm内存大小 volumes: - /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins #插件文件挂载 - /mydata/elasticsearch/data:/usr/share/elasticsearch/data #数据文件挂载 ports: - 9200:9200 - 9300:9300 kibana: image: kibana:6.4.0 container_name: kibana links: - elasticsearch:es #可以用es这个域名访问elasticsearch服务 depends_on: - elasticsearch #kibana在elasticsearch启动之后再启动 environment: - "elasticsearch.hosts=http://es:9200" #设置访问elasticsearch的地址 ports: - 5601:5601 logstash: image: logstash:6.4.0 container_name: logstash volumes: - /mydata/logstash/logstash-springboot.conf:/usr/share/logstash/pipeline/logstash.conf #挂载logstash的配置文件 depends_on: - elasticsearch #kibana在elasticsearch启动之后再启动 links: - elasticsearch:es #可以用es这个域名访问elasticsearch服务 ports: - 4560:4560 ``` ##### 上传到linux服务器并使用docker-compose命令运行 ```shell docker-compose up -d ``` 注意:Elasticsearch启动可能需要好几分钟,要耐心等待。 ![](../images/tech_screen_01.png) #### 在logstash中安装json_lines插件 ```shell # 进入logstash容器 docker exec -it logstash /bin/bash # 进入bin目录 cd /bin/ # 安装插件 logstash-plugin install logstash-codec-json_lines # 退出容器 exit # 重启logstash服务 docker restart logstash ``` #### 开启防火墙并在kibana中查看 ```shell systemctl stop firewalld ``` 访问地址:http://192.168.3.101:5601 ![](../images/tech_screen_02.png) ## SpringBoot应用集成Logstash ### 在pom.xml中添加logstash-logback-encoder依赖 ```xml net.logstash.logback logstash-logback-encoder 5.3 ``` ### 添加配置文件logback-spring.xml让logback的日志输出到logstash > 注意appender节点下的destination需要改成你自己的logstash服务地址,比如我的是:192.168.3.101:4560 。 ```xml ${APP_NAME} ${LOG_FILE_PATH}/${APP_NAME}-%d{yyyy-MM-dd}.log 30 ${FILE_LOG_PATTERN} 192.168.3.101:4560 ``` ### 运行Springboot应用 ![](../images/tech_screen_03.png) ## 在kibana中查看日志信息 ### 创建index pattern ![](../images/tech_screen_04.png) ![](../images/tech_screen_05.png) ![](../images/tech_screen_06.png) ### 查看收集的日志 ![](../images/tech_screen_07.png) ### 调用接口进行测试 ![](../images/tech_screen_08.png) ![](../images/tech_screen_09.png) ### 制造一个异常并查看 #### 修改获取所有品牌列表接口 ![](../images/tech_screen_10.png) #### 调用该接口并查看日志 ![](../images/tech_screen_11.png) ### 总结 搭建了ELK日志收集系统之后,我们如果要查看SpringBoot应用的日志信息,就不需要查看日志文件了,直接在Kibana中查看即可。 ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-elk](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-elk) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/minio_use.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 前后端分离项目,如何优雅实现文件存储! > 在上一节中我们讲到了使用MinIO来自建对象存储服务,这次我们来讲下MinIO如何结合SpringBoot和Vue来实现文件存储。 ## 学前准备 学习本文需要一些MinIO的基础知识,还不了解的小伙伴可以参考下:[Github标星19K+Star,10分钟自建对象存储服务!](https://mp.weixin.qq.com/s/kvLZRqgm1lEITm1j6rzJ0A) ## 结合SpringBoot使用 > 接下来我们将结合SpringBoot来实现一个完整的图片上传与删除操作。 - 上传流程示意图: ![](../images/minio_use_22.png) - 在pom.xml中添加MinIO的相关依赖: ```xml io.minio minio 3.0.10 ``` - 在SpringBoot中开启文件上传功能,需要在application.yml添加如下配置: ```yaml spring: servlet: multipart: enabled: true #开启文件上传 max-file-size: 10MB #限制文件上传大小为10M ``` - 添加一个`MinioController`控制器用于实现文件的上传和删除操作: ```java /** * Created by macro on 2019/12/25. */ @Api(tags = "MinioController", description = "MinIO对象存储管理") @Controller @RequestMapping("/minio") public class MinioController { private static final Logger LOGGER = LoggerFactory.getLogger(MinioController.class); @Value("${minio.endpoint}") private String ENDPOINT; @Value("${minio.bucketName}") private String BUCKET_NAME; @Value("${minio.accessKey}") private String ACCESS_KEY; @Value("${minio.secretKey}") private String SECRET_KEY; @ApiOperation("文件上传") @RequestMapping(value = "/upload", method = RequestMethod.POST) @ResponseBody public CommonResult upload(@RequestParam("file") MultipartFile file) { try { //创建一个MinIO的Java客户端 MinioClient minioClient = new MinioClient(ENDPOINT, ACCESS_KEY, SECRET_KEY); boolean isExist = minioClient.bucketExists(BUCKET_NAME); if (isExist) { LOGGER.info("存储桶已经存在!"); } else { //创建存储桶并设置只读权限 minioClient.makeBucket(BUCKET_NAME); minioClient.setBucketPolicy(BUCKET_NAME, "*.*", PolicyType.READ_ONLY); } String filename = file.getOriginalFilename(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); // 设置存储对象名称 String objectName = sdf.format(new Date()) + "/" + filename; // 使用putObject上传一个文件到存储桶中 minioClient.putObject(BUCKET_NAME, objectName, file.getInputStream(), file.getContentType()); LOGGER.info("文件上传成功!"); MinioUploadDto minioUploadDto = new MinioUploadDto(); minioUploadDto.setName(filename); minioUploadDto.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName); return CommonResult.success(minioUploadDto); } catch (Exception e) { LOGGER.info("上传发生错误: {}!", e.getMessage()); } return CommonResult.failed(); } @ApiOperation("文件删除") @RequestMapping(value = "/delete", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("objectName") String objectName) { try { MinioClient minioClient = new MinioClient(ENDPOINT, ACCESS_KEY, SECRET_KEY); minioClient.removeObject(BUCKET_NAME, objectName); return CommonResult.success(null); } catch (Exception e) { e.printStackTrace(); } return CommonResult.failed(); } } ``` - 在application.yml中对MinIO客户端进行配置: ```yaml # MinIO对象存储相关配置 minio: endpoint: http://192.168.6.132:9090 #MinIO服务所在地址 bucketName: mall #存储桶名称 accessKey: minioadmin #访问的key secretKey: minioadmin #访问的秘钥 ``` - 启动我的SpringBoot应用,使用Postman来访问上传接口进行文件上传,上传接口地址:http://localhost:8080/minio/upload ![](../images/minio_use_14.png) - 上传完成后,我们打开MinIO的管理界面可以看到上传后的图片,也可以通过返回的url来访问图片: ![](../images/minio_use_15.png) - 我们可以调用删除接口来删除该图片,需要注意的是`objectName`值是存储桶中的图片相对路径,删除文件接口地址:http://localhost:8080/minio/delete ![](../images/minio_use_16.png) ## 结合Vue使用 > 经过上面操作,我们的SpringBoot应用已经可以完成文件上传与删除操作了,接下来我们结合Vue来实现前端上传图片到MinIO中,以`mall-admin-web`中的代码为例。 - 我们的SpringBoot应用需要支持跨域请求,否则Vue前端无法进行接口调用,我们先添加一个全局的跨域请求配置: ```java /** * 全局跨域配置 * Created by macro on 2019/7/27. */ @Configuration public class GlobalCorsConfig { /** * 允许跨域调用的过滤器 */ @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); //允许所有域名进行跨域调用 config.addAllowedOrigin("*"); //允许跨越发送cookie config.setAllowCredentials(true); //放行全部原始头信息 config.addAllowedHeader("*"); //允许所有请求方法跨域调用 config.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } } ``` - `mall-admin-web`的文件上传操作主要是在`singleUpload.vue`和`multiUpload.vue`中,下面我们以`singleUpload.vue`的修改为例。 - 我们需要把原来的OSS上传和现在的MinIO上传做个兼容操作,先在Vue实例的数据对象中添加三个属性: ![](../images/minio_use_17.png) - 然后根据`useOss`属性设置`el-upload`上传组件的提交地址和提交参数: ![](../images/minio_use_18.png) - 在`el-upload`上传文件之前的钩子函数中添加如下代码,对于使用MinIO上传的操作不进行获取OSS上传策略的操作; ![](../images/minio_use_19.png) - 最后在`el-upload`文件上传成功的钩子函数中添加如下代码,对于使用MinIO上传的操作直接从返回结果中获取文件url; ![](../images/minio_use_20.png) - 运行`mall-admin-web`项目,使用商品分类下的添加功能来测试下文件上传,发现已经可以成功上传,图片也已经可以正常回显: ![](../images/minio_use_21.png) ## 后端项目地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-oss ## 前端项目地址 https://github.com/macrozheng/mall-admin-web ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/mybatis_mapper.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # MyBatis Generator使用过程中踩过的一个坑 > 在使用MyBatis Generator生成代码的过程中,曾经遇到一个坑,每次生成mapper.xml的时候并不是直接覆盖原文件,而是在原文件中追加了新的内容,导致运行项目出错,本文主要讲解如何解决这个问题。 ## 问题重现 ### 示例代码 使用的是mall-tiny-02的代码,代码地址:[https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-02](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-02) ### 直接运行MallTinyApplication的main函数 发现正常运行,启动成功! ![](../images/refer_screen_99.png) ### 运行代码生成器 运行com.macro.mall.tiny.mbg.Generator的main方法 ### 重新启动MallTinyApplication的main函数 发现已经无法正常运行,其中有这么一行关键性的错误: ```java nested exception is org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'file [D:\developer\github\mall-learning\mall-tiny-02\target\classes\com\macro\mall\tiny\mbg\mapper\PmsBrandMapper.xml]'. Cause: java.lang.IllegalArgumentException: Result Maps collection already contains value for com.macro.mall.tiny.mbg.mapper.PmsBrandMapper.BaseResultMap ``` **表明了PmsBrandMapper.xml文件解析错误,BaseResultMap重复定义。** ### 查看PmsBrandMapper.xml文件 从中可以发现MyBatis Generator生成的mapper.xml文件信息是直接追加在原来的文件上的,并不是直接覆盖,导致了这个错误。 ![](../images/refer_screen_100.png) ## 问题解决 > 以前一直以为是MyBatis Generator生成的问题,直接删除mapper.xml所在文件夹,重新生成就好了,现在提供一种MyBatis Generator官方提供的解决方法。 ### 升级MyBatis Generator的版本 MyBatis Generator 在1.3.7版本提供了解决方案,我们目前使用的版本为1.3.3。 ```xml org.mybatis.generator mybatis-generator-core 1.3.7 ``` ### 在generatorConfig.xml文件中添加覆盖mapper.xml的插件 ```xml ``` ### 重新运行代码生成器 发现PmsBrandMapper.xml生成已经正常,应用也可以正常运行了。 ![](../images/refer_screen_101.png) ![](../images/refer_screen_102.png) ## 项目源码地址 [https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-02](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-02) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/permission_back.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 手把手教你搞定权限管理,结合Spring Security实现接口的动态权限控制! > 权限控管理作为后台管理系统中必要的功能,mall项目中结合Spring Security实现了基于路径的动态权限控制,可以对后台接口访问进行细粒度的控制,今天我们来讲下它的后端实现原理。 ## 前置知识 > 学习本文需要一些Spring Security的知识,对Spring Security不太了解的朋友可以看下以下文章。 - [mall整合SpringSecurity和JWT实现认证和授权(一)](https://mp.weixin.qq.com/s/HFAfcSGANrdVJeTmT-7X_A) - [mall整合SpringSecurity和JWT实现认证和授权(二)](https://mp.weixin.qq.com/s/yO_8nAN-zoCB86ep4nuAOg) - [仅需四步,整合SpringSecurity+JWT实现登录认证 !](https://mp.weixin.qq.com/s/4xfdeBHKa_pXabgo41z26g) ## 数据库设计 > 权限管理相关表已经重新设计,将原来的权限拆分成了菜单和资源,菜单管理用于控制前端菜单的显示和隐藏,资源管理用来控制后端接口的访问权限。 ### 数据库表结构 > 其中`ums_admin`、`ums_role`、`ums_admin_role_relation`为原来的表,其他均为新增表。 ![](../images/mall_permission_back_01.png) ### 数据库表介绍 > 接下来我们将对每张表的用途做个详细介绍。 #### ums_admin > 后台用户表,定义了后台用户的一些基本信息。 ```sql create table ums_admin ( id bigint not null auto_increment, username varchar(64) comment '用户名', password varchar(64) comment '密码', icon varchar(500) comment '头像', email varchar(100) comment '邮箱', nick_name varchar(200) comment '昵称', note varchar(500) comment '备注信息', create_time datetime comment '创建时间', login_time datetime comment '最后登录时间', status int(1) default 1 comment '帐号启用状态:0->禁用;1->启用', primary key (id) ); ``` #### ums_role > 后台用户角色表,定义了后台用户角色的一些基本信息,通过给后台用户分配角色来实现菜单和资源的分配。 ```sql create table ums_role ( id bigint not null auto_increment, name varchar(100) comment '名称', description varchar(500) comment '描述', admin_count int comment '后台用户数量', create_time datetime comment '创建时间', status int(1) default 1 comment '启用状态:0->禁用;1->启用', sort int default 0, primary key (id) ); ``` #### ums_admin_role_relation > 后台用户和角色关系表,多对多关系表,一个角色可以分配给多个用户。 ```sql create table ums_admin_role_relation ( id bigint not null auto_increment, admin_id bigint, role_id bigint, primary key (id) ); ``` #### ums_menu > 后台菜单表,用于控制后台用户可以访问的菜单,支持隐藏、排序和更改名称、图标。 ```sql create table ums_menu ( id bigint not null auto_increment, parent_id bigint comment '父级ID', create_time datetime comment '创建时间', title varchar(100) comment '菜单名称', level int(4) comment '菜单级数', sort int(4) comment '菜单排序', name varchar(100) comment '前端名称', icon varchar(200) comment '前端图标', hidden int(1) comment '前端隐藏', primary key (id) ); ``` #### ums_resource > 后台资源表,用于控制后台用户可以访问的接口,使用了Ant路径的匹配规则,可以使用通配符定义一系列接口的权限。 ```sql create table ums_resource ( id bigint not null auto_increment, category_id bigint comment '资源分类ID', create_time datetime comment '创建时间', name varchar(200) comment '资源名称', url varchar(200) comment '资源URL', description varchar(500) comment '描述', primary key (id) ); ``` #### ums_resource_category > 后台资源分类表,在细粒度进行权限控制时,可能资源会比较多,所以设计了个资源分类的概念,便于给角色分配资源。 ```sql create table ums_resource_category ( id bigint not null auto_increment, create_time datetime comment '创建时间', name varchar(200) comment '分类名称', sort int(4) comment '排序', primary key (id) ); ``` #### ums_role_menu_relation > 后台角色菜单关系表,多对多关系,可以给一个角色分配多个菜单。 ```sql create table ums_role_menu_relation ( id bigint not null auto_increment, role_id bigint comment '角色ID', menu_id bigint comment '菜单ID', primary key (id) ); ``` #### ums_role_resource_relation > 后台角色资源关系表,多对多关系,可以给一个角色分配多个资源。 ```sql create table ums_role_resource_relation ( id bigint not null auto_increment, role_id bigint comment '角色ID', resource_id bigint comment '资源ID', primary key (id) ); ``` ## 结合Spring Security实现 > 实现动态权限是在原`mall-security`模块的基础上进行改造完成的,原实现有不清楚的可以自行参照`前置知识`中的文档来学习。 ### 以前的权限控制 > 以前的权限控制是采用Spring Security的默认机制实现的,下面我们以商品模块的代码为例来讲讲实现原理。 - 首先我们在需要权限的接口上使用`@PreAuthorize`注解定义好需要的权限; ```java /** * 商品管理Controller * Created by macro on 2018/4/26. */ @Controller @Api(tags = "PmsProductController", description = "商品管理") @RequestMapping("/product") public class PmsProductController { @Autowired private PmsProductService productService; @ApiOperation("创建商品") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody @PreAuthorize("hasAuthority('pms:product:create')") public CommonResult create(@RequestBody PmsProductParam productParam, BindingResult bindingResult) { int count = productService.create(productParam); if (count > 0) { return CommonResult.success(count); } else { return CommonResult.failed(); } } } ``` - 然后将该权限值存入到权限表中,当用户登录时,将其所拥有的权限查询出来; ```java /** * UmsAdminService实现类 * Created by macro on 2018/4/26. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Override public UserDetails loadUserByUsername(String username){ //获取用户信息 UmsAdmin admin = getAdminByUsername(username); if (admin != null) { List permissionList = getPermissionList(admin.getId()); return new AdminUserDetails(admin,permissionList); } throw new UsernameNotFoundException("用户名或密码错误"); } } ``` - 之后Spring Security把用户拥有的权限值和接口上注解定义的权限值进行比对,如果包含则可以访问,反之就不可以访问; - 但是这样做会带来一些问题,我们需要在每个接口上都定义好访问该接口的权限值,而且只能挨个控制接口的权限,无法批量控制。其实每个接口都可以由它的访问路径唯一确定,我们可以使用基于路径的动态权限控制来解决这些问题。 ### 基于路径的动态权限控制 > 接下来我们详细介绍下如何使用Spring Security实现基于路径的动态权限。 首先我们需要创建一个过滤器,用于实现动态权限控制,这里需要注意的是`doFilter`方法,对于OPTIONS请求直接放行,否则前端调用会出现跨域问题。对于配置在`IgnoreUrlsConfig`中的白名单路径我也需要直接放行,所有的鉴权操作都会在`super.beforeInvocation(fi)`中进行。 ```java /** * 动态权限过滤器,用于实现基于路径的动态权限过滤 * Created by macro on 2020/2/7. */ public class DynamicSecurityFilter extends AbstractSecurityInterceptor implements Filter { @Autowired private DynamicSecurityMetadataSource dynamicSecurityMetadataSource; @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Autowired public void setMyAccessDecisionManager(DynamicAccessDecisionManager dynamicAccessDecisionManager) { super.setAccessDecisionManager(dynamicAccessDecisionManager); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain); //OPTIONS请求直接放行 if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){ fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); return; } //白名单请求直接放行 PathMatcher pathMatcher = new AntPathMatcher(); for (String path : ignoreUrlsConfig.getUrls()) { if(pathMatcher.match(path,request.getRequestURI())){ fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); return; } } //此处会调用AccessDecisionManager中的decide方法进行鉴权操作 InterceptorStatusToken token = super.beforeInvocation(fi); try { fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally { super.afterInvocation(token, null); } } @Override public void destroy() { } @Override public Class getSecureObjectClass() { return FilterInvocation.class; } @Override public SecurityMetadataSource obtainSecurityMetadataSource() { return dynamicSecurityMetadataSource; } } ``` 在DynamicSecurityFilter中调用super.beforeInvocation(fi)方法时会调用AccessDecisionManager中的decide方法用于鉴权操作,而decide方法中的configAttributes参数会通过SecurityMetadataSource中的getAttributes方法来获取,configAttributes其实就是配置好的访问当前接口所需要的权限,下面是简化版的beforeInvocation源码。 ```java public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware { protected InterceptorStatusToken beforeInvocation(Object object) { //获取元数据 Collection attributes = this.obtainSecurityMetadataSource() .getAttributes(object); Authentication authenticated = authenticateIfRequired(); //进行鉴权操作 try { this.accessDecisionManager.decide(authenticated, object, attributes); } catch (AccessDeniedException accessDeniedException) { publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException)); throw accessDeniedException; } } } ``` 知道了鉴权的原理,接下来我们需要自己实现SecurityMetadataSource接口的getAttributes方法,用于获取当前访问路径所需资源。 ```java /** * 动态权限数据源,用于获取动态权限规则 * Created by macro on 2020/2/7. */ public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { private static Map configAttributeMap = null; @Autowired private DynamicSecurityService dynamicSecurityService; @PostConstruct public void loadDataSource() { configAttributeMap = dynamicSecurityService.loadDataSource(); } public void clearDataSource() { configAttributeMap.clear(); configAttributeMap = null; } @Override public Collection getAttributes(Object o) throws IllegalArgumentException { if (configAttributeMap == null) this.loadDataSource(); List configAttributes = new ArrayList<>(); //获取当前访问的路径 String url = ((FilterInvocation) o).getRequestUrl(); String path = URLUtil.getPath(url); PathMatcher pathMatcher = new AntPathMatcher(); Iterator iterator = configAttributeMap.keySet().iterator(); //获取访问该路径所需资源 while (iterator.hasNext()) { String pattern = iterator.next(); if (pathMatcher.match(pattern, path)) { configAttributes.add(configAttributeMap.get(pattern)); } } // 未设置操作请求权限,返回空集合 return configAttributes; } @Override public Collection getAllConfigAttributes() { return null; } @Override public boolean supports(Class aClass) { return true; } } ``` 由于我们的后台资源规则被缓存在了一个Map对象之中,所以当后台资源发生变化时,我们需要清空缓存的数据,然后下次查询时就会被重新加载进来。这里我们需要修改UmsResourceController类,注入DynamicSecurityMetadataSource,当修改后台资源时,需要调用clearDataSource方法来清空缓存的数据。 ```java /** * 后台资源管理Controller * Created by macro on 2020/2/4. */ @Controller @Api(tags = "UmsResourceController", description = "后台资源管理") @RequestMapping("/resource") public class UmsResourceController { @Autowired private UmsResourceService resourceService; @Autowired private DynamicSecurityMetadataSource dynamicSecurityMetadataSource; @ApiOperation("添加后台资源") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody public CommonResult create(@RequestBody UmsResource umsResource) { int count = resourceService.create(umsResource); dynamicSecurityMetadataSource.clearDataSource(); if (count > 0) { return CommonResult.success(count); } else { return CommonResult.failed(); } } } ``` 之后我们需要实现AccessDecisionManager接口来实现权限校验,对于没有配置资源的接口我们直接允许访问,对于配置了资源的接口,我们把访问所需资源和用户拥有的资源进行比对,如果匹配则允许访问。 ```java /** * 动态权限决策管理器,用于判断用户是否有访问权限 * Created by macro on 2020/2/7. */ public class DynamicAccessDecisionManager implements AccessDecisionManager { @Override public void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { // 当接口未被配置资源时直接放行 if (CollUtil.isEmpty(configAttributes)) { return; } Iterator iterator = configAttributes.iterator(); while (iterator.hasNext()) { ConfigAttribute configAttribute = iterator.next(); //将访问所需资源或用户拥有资源进行比对 String needAuthority = configAttribute.getAttribute(); for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) { if (needAuthority.trim().equals(grantedAuthority.getAuthority())) { return; } } } throw new AccessDeniedException("抱歉,您没有访问权限"); } @Override public boolean supports(ConfigAttribute configAttribute) { return true; } @Override public boolean supports(Class aClass) { return true; } } ``` 我们之前在DynamicSecurityMetadataSource中注入了一个DynamicSecurityService对象,它是我自定义的一个动态权限业务接口,其主要用于加载所有的后台资源规则。 ```java /** * 动态权限相关业务类 * Created by macro on 2020/2/7. */ public interface DynamicSecurityService { /** * 加载资源ANT通配符和资源对应MAP */ Map loadDataSource(); } ``` 接下来我们需要修改Spring Security的配置类SecurityConfig,当有动态权限业务类时在FilterSecurityInterceptor过滤器前添加我们的动态权限过滤器。这里在创建动态权限相关对象时,还使用了@ConditionalOnBean这个注解,当没有动态权限业务类时就不会创建动态权限相关对象,实现了有动态权限控制和没有这两种情况的兼容。 ```java /** * 对SpringSecurity的配置的扩展,支持自定义白名单资源路径和查询用户逻辑 * Created by macro on 2019/11/5. */ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired(required = false) private DynamicSecurityService dynamicSecurityService; @Override protected void configure(HttpSecurity httpSecurity) throws Exception { ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity .authorizeRequests(); //有动态权限配置时添加动态权限校验过滤器 if(dynamicSecurityService!=null){ registry.and().addFilterBefore(dynamicSecurityFilter(), FilterSecurityInterceptor.class); } } @ConditionalOnBean(name = "dynamicSecurityService") @Bean public DynamicAccessDecisionManager dynamicAccessDecisionManager() { return new DynamicAccessDecisionManager(); } @ConditionalOnBean(name = "dynamicSecurityService") @Bean public DynamicSecurityFilter dynamicSecurityFilter() { return new DynamicSecurityFilter(); } @ConditionalOnBean(name = "dynamicSecurityService") @Bean public DynamicSecurityMetadataSource dynamicSecurityMetadataSource() { return new DynamicSecurityMetadataSource(); } } ``` 这里还有个问题需要提下,当前端跨域访问没有权限的接口时,会出现跨域问题,只需要在没有权限访问的处理类RestfulAccessDeniedHandler中添加允许跨域访问的响应头即可。 ````java /** * 自定义返回结果:没有权限访问时 * Created by macro on 2018/4/26. */ public class RestfulAccessDeniedHandler implements AccessDeniedHandler{ @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException { response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Cache-Control","no-cache"); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json"); response.getWriter().println(JSONUtil.parse(CommonResult.forbidden(e.getMessage()))); response.getWriter().flush(); } } ```` 当我们其他模块需要动态权限控制时,只要创建一个DynamicSecurityService对象就行了,比如在`mall-admin`模块中我们启用了动态权限功能。 ```java /** * mall-security模块相关配置 * Created by macro on 2019/11/9. */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class MallSecurityConfig extends SecurityConfig { @Autowired private UmsAdminService adminService; @Autowired private UmsResourceService resourceService; @Bean public UserDetailsService userDetailsService() { //获取登录用户信息 return username -> adminService.loadUserByUsername(username); } @Bean public DynamicSecurityService dynamicSecurityService() { return new DynamicSecurityService() { @Override public Map loadDataSource() { Map map = new ConcurrentHashMap<>(); List resourceList = resourceService.listAll(); for (UmsResource resource : resourceList) { map.put(resource.getUrl(), new org.springframework.security.access.SecurityConfig(resource.getId() + ":" + resource.getName())); } return map; } }; } } ``` ## 权限管理功能演示 具体参考:[大家心心念念的权限管理功能,这次安排上了!](https://mp.weixin.qq.com/s/3TNrPNmxHpFTcAhfjnuP0g) ## 项目源码地址 [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/permission_front.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 手把手教你搞定权限管理,结合Vue实现菜单的动态权限控制! > 权限管理在后端项目中主要体现在对接口访问权限的控制,在前端项目中主要体现在对菜单访问权限的控制。在[《手把手教你搞定权限管理,结合Spring Security实现接口的动态权限控制!》](https://mp.weixin.qq.com/s/nvKKNSJuIrGuHeJkUeO7rw)中我们实现了对后端接口的动态权限控制,今天我们讲下如何结合Vue来实现菜单的动态权限控制。 ## 使用技术 > `mall-admin-web`实现菜单的动态权限控制使用到了两种技术,一种是Vue Router,另一种是Vuex,我们先来了解下这两种技术。 ### Vue Router Vue Router是Vue.js官方的路由管理器。路由就是一个路径,当我们访问指定路径时就会跳转到指定页面。 我们项目的路由都是在`src/router/index.js`文件中定义的,举个例子,比如我们的商品列表页面路由定义如下。 ![](../images/mall_permission_front_01.png) 所以当我们访问[http://localhost:8090/#/pms/product](http://localhost:8090/#/pms/product)时就会跳转到商品列表页面。 ![](../images/mall_permission_front_02.png) 我们前端的左侧菜单都是根据Vue Router中定义的路由表生成的,要实现动态菜单显示,其实只要实现动态路由即可。 ### Vuex Vuex是一个专为Vue.js应用程序开发的状态管理模式,它采用集中式存储管理应用的所有组件的状态。Vuex可以简单理解为一个全局的状态管理器,我们可以把一些全局的状态存储在里面。当我们在多个组件中显示这些状态时,只要在任意一个组件中改变这个状态,基于Vue的响应式渲染,其余组件中的这个状态均会改变。 Vuex中有几个核心概念需要了解下: - Store:相当于一个容器,它包含着应用中大部分的状态; - State:Store中存储的状态,由于使用了单一状态树,即Vuex中存储的状态只存在一份,当这个状态发生改变时,和它绑定的组件中的这个状态均会发生改变; - Getter:从State中派生出的一些状态,可以认为是State的计算属性; - Mutation:状态的变化,更改Vuex中的State的唯一方法是提交Mutation; - Action:用于提交Mutation的动作,从而更改Vuex中的State; - Module:Store中的模块,由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。为了解决以上问题,Vuex允许我们将Store分割成模块。 Vuex中的核心流程如下: ![](../images/mall_permission_front_03.png) ## 菜单的动态权限控制 > 接下来我们来讲下如何结合Vue Router和Vuex来实现菜单的动态权限控制。 首先我们需要修改`src/router/index.js`中的路由表,将路由表进行拆分,拆分成必须要显示的静态路由表和可以动态显示的动态路由表。 ![](../images/mall_permission_front_04.png) 然后我们需要添加`src/store/modules/permission.js`文件,在Vuex的Store中添加权限相关状态,比如和左侧菜单绑定的路由表。 ![](../images/mall_permission_front_05.png) 这里有个比较核心的GenerateRoutes方法,用于生成当前用户可以访问的路由。我们的data参数中包含了用户可以访问的菜单信息。它的具体执行流程如下:从菜单信息中筛选出可以访问的动态路由,然后进行排序,最后提交状态改变到Vuex中去改变routers这个状态。 ![](../images/mall_permission_front_06.png) 关于前端路由和后台菜单的匹配,其实是根据路由名称和菜单的前端名称来确定的,比如商品列表中的路由名称和`ums_menu`表中存储的前端名称如下。 ![](../images/mall_permission_front_07.png) ![](../images/mall_permission_front_08.png) 接下来我们需要修改`src/store/index.js`文件,在Vuex的Store中添加这个权限模块的状态。 ![](../images/mall_permission_front_09.png) 再修改`src/store/getters.js`文件,给权限模块中的两个状态取个别名方便访问。 ![](../images/mall_permission_front_10.png) 我们还需要修改`src/views/layout/components/Sidebar/index.vue`文件,将左侧菜单组件和Vuex中存储的路由状态进行绑定,这样当我们修改了Vuex中的状态后,菜单就会改变了。`mapGetters`是个辅助函数,可以将Store中的Getter属性映射到局部计算属性。 ![](../images/mall_permission_front_11.png) 最后我们需要在用户登录成功后,通过`store.dispatch('GenerateRoutes', { menus,username })`来修改Vuex中存储的路由状态并传入用户可以访问的菜单信息。 ![](../images/mall_permission_front_12.png) ## 权限管理功能演示 具体参考:[大家心心念念的权限管理功能,这次安排上了!](https://mp.weixin.qq.com/s/3TNrPNmxHpFTcAhfjnuP0g) ## 项目源码地址 [https://github.com/macrozheng/mall-admin-web](https://github.com/macrozheng/mall-admin-web) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/product_search.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # Elasticsearch项目实战,商品搜索功能设计与实现! > 上次写了一篇[《Elasticsearch快速入门,掌握这些刚刚好!》](https://mp.weixin.qq.com/s/cohWZy_eUOUqbmUxhXzzNA),带大家学习了下Elasticsearch的基本用法,这次我们来篇实战教程,以`mall`项目中的商品搜索为例,把Elasticsearch用起来! ## 中文分词器 > 由于商品搜索会涉及中文搜索,Elasticsearch需要安装插件才可以支持,我们先来了解下中文分词器,这里使用的是IKAnalyzer。在[《Elasticsearch快速入门,掌握这些刚刚好!》](https://mp.weixin.qq.com/s/cohWZy_eUOUqbmUxhXzzNA)中已经讲过其安装方式,这里直接讲解它的用法。 ### 使用IKAnalyzer - 使用默认分词器,可以发现默认分词器只是将中文逐词分隔,并不符合我们的需求; ``` GET /pms/_analyze { "text": "小米手机性价比很高", "tokenizer": "standard" } ``` ![](../images/product_search_01.png) - 使用中文分词器以后,可以将中文文本按语境进行分隔,可以满足我们的需求。 ``` GET /pms/_analyze { "text": "小米手机性价比很高", "tokenizer": "ik_max_word" } ``` ![](../images/product_search_02.png) ### 在SpringBoot中使用 在SpringBoot中使用Elasticsearch本文不再赘述,直接参考[《mall整合Elasticsearch实现商品搜索》](https://mp.weixin.qq.com/s/op7CTQS5dGOKv5BvRbeFow)即可。这里需要提一下,对于需要进行中文分词的字段,我们直接使用@Field注解将analyzer属性设置为`ik_max_word`即可。 ```java /** * 搜索中的商品信息 * 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(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; //省略若干代码...... } ``` ## 简单商品搜索 > 我们先来实现一个最简单的商品搜索,搜索商品名称、副标题、关键词中包含指定关键字的商品。 - 使用Query DSL调用Elasticsearch的Restful API实现; ``` POST /pms/product/_search { "from": 0, "size": 2, "query": { "multi_match": { "query": "小米", "fields": [ "name", "subTitle", "keywords" ] } } } ``` ![](../images/product_search_03.png) - 在SpringBoot中实现,使用Elasticsearch Repositories的衍生查询来搜索; ```java /** * 商品搜索管理Service实现类 * Created by macro on 2018/6/19. */ @Service public class EsProductServiceImpl implements EsProductService { @Override public Page search(String keyword, Integer pageNum, Integer pageSize) { Pageable pageable = PageRequest.of(pageNum, pageSize); return productRepository.findByNameOrSubTitleOrKeywords(keyword, keyword, keyword, pageable); } } ``` - 衍生查询其实原理很简单,就是将一定规则方法名称的方法转化为Elasticsearch的Query DSL语句,看完下面这张表你就懂了。 ![](../images/product_search_04.png) ## 综合商品搜索 > 接下来我们来实现一个复杂的商品搜索,涉及到过滤、不同字段匹配权重不同以及可以进行排序。 - 首先来说下我们的需求,按输入的关键字搜索商品名称、副标题和关键词,可以按品牌和分类进行筛选,可以有5种排序方式,默认按相关度进行排序,看下接口文档有助于理解; ![](../images/product_search_05.png) - 这里我们有一点特殊的需求,比如商品名称匹配关键字的的商品我们认为与搜索条件更匹配,其次是副标题和关键字,这时就需要用到`function_score`查询了; - 在Elasticsearch中搜索到文档的相关性由`_score`字段来表示的,文档的`_score`字段值越高,表示与搜索条件越匹配,而`function_score`查询可以通过设置权重来影响`_score`字段值,使用它我们就可以实现上面的需求了; - 使用Query DSL调用Elasticsearch的Restful API实现,可以发现商品名称权重设置为了10,商品副标题权重设置为了5,商品关键字设置为了2; ``` POST /pms/product/_search { "query": { "function_score": { "query": { "bool": { "must": [ { "match_all": {} } ], "filter": { "bool": { "must": [ { "term": { "brandId": 6 } }, { "term": { "productCategoryId": 19 } } ] } } } }, "functions": [ { "filter": { "match": { "name": "小米" } }, "weight": 10 }, { "filter": { "match": { "subTitle": "小米" } }, "weight": 5 }, { "filter": { "match": { "keywords": "小米" } }, "weight": 2 } ], "score_mode": "sum", "min_score": 2 } }, "sort": [ { "_score": { "order": "desc" } } ] } ``` ![](../images/product_search_06.png) - 在SpringBoot中实现,使用Elasticsearch Repositories的search方法来实现,但需要自定义查询条件QueryBuilder; ```java /** * 商品搜索管理Service实现类 * Created by macro on 2018/6/19. */ @Service public class EsProductServiceImpl implements EsProductService { @Override public Page search(String keyword, Long brandId, Long productCategoryId, Integer pageNum, Integer pageSize,Integer sort) { Pageable pageable = PageRequest.of(pageNum, pageSize); NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder(); //分页 nativeSearchQueryBuilder.withPageable(pageable); //过滤 if (brandId != null || productCategoryId != null) { BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); if (brandId != null) { boolQueryBuilder.must(QueryBuilders.termQuery("brandId", brandId)); } if (productCategoryId != null) { boolQueryBuilder.must(QueryBuilders.termQuery("productCategoryId", productCategoryId)); } nativeSearchQueryBuilder.withFilter(boolQueryBuilder); } //搜索 if (StringUtils.isEmpty(keyword)) { nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery()); } else { List filterFunctionBuilders = new ArrayList<>(); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("name", keyword), ScoreFunctionBuilders.weightFactorFunction(10))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("subTitle", keyword), ScoreFunctionBuilders.weightFactorFunction(5))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("keywords", keyword), ScoreFunctionBuilders.weightFactorFunction(2))); FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()]; filterFunctionBuilders.toArray(builders); FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders) .scoreMode(FunctionScoreQuery.ScoreMode.SUM) .setMinScore(2); nativeSearchQueryBuilder.withQuery(functionScoreQueryBuilder); } //排序 if(sort==1){ //按新品从新到旧 nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC)); }else if(sort==2){ //按销量从高到低 nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("sale").order(SortOrder.DESC)); }else if(sort==3){ //按价格从低到高 nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC)); }else if(sort==4){ //按价格从高到低 nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC)); }else{ //按相关度 nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC)); } nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC)); NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build(); LOGGER.info("DSL:{}", searchQuery.getQuery().toString()); return productRepository.search(searchQuery); } } ``` ## 相关商品推荐 > 当我们查看相关商品的时候,一般底部会有一些商品推荐,这里使用Elasticsearch来简单实现下。 - 首先来说下我们的需求,可以根据指定商品的ID来查找相关商品,看下接口文档有助于理解; ![](../images/product_search_07.png) - 这里我们的实现原理是这样的:首先根据ID获取指定商品信息,然后以指定商品的名称、品牌和分类来搜索商品,并且要过滤掉当前商品,调整搜索条件中的权重以获取最好的匹配度; - 使用Query DSL调用Elasticsearch的Restful API实现; ``` POST /pms/product/_search { "query": { "function_score": { "query": { "bool": { "must": [ { "match_all": {} } ], "filter": { "bool": { "must_not": { "term": { "id": 28 } } } } } }, "functions": [ { "filter": { "match": { "name": "红米5A" } }, "weight": 8 }, { "filter": { "match": { "subTitle": "红米5A" } }, "weight": 2 }, { "filter": { "match": { "keywords": "红米5A" } }, "weight": 2 }, { "filter": { "term": { "brandId": 6 } }, "weight": 5 }, { "filter": { "term": { "productCategoryId": 19 } }, "weight": 3 } ], "score_mode": "sum", "min_score": 2 } } } ``` ![](../images/product_search_08.png) - 在SpringBoot中实现,使用Elasticsearch Repositories的search方法来实现,但需要自定义查询条件QueryBuilder; ```java /** * 商品搜索管理Service实现类 * Created by macro on 2018/6/19. */ @Service public class EsProductServiceImpl implements EsProductService { @Override public Page recommend(Long id, Integer pageNum, Integer pageSize) { Pageable pageable = PageRequest.of(pageNum, pageSize); List esProductList = productDao.getAllEsProductList(id); if (esProductList.size() > 0) { EsProduct esProduct = esProductList.get(0); String keyword = esProduct.getName(); Long brandId = esProduct.getBrandId(); Long productCategoryId = esProduct.getProductCategoryId(); //根据商品标题、品牌、分类进行搜索 List filterFunctionBuilders = new ArrayList<>(); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("name", keyword), ScoreFunctionBuilders.weightFactorFunction(8))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("subTitle", keyword), ScoreFunctionBuilders.weightFactorFunction(2))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("keywords", keyword), ScoreFunctionBuilders.weightFactorFunction(2))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("brandId", brandId), ScoreFunctionBuilders.weightFactorFunction(5))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("productCategoryId", productCategoryId), ScoreFunctionBuilders.weightFactorFunction(3))); FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()]; filterFunctionBuilders.toArray(builders); FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders) .scoreMode(FunctionScoreQuery.ScoreMode.SUM) .setMinScore(2); //用于过滤掉相同的商品 BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder(); boolQueryBuilder.mustNot(QueryBuilders.termQuery("id",id)); //构建查询条件 NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder(); builder.withQuery(functionScoreQueryBuilder); builder.withFilter(boolQueryBuilder); builder.withPageable(pageable); NativeSearchQuery searchQuery = builder.build(); LOGGER.info("DSL:{}", searchQuery.getQuery().toString()); return productRepository.search(searchQuery); } return new PageImpl<>(null); } } ``` ## 聚合搜索商品相关信息 > 在搜索商品时,经常会有一个筛选界面来帮助我们找到想要的商品,这里使用Elasticsearch来简单实现下。 - 首先来说下我们的需求,可以根据搜索关键字获取到与关键字匹配商品相关的分类、品牌以及属性,下面这张图有助于理解; ![](../images/product_search_09.png) - 这里我们可以使用Elasticsearch的聚合来实现,搜索出相关商品,聚合出商品的品牌、商品的分类以及商品的属性,只要出现次数最多的前十个即可; - 使用Query DSL调用Elasticsearch的Restful API实现; ``` POST /pms/product/_search { "query": { "multi_match": { "query": "小米", "fields": [ "name", "subTitle", "keywords" ] } }, "size": 0, "aggs": { "brandNames": { "terms": { "field": "brandName", "size": 10 } }, "productCategoryNames": { "terms": { "field": "productCategoryName", "size": 10 } }, "allAttrValues": { "nested": { "path": "attrValueList" }, "aggs": { "productAttrs": { "filter": { "term": { "attrValueList.type": 1 } }, "aggs": { "attrIds": { "terms": { "field": "attrValueList.productAttributeId", "size": 10 }, "aggs": { "attrValues": { "terms": { "field": "attrValueList.value", "size": 10 } }, "attrNames": { "terms": { "field": "attrValueList.name", "size": 10 } } } } } } } } } } ``` - 比如我们搜索`小米`这个关键字的时候,聚合出了下面的分类和品牌信息; ![](../images/product_search_10.png) - 聚合出了`屏幕尺寸`为`5.0`和`5.8`的筛选属性信息; ![](../images/product_search_11.png) - 在SpringBoot中实现,聚合操作比较复杂,已经超出了Elasticsearch Repositories的使用范围,需要直接使用ElasticsearchTemplate来实现; ```java /** * 商品搜索管理Service实现类 * Created by macro on 2018/6/19. */ @Service public class EsProductServiceImpl implements EsProductService { @Override public EsProductRelatedInfo searchRelatedInfo(String keyword) { NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder(); //搜索条件 if(StringUtils.isEmpty(keyword)){ builder.withQuery(QueryBuilders.matchAllQuery()); }else{ builder.withQuery(QueryBuilders.multiMatchQuery(keyword,"name","subTitle","keywords")); } //聚合搜索品牌名称 builder.addAggregation(AggregationBuilders.terms("brandNames").field("brandName")); //集合搜索分类名称 builder.addAggregation(AggregationBuilders.terms("productCategoryNames").field("productCategoryName")); //聚合搜索商品属性,去除type=1的属性 AbstractAggregationBuilder aggregationBuilder = AggregationBuilders.nested("allAttrValues","attrValueList") .subAggregation(AggregationBuilders.filter("productAttrs",QueryBuilders.termQuery("attrValueList.type",1)) .subAggregation(AggregationBuilders.terms("attrIds") .field("attrValueList.productAttributeId") .subAggregation(AggregationBuilders.terms("attrValues") .field("attrValueList.value")) .subAggregation(AggregationBuilders.terms("attrNames") .field("attrValueList.name")))); builder.addAggregation(aggregationBuilder); NativeSearchQuery searchQuery = builder.build(); return elasticsearchTemplate.query(searchQuery, response -> { LOGGER.info("DSL:{}",searchQuery.getQuery().toString()); return convertProductRelatedInfo(response); }); } /** * 将返回结果转换为对象 */ private EsProductRelatedInfo convertProductRelatedInfo(SearchResponse response) { EsProductRelatedInfo productRelatedInfo = new EsProductRelatedInfo(); Map aggregationMap = response.getAggregations().getAsMap(); //设置品牌 Aggregation brandNames = aggregationMap.get("brandNames"); List brandNameList = new ArrayList<>(); for(int i = 0; i<((Terms) brandNames).getBuckets().size(); i++){ brandNameList.add(((Terms) brandNames).getBuckets().get(i).getKeyAsString()); } productRelatedInfo.setBrandNames(brandNameList); //设置分类 Aggregation productCategoryNames = aggregationMap.get("productCategoryNames"); List productCategoryNameList = new ArrayList<>(); for(int i=0;i<((Terms) productCategoryNames).getBuckets().size();i++){ productCategoryNameList.add(((Terms) productCategoryNames).getBuckets().get(i).getKeyAsString()); } productRelatedInfo.setProductCategoryNames(productCategoryNameList); //设置参数 Aggregation productAttrs = aggregationMap.get("allAttrValues"); List attrIds = ((LongTerms) ((InternalFilter) ((InternalNested) productAttrs).getProperty("productAttrs")).getProperty("attrIds")).getBuckets(); List attrList = new ArrayList<>(); for (Terms.Bucket attrId : attrIds) { EsProductRelatedInfo.ProductAttr attr = new EsProductRelatedInfo.ProductAttr(); attr.setAttrId((Long) attrId.getKey()); List attrValueList = new ArrayList<>(); List attrValues = ((StringTerms) attrId.getAggregations().get("attrValues")).getBuckets(); List attrNames = ((StringTerms) attrId.getAggregations().get("attrNames")).getBuckets(); for (Terms.Bucket attrValue : attrValues) { attrValueList.add(attrValue.getKeyAsString()); } attr.setAttrValues(attrValueList); if(!CollectionUtils.isEmpty(attrNames)){ String attrName = attrNames.get(0).getKeyAsString(); attr.setAttrName(attrName); } attrList.add(attr); } productRelatedInfo.setProductAttrs(attrList); return productRelatedInfo; } } ``` ## 参考资料 > 关于Spring Data Elasticsearch的具体使用可以参考官方文档。 https://docs.spring.io/spring-data/elasticsearch/docs/3.2.6.RELEASE/reference/html/#reference ## 项目地址 [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/product_sku.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 商品SKU功能设计与优化 > 原来的商品SKU设计存在着两个问题,一个是SKU表设计上面比较固化,无法扩展。另一个是当修改了商品信息之后,商品SKU的ID会发生变化,由于购物车表和订单商品表都关联了商品SKU的ID,这样就会导致匹配不上。最近对这两个问题做了点优化,下面来聊聊优化的思路。 ## 商品的SPU和SKU > 首先我们来了解下商品SPU和SKU的概念,可能很多没有接触过电商的朋友都不了解。 - SPU(Standard Product Unit ):指的是标准商品单位,商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个商品的特性; - SKU(Stock Keeping Unit):库存量单位,是物理上不可分割的最小存货单元。 举个例子:比如说现在有个手机商品叫小米8,小米8有不同的属性,比如有它有黑色和蓝色的,有32G和64G版本的。此时`小米8`就是一个SPU,而`小米8黑色64G`就是一个SKU。 ![](../images/product_sku_01.png) ## 商品的SKU设计 ### 以前的设计 商品的SKU信息是存储在`pms_sku_stock`表中的,使用sp1、sp2、sp3这三个属性来存储商品的销售属性,这样做很不灵活,也难以扩展。 ![](../images/product_sku_02.png) 这种做法也带来了后续的问题,比如我们的购物车和订单都会需要存储销售属性,这样的话都会需要添加sp1、sp2、sp3的属性。 ![](../images/product_sku_03.png) ![](../images/product_sku_04.png) ### 改进后的设计 由于商品的销售属性是动态的,没法确定到底有多少个,此时我们可以改用JSON格式来存储,在`pms_sku_stock`表中添加了`sp_data`字段。 ![](../images/product_sku_05.png) `sp_data`存储的就是一个JSON数组,比如颜色为黑色,容量为32G的手机存储信息如下。 ```json [ { "key": "颜色", "value": "黑色" }, { "key": "容量", "value": "32G" } ] ``` 这样修改以后,在原来的购物车表`oms_cart_item`和订单商品表`oms_order_item`中就都可以用JSON格式来存储销售属性了,使用的是`product_attr`字段。 ## 商品关联SKU的修改 ### 以前的做法 商品的SKU信息作为商品的关联信息,在修改商品信息时会同时进行修改。以前的做法是直接删除该商品的所有SKU信息,再重新添加。这样就会导致商品SKU中的ID被修改,由于在购物车和订单商品中关联了商品SKU的ID,就会导致原来的ID失效的问题。下面是原来修改商品中SKU信息的代码。 ```java /** * 商品管理Service实现类 * Created by macro on 2018/4/26. */ @Service public class PmsProductServiceImpl implements PmsProductService { @Override public int update(Long id, PmsProductParam productParam) { //省略若干代码... //删除该商品关联的SKU PmsSkuStockExample skuStockExample = new PmsSkuStockExample(); skuStockExample.createCriteria().andProductIdEqualTo(id); skuStockMapper.deleteByExample(skuStockExample); handleSkuStockCode(productParam.getSkuStockList(),id); //插入传入的所有SKU relateAndInsertList(skuStockDao, productParam.getSkuStockList(), id); } } ``` ### 改进后的做法 首先我们需要和前端约定下,新增的商品SKU信息不传ID,要修改的商品SKU信息传ID,删除的直接不传SKU信息。然后我们可以根据传入的SKU信息来确定需要新增、修改、删除的SKU信息,这样就可以做到在更新商品SKU信息时,不改变原来商品SKU的ID了,具体流程如下。 ![](../images/product_sku_06.png) 具体代码实现如下: ```java /** * 商品管理Service实现类 * Created by macro on 2018/4/26. */ @Service public class PmsProductServiceImpl implements PmsProductService { private void handleUpdateSkuStockList(Long id, PmsProductParam productParam) { //当前的sku信息 List currSkuList = productParam.getSkuStockList(); //当前没有sku直接删除 if(CollUtil.isEmpty(currSkuList)){ PmsSkuStockExample skuStockExample = new PmsSkuStockExample(); skuStockExample.createCriteria().andProductIdEqualTo(id); skuStockMapper.deleteByExample(skuStockExample); return; } //获取初始sku信息 PmsSkuStockExample skuStockExample = new PmsSkuStockExample(); skuStockExample.createCriteria().andProductIdEqualTo(id); List oriStuList = skuStockMapper.selectByExample(skuStockExample); //获取新增sku信息 List insertSkuList = currSkuList.stream().filter(item->item.getId()==null).collect(Collectors.toList()); //获取需要更新的sku信息 List updateSkuList = currSkuList.stream().filter(item->item.getId()!=null).collect(Collectors.toList()); List updateSkuIds = updateSkuList.stream().map(PmsSkuStock::getId).collect(Collectors.toList()); //获取需要删除的sku信息 List removeSkuList = oriStuList.stream().filter(item-> !updateSkuIds.contains(item.getId())).collect(Collectors.toList()); handleSkuStockCode(insertSkuList,id); handleSkuStockCode(updateSkuList,id); //新增sku if(CollUtil.isNotEmpty(insertSkuList)){ relateAndInsertList(skuStockDao, insertSkuList, id); } //删除sku if(CollUtil.isNotEmpty(removeSkuList)){ List removeSkuIds = removeSkuList.stream().map(PmsSkuStock::getId).collect(Collectors.toList()); PmsSkuStockExample removeExample = new PmsSkuStockExample(); removeExample.createCriteria().andIdIn(removeSkuIds); skuStockMapper.deleteByExample(removeExample); } //修改sku if(CollUtil.isNotEmpty(updateSkuList)){ for (PmsSkuStock pmsSkuStock : updateSkuList) { skuStockMapper.updateByPrimaryKeySelective(pmsSkuStock); } } } } ``` ## 总结 如果我们要在数据库中存储一些格式不固定的属性时,可以采用JSON的形式进行存储。对于关联属性的修改,可以通过一些逻辑操作来实现不改变原有ID的修改。 ## 项目源码地址 [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/rabbitmq_delay.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # RabbitMQ实现延迟消息居然如此简单,整个插件就完事了! > RabbitMQ实现延迟消息的方式有两种,一种是使用`死信队列`实现,另一种是使用`延迟插件`实现。`死信队列`实现我们以前曾经讲过,具体参考[《mall整合RabbitMQ实现延迟消息》](https://mp.weixin.qq.com/s/Rp4TfejQkYN00oQ-kMCRcg),这次我们讲个更简单的,使用`延迟插件`实现。 ## 学前准备 学习本文需要对RabbitMQ有所了解,还不了解的朋友可以看下:[《花了3天总结的RabbitMQ实用技巧,有点东西!》](https://mp.weixin.qq.com/s/qGg3etLnI38i-G8aFbulWw) ## 插件安装 > 首先我们需要下载并安装RabbitMQ的延迟插件。 - 去RabbitMQ的官网下载插件,插件地址:https://www.rabbitmq.com/community-plugins.html - 直接搜索`rabbitmq_delayed_message_exchange`即可找到我们需要下载的插件,下载和RabbitMQ配套的版本,不要弄错; ![](../images/rabbitmq_delay_01.png) - 将插件文件复制到RabbitMQ安装目录的`plugins`目录下; ![](../images/rabbitmq_delay_02.png) - 进入RabbitMQ安装目录的`sbin`目录下,使用如下命令启用延迟插件; ```bash rabbitmq-plugins enable rabbitmq_delayed_message_exchange ``` - 启用插件成功后就可以看到如下信息,之后重新启动RabbitMQ服务即可。 ![](../images/rabbitmq_delay_03.png) ## 实现延迟消息 > 接下来我们需要在SpringBoot中实现延迟消息功能,这次依然沿用商品下单的场景。比如说有个用户下单了,他60分钟不支付订单,订单就会被取消,这就是一个典型的延迟消息使用场景。 - 首先我们需要在`pom.xml`文件中添加`AMQP`相关依赖; ```xml org.springframework.boot spring-boot-starter-amqp ``` - 之后在`application.yml`添加RabbitMQ的相关配置; ```yaml spring: rabbitmq: host: localhost # rabbitmq的连接地址 port: 5672 # rabbitmq的连接端口号 virtual-host: /mall # rabbitmq的虚拟host username: mall # rabbitmq的用户名 password: mall # rabbitmq的密码 publisher-confirms: true #如果对异步消息需要回调必须设置为true ``` - 接下来创建RabbitMQ的Java配置,主要用于配置交换机、队列和绑定关系; ```java /** * 消息队列配置 * Created by macro on 2018/9/14. */ @Configuration public class RabbitMqConfig { /** * 订单延迟插件消息队列所绑定的交换机 */ @Bean CustomExchange orderPluginDirect() { //创建一个自定义交换机,可以发送延迟消息 Map args = new HashMap<>(); args.put("x-delayed-type", "direct"); return new CustomExchange(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getExchange(), "x-delayed-message",true, false,args); } /** * 订单延迟插件队列 */ @Bean public Queue orderPluginQueue() { return new Queue(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getName()); } /** * 将订单延迟插件队列绑定到交换机 */ @Bean public Binding orderPluginBinding(CustomExchange orderPluginDirect,Queue orderPluginQueue) { return BindingBuilder .bind(orderPluginQueue) .to(orderPluginDirect) .with(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getRouteKey()) .noargs(); } } ``` - 创建一个取消订单消息的发出者,通过给消息设置`x-delay`头来设置消息从交换机发送到队列的延迟时间; ```java /** * 取消订单消息的发出者 * Created by macro on 2018/9/14. */ @Component public class CancelOrderSender { private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderSender.class); @Autowired private AmqpTemplate amqpTemplate; public void sendMessage(Long orderId,final long delayTimes){ //给延迟队列发送消息 amqpTemplate.convertAndSend(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getExchange(), QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getRouteKey(), orderId, new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { //给消息设置延迟毫秒值 message.getMessageProperties().setHeader("x-delay",delayTimes); return message; } }); LOGGER.info("send delay message orderId:{}",orderId); } } ``` - 创建一个取消订单消息的接收者,用于处理订单延迟插件队列中的消息。 ```java /** * 取消订单消息的处理者 * Created by macro on 2018/9/14. */ @Component @RabbitListener(queues = "mall.order.cancel.plugin") public class CancelOrderReceiver { private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderReceiver.class); @Autowired private OmsPortalOrderService portalOrderService; @RabbitHandler public void handle(Long orderId){ LOGGER.info("receive delay message orderId:{}",orderId); portalOrderService.cancelOrder(orderId); } } ``` - 然后在我们的订单业务实现类中添加如下逻辑,当下单成功之前,往消息队列中发送一个取消订单的延迟消息,这样如果订单没有被支付的话,就能取消订单了; ````java /** * 前台订单管理Service * Created by macro on 2018/8/30. */ @Service public class OmsPortalOrderServiceImpl implements OmsPortalOrderService { private static Logger LOGGER = LoggerFactory.getLogger(OmsPortalOrderServiceImpl.class); @Autowired private CancelOrderSender cancelOrderSender; @Override public CommonResult generateOrder(OrderParam orderParam) { //todo 执行一系类下单操作,具体参考mall项目 LOGGER.info("process generateOrder"); //下单完成后开启一个延迟消息,用于当用户没有付款时取消订单(orderId应该在下单后生成) sendDelayMessageCancelOrder(11L); return CommonResult.success(null, "下单成功"); } @Override public void cancelOrder(Long orderId) { //todo 执行一系类取消订单操作,具体参考mall项目 LOGGER.info("process cancelOrder orderId:{}",orderId); } private void sendDelayMessageCancelOrder(Long orderId) { //获取订单超时时间,假设为60分钟(测试用的30秒) long delayTimes = 30 * 1000; //发送延迟消息 cancelOrderSender.sendMessage(orderId, delayTimes); } } ```` - 启动项目后,在Swagger中调用下单接口; ![](../images/rabbitmq_delay_04.png) - 调用完成后查看控制台日志可以发现,从消息发送和消息接收处理正好相差了`30s`,我们设置的延迟时间。 ```bash 2020-06-08 13:46:01.474 INFO 1644 --- [nio-8080-exec-1] c.m.m.t.s.i.OmsPortalOrderServiceImpl : process generateOrder 2020-06-08 13:46:01.482 INFO 1644 --- [nio-8080-exec-1] c.m.m.tiny.component.CancelOrderSender : send delay message orderId:11 2020-06-08 13:46:31.517 INFO 1644 --- [cTaskExecutor-4] c.m.m.t.component.CancelOrderReceiver : receive delay message orderId:11 2020-06-08 13:46:31.520 INFO 1644 --- [cTaskExecutor-4] c.m.m.t.s.i.OmsPortalOrderServiceImpl : process cancelOrder orderId:11 ``` ## 两种实现方式对比 > 我们之前使用过死信队列的方式,这里我们把两种方式做个对比,先来聊下这两种方式的实现原理。 ### 死信队列 死信队列是这样一个队列,如果消息发送到该队列并超过了设置的时间,就会被转发到设置好的处理超时消息的队列当中去,利用该特性可以实现延迟消息。 ### 延迟插件 通过安装插件,自定义交换机,让交换机拥有延迟发送消息的能力,从而实现延迟消息。 ### 结论 由于死信队列方式需要创建两个交换机(死信队列交换机+处理队列交换机)、两个队列(死信队列+处理队列),而延迟插件方式只需创建一个交换机和一个队列,所以后者使用起来更简单。 ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-delay ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/redis_permission.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 使用Redis+AOP优化权限管理功能,这波操作贼爽! > 之前有很多朋友提过,mall项目中的权限管理功能有性能问题,因为每次访问接口进行权限校验时都会从数据库中去查询用户信息。最近对这个问题进行了优化,通过Redis+AOP解决了该问题,下面来讲下我的优化思路。 ## 前置知识 学习本文需要一些Spring Data Redis的知识,不了解的朋友可以看下[《Spring Data Redis 最佳实践!》](https://mp.weixin.qq.com/s/9j3exBtZ9FWWlyZkxlWaOA)。 还需要一些Spring AOP的知识,不了解的朋友可以看下[《SpringBoot应用中使用AOP记录接口访问日志》](https://mp.weixin.qq.com/s/mNujRjejQ1bITveFI6gkcg)。 ## 问题重现 在`mall-security`模块中有一个过滤器,当用户登录后,请求会带着token经过这个过滤器。这个过滤器会根据用户携带的token进行类似免密登录的操作,其中有一步会从数据库中查询登录用户信息,下面是这个过滤器类的代码。 ```java /** * 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); } } ``` 当我们登录后访问任意接口时,控制台会打印如下日志,表示会从数据库中查询用户信息和用户所拥有的资源信息,每次访问接口都触发这种操作,有的时候会带来一定的性能问题。 ```bash 2020-03-17 16:13:02.623 DEBUG 4544 --- [nio-8081-exec-2] c.m.m.m.UmsAdminMapper.selectByExample : ==> Preparing: select id, username, password, icon, email, nick_name, note, create_time, login_time, status from ums_admin WHERE ( username = ? ) 2020-03-17 16:13:02.624 DEBUG 4544 --- [nio-8081-exec-2] c.m.m.m.UmsAdminMapper.selectByExample : ==> Parameters: admin(String) 2020-03-17 16:13:02.625 DEBUG 4544 --- [nio-8081-exec-2] c.m.m.m.UmsAdminMapper.selectByExample : <== Total: 1 2020-03-17 16:13:02.628 DEBUG 4544 --- [nio-8081-exec-2] c.macro.mall.dao.UmsRoleDao.getMenuList : ==> Preparing: SELECT m.id id, m.parent_id parentId, m.create_time createTime, m.title title, m.level level, m.sort sort, m.name name, m.icon icon, m.hidden hidden FROM ums_admin_role_relation arr LEFT JOIN ums_role r ON arr.role_id = r.id LEFT JOIN ums_role_menu_relation rmr ON r.id = rmr.role_id LEFT JOIN ums_menu m ON rmr.menu_id = m.id WHERE arr.admin_id = ? AND m.id IS NOT NULL GROUP BY m.id 2020-03-17 16:13:02.628 DEBUG 4544 --- [nio-8081-exec-2] c.macro.mall.dao.UmsRoleDao.getMenuList : ==> Parameters: 3(Long) 2020-03-17 16:13:02.632 DEBUG 4544 --- [nio-8081-exec-2] c.macro.mall.dao.UmsRoleDao.getMenuList : <== Total: 24 ``` ## 使用Redis作为缓存 > 对于上面的问题,最容易想到的就是把用户信息和用户资源信息存入到Redis中去,避免频繁查询数据库,本文的优化思路大体也是这样的。 首先我们需要对Spring Security中获取用户信息的方法添加缓存,我们先来看下这个方法执行了哪些数据库查询操作。 ```java /** * UmsAdminService实现类 * Created by macro on 2018/4/26. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Override public UserDetails loadUserByUsername(String username){ //获取用户信息 UmsAdmin admin = getAdminByUsername(username); if (admin != null) { //获取用户的资源信息 List resourceList = getResourceList(admin.getId()); return new AdminUserDetails(admin,resourceList); } throw new UsernameNotFoundException("用户名或密码错误"); } } ``` 主要是获取用户信息和获取用户的资源信息这两个操作,接下来我们需要给这两个操作添加缓存操作,这里使用的是RedisTemple的操作方式。当查询数据时,先去Redis缓存中查询,如果Redis中没有,再从数据库查询,查询到以后在把数据存储到Redis中去。 ```java /** * UmsAdminService实现类 * Created by macro on 2018/4/26. */ @Service public class UmsAdminServiceImpl implements UmsAdminService { //专门用来操作Redis缓存的业务类 @Autowired private UmsAdminCacheService adminCacheService; @Override public UmsAdmin getAdminByUsername(String username) { //先从缓存中获取数据 UmsAdmin admin = adminCacheService.getAdmin(username); if(admin!=null) return admin; //缓存中没有从数据库中获取 UmsAdminExample example = new UmsAdminExample(); example.createCriteria().andUsernameEqualTo(username); List adminList = adminMapper.selectByExample(example); if (adminList != null && adminList.size() > 0) { admin = adminList.get(0); //将数据库中的数据存入缓存中 adminCacheService.setAdmin(admin); return admin; } return null; } @Override public List getResourceList(Long adminId) { //先从缓存中获取数据 List resourceList = adminCacheService.getResourceList(adminId); if(CollUtil.isNotEmpty(resourceList)){ return resourceList; } //缓存中没有从数据库中获取 resourceList = adminRoleRelationDao.getResourceList(adminId); if(CollUtil.isNotEmpty(resourceList)){ //将数据库中的数据存入缓存中 adminCacheService.setResourceList(adminId,resourceList); } return resourceList; } } ``` 上面这种查询操作其实用Spring Cache来操作更简单,直接使用@Cacheable即可实现,为什么还要使用RedisTemplate来直接操作呢?因为作为缓存,我们所希望的是,如果Redis宕机了,我们的业务逻辑不会有影响,而使用Spring Cache来实现的话,当Redis宕机以后,用户的登录等种种操作就会都无法进行了。 由于我们把用户信息和用户资源信息都缓存到了Redis中,所以当我们修改用户信息和资源信息时都需要删除缓存中的数据,具体什么时候删除,查看缓存业务类的注释即可。 ```java /** * 后台用户缓存操作类 * Created by macro on 2020/3/13. */ public interface UmsAdminCacheService { /** * 删除后台用户缓存 */ void delAdmin(Long adminId); /** * 删除后台用户资源列表缓存 */ void delResourceList(Long adminId); /** * 当角色相关资源信息改变时删除相关后台用户缓存 */ void delResourceListByRole(Long roleId); /** * 当角色相关资源信息改变时删除相关后台用户缓存 */ void delResourceListByRoleIds(List roleIds); /** * 当资源信息改变时,删除资源项目后台用户缓存 */ void delResourceListByResource(Long resourceId); } ``` 经过上面的一系列优化之后,性能问题解决了。但是引入新的技术之后,新的问题也会产生,比如说当Redis宕机以后,我们直接就无法登录了,下面我们使用AOP来解决这个问题。 ## 使用AOP处理缓存操作异常 > 为什么要用AOP来解决这个问题呢?因为我们的缓存业务类`UmsAdminCacheService`已经写好了,要保证缓存业务类中的方法执行不影响正常的业务逻辑,就需要在所有方法中添加`try catch`逻辑。使用AOP,我们可以在一个地方写上`try catch`逻辑,然后应用到所有方法上去。试想下,我们如果又多了几个缓存业务类,只要配置下切面即可,这波操作多方便! 首先我们先定义一个切面,在相关缓存业务类上面应用,在它的环绕通知中直接处理掉异常,保障后续操作能执行。 ```java /** * Redis缓存切面,防止Redis宕机影响正常业务逻辑 * Created by macro on 2020/3/17. */ @Aspect @Component @Order(2) public class RedisCacheAspect { private static Logger LOGGER = LoggerFactory.getLogger(RedisCacheAspect.class); @Pointcut("execution(public * com.macro.mall.portal.service.*CacheService.*(..)) || execution(public * com.macro.mall.service.*CacheService.*(..))") public void cacheAspect() { } @Around("cacheAspect()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { Object result = null; try { result = joinPoint.proceed(); } catch (Throwable throwable) { LOGGER.error(throwable.getMessage()); } return result; } } ``` 这样处理之后,就算我们的Redis宕机了,我们的业务逻辑也能正常执行。 不过并不是所有的方法都需要处理异常的,比如我们的验证码存储,如果我们的Redis宕机了,我们的验证码存储接口需要的是报错,而不是返回执行成功。 对于上面这种需求我们可以通过自定义注解来完成,首先我们自定义一个`CacheException`注解,如果方法上面有这个注解,发生异常则直接抛出。 ```java /** * 自定义注解,有该注解的缓存方法会抛出异常 */ @Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CacheException { } ``` 之后需要改造下我们的切面类,对于有`@CacheException`注解的方法,如果发生异常直接抛出。 ```java /** * Redis缓存切面,防止Redis宕机影响正常业务逻辑 * Created by macro on 2020/3/17. */ @Aspect @Component @Order(2) public class RedisCacheAspect { private static Logger LOGGER = LoggerFactory.getLogger(RedisCacheAspect.class); @Pointcut("execution(public * com.macro.mall.portal.service.*CacheService.*(..)) || execution(public * com.macro.mall.service.*CacheService.*(..))") public void cacheAspect() { } @Around("cacheAspect()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); Object result = null; try { result = joinPoint.proceed(); } catch (Throwable throwable) { //有CacheException注解的方法需要抛出异常 if (method.isAnnotationPresent(CacheException.class)) { throw throwable; } else { LOGGER.error(throwable.getMessage()); } } return result; } } ``` 接下来我们需要把`@CacheException`注解应用到存储和获取验证码的方法上去,这里需要注意的是要应用在实现类上而不是接口上,因为`isAnnotationPresent`方法只能获取到当前方法上的注解,而不能获取到它实现接口方法上的注解。 ```java /** * UmsMemberCacheService实现类 * Created by macro on 2020/3/14. */ @Service public class UmsMemberCacheServiceImpl implements UmsMemberCacheService { @Autowired private RedisService redisService; @CacheException @Override public void setAuthCode(String telephone, String authCode) { String key = REDIS_DATABASE + ":" + REDIS_KEY_AUTH_CODE + ":" + telephone; redisService.set(key,authCode,REDIS_EXPIRE_AUTH_CODE); } @CacheException @Override public String getAuthCode(String telephone) { String key = REDIS_DATABASE + ":" + REDIS_KEY_AUTH_CODE + ":" + telephone; return (String) redisService.get(key); } } ``` ## 总结 对于影响性能的,频繁查询数据库的操作,我们可以通过Redis作为缓存来优化。缓存操作不该影响正常业务逻辑,我们可以使用AOP来统一处理缓存操作中的异常。 ## 项目源码地址 [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/springboot_auto_deploy.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 我常用的自动化部署技巧,贼好用,推荐给大家! > SpringBoot+Jenkins自动化部署技巧,远程部署同样适用,附通用自动化脚本!本文将从半自动化部署讲起,到自动化部署,讲解一套生产环境切实可用的自动化部署方案! ## 半自动化部署 > 之前写过的SpringBoot应用打包Docker镜像都是通过Maven插件来实现的,由于远程服务器需要开发2375端口,存在一定的安全隐患。这次介绍另一种方法,使用DockerFile+Jar+自动化脚本的形式来部署。由于需要一定的手动操作,我把它称之为半自动化部署。 ### 项目打包 - 这次我们不使用Docker的Maven插件来打包,先在`pom.xml`中注释掉它; ![](../images/sb_auto_deploy_01.png) - 然后使用Maven的package命令直接将应用打成Jar包; ![](../images/sb_auto_deploy_02.png) - 此时在`target`目录下就会生成一个Jar包,我们打包Docker镜像的时候会用到它。 ![](../images/sb_auto_deploy_03.png) ### DockerFile 主要是定义了如何将Jar包打包成Docker镜像,对DockerFile不了解的朋友可以看下[《使用Dockerfile为SpringBoot应用构建Docker镜像》](https://mp.weixin.qq.com/s/U_OcNMpLAJJum_s9jbZLGg),具体内容如下。 ```dockerfile # 该镜像需要依赖的基础镜像 FROM java:8 # 将当前目录下的jar包复制到docker容器的/目录下 ADD mall-tiny-jenkins-1.0-SNAPSHOT.jar /mall-tiny-jenkins-1.0-SNAPSHOT.jar # 声明服务运行在8088端口 EXPOSE 8088 # 指定docker容器启动时运行jar包 ENTRYPOINT ["java", "-jar","/mall-tiny-jenkins-1.0-SNAPSHOT.jar"] # 指定维护者的名字 MAINTAINER macro ``` ### 自动化脚本 可以作为通用脚本来使用的模板脚本,只需改变其中的一些参数即可,具体执行流程为:停止旧服务->删除旧容器->删除旧镜像->打包新镜像->运行新镜像。 ```bash #!/usr/bin/env bash # 定义应用组名 group_name='mall-tiny' # 定义应用名称 app_name='mall-tiny-jenkins' # 定义应用版本 app_version='1.0-SNAPSHOT' # 定义应用环境 profile_active='qa' echo '----copy jar----' docker stop ${app_name} echo '----stop container----' docker rm ${app_name} echo '----rm container----' docker rmi ${group_name}/${app_name}:${app_version} echo '----rm image----' # 打包编译docker镜像 docker build -t ${group_name}/${app_name}:${app_version} . echo '----build image----' docker run -p 8088:8088 --name ${app_name} \ --link mysql:db \ -e 'spring.profiles.active'=${profile_active} \ -e TZ="Asia/Shanghai" \ -v /etc/localtime:/etc/localtime \ -v /mydata/app/${app_name}/logs:/var/logs \ -d ${group_name}/${app_name}:${app_version} echo '----start container----' ``` 下面讲下自动化脚本里面值得注意的地方: - `group_name`、`app_name`、`app_version`可以用来定义打包镜像的属性; - `profile_active`可以让你的应用使用不同环境下的配置,比如使用`qa`可以启用测试环境的配置,使用`prod`可以启用生产环境配置,真正的一包多用; - `docker rmi`这步一定要有,如果不删除旧镜像,当新镜像打包的时候会产生`none`镜像; - `docker run`命令中的`-e TZ="Asia/Shanghai"`时区一定要设置,否则容器时间会和宿主机会相差8个小时。 ### 部署运行 - 直接上传我们的应用Jar包、DockerFile文件和自动化部署脚本到指定目录下; ![](../images/sb_auto_deploy_04.png) - 将自动化脚本修改为可执行; ```bash chmod +x run.sh ``` - 使用`./run.sh`命令直接运行脚本即可。 ![](../images/sb_auto_deploy_05.png) ## 结合Jenkins自动化部署 > 之前的打包、上传文件都是我们手动完成的,其实这些操作也可以让Jenkins来帮我们实现,有了Jenkins才算得上是真正的自动化部署! ### 学前准备 学习下面的内容需要对Jenkins有一定的了解,不了解的朋友可以看下:[《使用Jenkins一键打包部署SpringBoot应用,就是这么6!》](https://mp.weixin.qq.com/s/tQqvgSc9cHBtnqRQSbI4aw) ### Publish Over SSH 这里推荐安装这款Jenkins插件,它的主要作用是可以通过SSH在不同服务器之间传输文件和执行命令。比如说我们把Jenkins装在了测试服务器上,我们可以使用Jenkins在测试服务器上从Git仓库获取代码,然后打成Jar包。打包完成后我们可以通过这个插件将Jar包传输到正式服务器上去,然后执行正式服务器上的自动化脚本,从而实现正式服务器上的自动化部署。 - 首先我们可以在`系统管理->插件管理`中找到该插件,然后进行安装; ![](../images/sb_auto_deploy_06.png) - 然后在`系统管理->插件管理`中添加相应的SSH配置; ![](../images/sb_auto_deploy_07.png) - 配置完成后创建一个应用的构建任务,`源码管理`和`构建`中的Maven打包配置和之前的Jenkins教程中一样,只有最后一步不同,添加构建步骤为`通过SSH发送文件并执行命令`; ![](../images/sb_auto_deploy_08.png) - 配置好我们的`SSH Publisher`,主要是源文件路径和目标文件路径,以及需要执行的脚本; ![](../images/sb_auto_deploy_09.png) - 之后执行构建任务即可实现自动化部署了,此方法在两台不同服务器之间同样适用! ![](../images/sb_auto_deploy_10.png) ## 总结 从我写过的几篇自动化部署文章中,其实可以看出,Linux下的自动化部署主要是依靠一连串的Linux命令来实现的。Jenkins的自动化部署也是基于这些的,所以要学会自动化部署,Linux命令和Docker命令是必不可少的! ## 项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-jenkins ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/springboot_cors.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 前后端分离项目,如何解决跨域问题 > 跨域资源共享(CORS)是前后端分离项目很常见的问题,本文主要介绍当SpringBoot应用整合SpringSecurity以后如何解决该问题。 ## 什么是跨域问题 CORS全称Cross-Origin Resource Sharing,意为跨域资源共享。当一个资源去访问另一个不同域名或者同域名不同端口的资源时,就会发出跨域请求。如果此时另一个资源不允许其进行跨域资源访问,那么访问的那个资源就会遇到跨域问题。 ## 跨域问题演示及解决 > 我们使用mall项目的源代码来演示一下跨域问题。此时前端代码运行在8090端口上,后端代码运行在8080端口上,由于其域名都是localhost,但是前端和后端运行端口不一致,此时前端访问后端接口时,就会产生跨域问题。 ### 点击前端登录按钮 >此时发现调用登录接口时出现跨域问题。 ![](../images/tech_screen_35.png) ![](../images/tech_screen_28.png) ![](../images/tech_screen_29.png) ### 覆盖默认的CorsFilter来解决该问题 > 添加GlobalCorsConfig配置文件来允许跨域访问。 ```java package com.macro.mall.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; /** * 全局跨域配置 * Created by macro on 2019/7/27. */ @Configuration public class GlobalCorsConfig { /** * 允许跨域调用的过滤器 */ @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); //允许所有域名进行跨域调用 config.addAllowedOrigin("*"); //允许跨越发送cookie config.setAllowCredentials(true); //放行全部原始头信息 config.addAllowedHeader("*"); //允许所有请求方法跨域调用 config.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } } ``` ### 重新运行代码,点击登录按钮 >发现需要登录认证的/admin/info接口的OPTIONS请求无法通过认证,那是因为复杂的跨越请求需要先进行一次OPTIONS请求进行预检,我们的应用整合了SpringSecurity,对OPTIONS请求并没有放开登录认证。 ![](../images/tech_screen_30.png) ![](../images/tech_screen_31.png) ### 设置SpringSecurity允许OPTIONS请求访问 > 在SecurityConfig类的configure(HttpSecurity httpSecurity)方法中添加如下代码。 ```java .antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求 .permitAll() ``` ![](../images/tech_screen_32.png) ### 重新运行代码,点击登录按钮 > 发现已经可以正常访问。 ![](../images/tech_screen_33.png) ![](../images/tech_screen_34.png) ## 一次完整的跨域请求 ### 先发起一次OPTIONS请求进行预检 - 请求头信息: ```text Access-Control-Request-Headers: content-type Access-Control-Request-Method: POST Origin: http://localhost:8090 Referer: http://localhost:8090/ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 ``` - 响应头信息: ```text Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: content-type Access-Control-Allow-Methods: POST Access-Control-Allow-Origin: http://localhost:8090 Cache-Control: no-cache, no-store, max-age=0, must-revalidate Content-Length: 0 Date: Sat, 27 Jul 2019 13:40:32 GMT Expires: 0 Pragma: no-cache Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers X-Content-Type-Options: nosniff X-Frame-Options: DENY X-XSS-Protection: 1; mode=block ``` - 请求成功返回状态码为200 ### 发起真实的跨域请求 - 请求头信息: ```text Accept: application/json, text/plain, */* Content-Type: application/json;charset=UTF-8 Origin: http://localhost:8090 Referer: http://localhost:8090/ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 {username: "admin", password: "123456"} password: "123456" username: "admin" ``` - 响应头信息: ```text Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: http://localhost:8090 Cache-Control: no-cache, no-store, max-age=0, must-revalidate Content-Type: application/json;charset=UTF-8 Date: Sat, 27 Jul 2019 13:40:32 GMT Expires: 0 Pragma: no-cache Transfer-Encoding: chunked Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers X-Content-Type-Options: nosniff X-Frame-Options: DENY X-XSS-Protection: 1; mode=block ``` - 请求成功返回状态码为200 ## 项目源码地址 [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/springboot_validator.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # SpringBoot中处理校验逻辑的两种方式,真的很机智! > 平时在开发接口的时候,常常会需要对参数进行校验,这里提供两种处理校验逻辑的方式。一种是使用Hibernate Validator来处理,另一种是使用全局异常来处理,下面我们讲下这两种方式的用法。 ## Hibernate Validator > Hibernate Validator是SpringBoot内置的校验框架,只要集成了SpringBoot就自动集成了它,我们可以通过在对象上面使用它提供的注解来完成参数校验。 ### 常用注解 > 我们先来了解下常用的注解,对Hibernate Validator所提供的校验功能有个印象。 - @Null:被注释的属性必须为null; - @NotNull:被注释的属性不能为null; - @AssertTrue:被注释的属性必须为true; - @AssertFalse:被注释的属性必须为false; - @Min:被注释的属性必须大于等于其value值; - @Max:被注释的属性必须小于等于其value值; - @Size:被注释的属性必须在其min和max值之间; - @Pattern:被注释的属性必须符合其regexp所定义的正则表达式; - @NotBlank:被注释的字符串不能为空字符串; - @NotEmpty:被注释的属性不能为空; - @Email:被注释的属性必须符合邮箱格式。 ### 使用方式 > 接下来我们以添加品牌接口的参数校验为例来讲解下Hibernate Validator的使用方法,其中涉及到一些AOP的知识,不了解的朋友可以参考下[《SpringBoot应用中使用AOP记录接口访问日志》](https://mp.weixin.qq.com/s/mNujRjejQ1bITveFI6gkcg)。 - 首先我们需要在添加品牌接口的参数`PmsBrandParam`中添加校验注解,用于确定属性的校验规则及校验失败后需要返回的信息; ```java /** * 品牌传递参数 * Created by macro on 2018/4/26. */ public class PmsBrandParam { @ApiModelProperty(value = "品牌名称",required = true) @NotEmpty(message = "名称不能为空") private String name; @ApiModelProperty(value = "品牌首字母") private String firstLetter; @ApiModelProperty(value = "排序字段") @Min(value = 0, message = "排序最小为0") private Integer sort; @ApiModelProperty(value = "是否为厂家制造商") @FlagValidator(value = {"0","1"}, message = "厂家状态不正确") private Integer factoryStatus; @ApiModelProperty(value = "是否进行显示") @FlagValidator(value = {"0","1"}, message = "显示状态不正确") private Integer showStatus; @ApiModelProperty(value = "品牌logo",required = true) @NotEmpty(message = "品牌logo不能为空") private String logo; @ApiModelProperty(value = "品牌大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; //省略若干Getter和Setter方法... } ``` - 然后在添加品牌的接口中添加@Validated注解,并注入一个BindingResult参数; ```java /** * 品牌功能Controller * Created by macro on 2018/4/26. */ @Controller @Api(tags = "PmsBrandController", description = "商品品牌管理") @RequestMapping("/brand") public class PmsBrandController { @Autowired private PmsBrandService brandService; @ApiOperation(value = "添加品牌") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody public CommonResult create(@Validated @RequestBody PmsBrandParam pmsBrand, BindingResult result) { CommonResult commonResult; int count = brandService.createBrand(pmsBrand); if (count == 1) { commonResult = CommonResult.success(count); } else { commonResult = CommonResult.failed(); } return commonResult; } } ``` - 然后在整个Controller层创建一个切面,在其环绕通知中获取到注入的BindingResult对象,通过hasErrors方法判断校验是否通过,如果有错误信息直接返回错误信息,验证通过则放行; ```java /** * HibernateValidator错误结果处理切面 * Created by macro on 2018/4/26. */ @Aspect @Component @Order(2) public class BindingResultAspect { @Pointcut("execution(public * com.macro.mall.controller.*.*(..))") public void BindingResult() { } @Around("BindingResult()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { Object[] args = joinPoint.getArgs(); for (Object arg : args) { if (arg instanceof BindingResult) { BindingResult result = (BindingResult) arg; if (result.hasErrors()) { FieldError fieldError = result.getFieldError(); if(fieldError!=null){ return CommonResult.validateFailed(fieldError.getDefaultMessage()); }else{ return CommonResult.validateFailed(); } } } } return joinPoint.proceed(); } } ``` - 此时我们访问添加品牌的接口,不传入`name`字段,就会返回`名称不能为空`的错误信息; ![](../images/springboot_validator_01.png) - 使用切面的话,由于每个校验方法中都需要注入`BindingResult`对象,这样会导致很多重复工作,其实当校验失败时,SpringBoot默认会抛出`MethodArgumentNotValidException`或`BindException`异常,我们只要全局处理该异常依然可以得到校验失败信息。 ```java /** * 全局异常处理 * Created by macro on 2020/2/27. */ @ControllerAdvice public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = MethodArgumentNotValidException.class) public R handleValidException(MethodArgumentNotValidException e) { BindingResult bindingResult = e.getBindingResult(); String message = null; if (bindingResult.hasErrors()) { FieldError fieldError = bindingResult.getFieldError(); if (fieldError != null) { message = fieldError.getField()+fieldError.getDefaultMessage(); } } return CommonResult.failed(message); } @ResponseBody @ExceptionHandler(value = BindException.class) public R handleValidException(BindException e) { BindingResult bindingResult = e.getBindingResult(); String message = null; if (bindingResult.hasErrors()) { FieldError fieldError = bindingResult.getFieldError(); if (fieldError != null) { message = fieldError.getField()+fieldError.getDefaultMessage(); } } return Response.failed(message); } } ``` - 由于SpringBoot 2.3版本默认移除了校验功能,如果想要开启的话需要添加如下依赖。 ```xml org.springframework.boot spring-boot-starter-validation ``` ### 自定义注解 > 有时候框架提供的校验注解并不能满足我们的需要,此时我们就需要自定义校验注解。比如还是上面的添加品牌,此时有个参数`showStatus`,我们希望它只能是0或者1,不能是其他数字,此时可以使用自定义注解来实现该功能。 - 首先自定义一个校验注解类FlagValidator,然后添加@Constraint注解,使用它的validatedBy属性指定校验逻辑的具体实现类; ```java /** * 用户验证状态是否在指定范围内的注解 * Created by macro on 2018/4/26. */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD,ElementType.PARAMETER}) @Constraint(validatedBy = FlagValidatorClass.class) public @interface FlagValidator { String[] value() default {}; String message() default "flag is not found"; Class[] groups() default {}; Class[] payload() default {}; } ``` - 然后创建FlagValidatorClass作为校验逻辑的具体实现类,实现ConstraintValidator接口,这里需要指定两个泛型参数,第一个需要指定为你自定义的校验注解类,第二个指定为你要校验属性的类型,isValid方法中就是具体的校验逻辑。 ```java /** * 状态标记校验器 * Created by macro on 2018/4/26. */ public class FlagValidatorClass implements ConstraintValidator { private String[] values; @Override public void initialize(FlagValidator flagValidator) { this.values = flagValidator.value(); } @Override public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) { boolean isValid = false; if(value==null){ //当状态为空时使用默认值 return true; } for(int i=0;i 使用全局异常处理来处理校验逻辑的思路很简单,首先我们需要通过@ControllerAdvice注解定义一个全局异常的处理类,然后自定义一个校验异常,当我们在Controller中校验失败时,直接抛出该异常,这样就可以达到校验失败返回错误信息的目的了。 ### 使用到的注解 @ControllerAdvice:类似于@Component注解,可以指定一个组件,这个组件主要用于增强@Controller注解修饰的类的功能,比如说进行全局异常处理。 @ExceptionHandler:用来修饰全局异常处理的方法,可以指定异常的类型。 ### 使用方式 - 首先我们需要自定义一个异常类`ApiException`,当我们校验失败时抛出该异常: ```java /** * 自定义API异常 * Created by macro on 2020/2/27. */ public class ApiException extends RuntimeException { private IErrorCode errorCode; public ApiException(IErrorCode errorCode) { super(errorCode.getMessage()); this.errorCode = errorCode; } public ApiException(String message) { super(message); } public ApiException(Throwable cause) { super(cause); } public ApiException(String message, Throwable cause) { super(message, cause); } public IErrorCode getErrorCode() { return errorCode; } } ``` - 然后创建一个断言处理类`Asserts`,用于抛出各种`ApiException`; ```java /** * 断言处理类,用于抛出各种API异常 * Created by macro on 2020/2/27. */ public class Asserts { public static void fail(String message) { throw new ApiException(message); } public static void fail(IErrorCode errorCode) { throw new ApiException(errorCode); } } ``` - 然后再创建我们的全局异常处理类`GlobalExceptionHandler`,用于处理全局异常,并返回封装好的CommonResult对象; ```java /** * 全局异常处理 * Created by macro on 2020/2/27. */ @ControllerAdvice public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = ApiException.class) public CommonResult handle(ApiException e) { if (e.getErrorCode() != null) { return CommonResult.failed(e.getErrorCode()); } return CommonResult.failed(e.getMessage()); } } ``` - 这里拿用户领取优惠券的代码为例,我们先对比下改进前后的代码,首先看Controller层代码。改进后只要Service中的方法执行成功就表示领取优惠券成功,因为领取不成功的话会直接抛出ApiException从而返回错误信息; ```java /** * 用户优惠券管理Controller * Created by macro on 2018/8/29. */ @Controller @Api(tags = "UmsMemberCouponController", description = "用户优惠券管理") @RequestMapping("/member/coupon") public class UmsMemberCouponController { @Autowired private UmsMemberCouponService memberCouponService; //改进前 @ApiOperation("领取指定优惠券") @RequestMapping(value = "/add/{couponId}", method = RequestMethod.POST) @ResponseBody public CommonResult add(@PathVariable Long couponId) { return memberCouponService.add(couponId); } //改进后 @ApiOperation("领取指定优惠券") @RequestMapping(value = "/add/{couponId}", method = RequestMethod.POST) @ResponseBody public CommonResult add(@PathVariable Long couponId) { memberCouponService.add(couponId); return CommonResult.success(null,"领取成功"); } } ``` - 再看下Service接口中的代码,区别在于返回结果,改进后返回的是void。其实CommonResult的作用本来就是为了把Service中获取到的数据封装成统一返回结果,改进前的做法违背了这个原则,改进后的做法解决了这个问题; ```java /** * 用户优惠券管理Service * Created by macro on 2018/8/29. */ public interface UmsMemberCouponService { /** * 会员添加优惠券(改进前) */ @Transactional CommonResult add(Long couponId); /** * 会员添加优惠券(改进后) */ @Transactional void add(Long couponId); } ``` - 再看下Service实现类中的代码,可以看到原先校验逻辑中返回CommonResult的逻辑都改成了调用Asserts的fail方法来实现; ```java /** * 会员优惠券管理Service实现类 * Created by macro on 2018/8/29. */ @Service public class UmsMemberCouponServiceImpl implements UmsMemberCouponService { @Autowired private UmsMemberService memberService; @Autowired private SmsCouponMapper couponMapper; @Autowired private SmsCouponHistoryMapper couponHistoryMapper; @Autowired private SmsCouponHistoryDao couponHistoryDao; //改进前 @Override public CommonResult add(Long couponId) { UmsMember currentMember = memberService.getCurrentMember(); //获取优惠券信息,判断数量 SmsCoupon coupon = couponMapper.selectByPrimaryKey(couponId); if(coupon==null){ return CommonResult.failed("优惠券不存在"); } if(coupon.getCount()<=0){ return CommonResult.failed("优惠券已经领完了"); } Date now = new Date(); if(now.before(coupon.getEnableTime())){ return CommonResult.failed("优惠券还没到领取时间"); } //判断用户领取的优惠券数量是否超过限制 SmsCouponHistoryExample couponHistoryExample = new SmsCouponHistoryExample(); couponHistoryExample.createCriteria().andCouponIdEqualTo(couponId).andMemberIdEqualTo(currentMember.getId()); long count = couponHistoryMapper.countByExample(couponHistoryExample); if(count>=coupon.getPerLimit()){ return CommonResult.failed("您已经领取过该优惠券"); } //省略领取优惠券逻辑... return CommonResult.success(null,"领取成功"); } //改进后 @Override public void add(Long couponId) { UmsMember currentMember = memberService.getCurrentMember(); //获取优惠券信息,判断数量 SmsCoupon coupon = couponMapper.selectByPrimaryKey(couponId); if(coupon==null){ Asserts.fail("优惠券不存在"); } if(coupon.getCount()<=0){ Asserts.fail("优惠券已经领完了"); } Date now = new Date(); if(now.before(coupon.getEnableTime())){ Asserts.fail("优惠券还没到领取时间"); } //判断用户领取的优惠券数量是否超过限制 SmsCouponHistoryExample couponHistoryExample = new SmsCouponHistoryExample(); couponHistoryExample.createCriteria().andCouponIdEqualTo(couponId).andMemberIdEqualTo(currentMember.getId()); long count = couponHistoryMapper.countByExample(couponHistoryExample); if(count>=coupon.getPerLimit()){ Asserts.fail("您已经领取过该优惠券"); } //省略领取优惠券逻辑... } } ``` - 这里我们输入一个没有的优惠券ID来测试下该功能,会返回`优惠券不存在`的错误信息。 ![](../images/springboot_validator_03.png) ### 优缺点 使用全局异常来处理校验逻辑的优点是比较灵活,可以处理复杂的校验逻辑。缺点是我们需要重复编写校验代码,不像使用Hibernate Validator那样只要使用注解就可以了。不过我们可以在上面的`Asserts`类中添加一些工具方法来增强它的功能,比如判断是否为空和判断长度等都可以自己实现。 ## 总结 我们可以两种方法一起结合使用,比如简单的参数校验使用Hibernate Validator来实现,而一些涉及到数据库操作的复杂校验使用全局异常处理的方式来实现。 ## 项目源码地址 [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/springsecurity_use.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 仅需四步,整合SpringSecurity+JWT实现登录认证! > 学习过我的mall项目的应该知道,`mall-admin`模块是使用SpringSecurity+JWT来实现登录认证的,而`mall-portal`模块是使用的SpringSecurity基于Session的默认机制来实现登陆认证的。很多小伙伴都找不到`mall-portal`的登录接口,最近我把这两个模块的登录认证给统一了,都使用SpringSecurity+JWT的形式实现。 主要是通过把登录认证的通用逻辑抽取到了`mall-security`模块来实现的,下面我们讲讲如何使用`mall-security`模块来实现登录认证,仅需四步即可。 ## 整合步骤 > 这里我们以`mall-portal`改造为例来说说如何实现。 - 第一步,给需要登录认证的模块添加`mall-security`依赖: ```xml com.macro.mall mall-security ``` - 第二步,添加MallSecurityConfig配置类,继承`mall-security`中的SecurityConfig配置,并且配置一个UserDetailsService接口的实现类,用于获取登录用户详情: ```java /** * mall-security模块相关配置 * Created by macro on 2019/11/5. */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) public class MallSecurityConfig extends SecurityConfig { @Autowired private UmsMemberService memberService; @Bean public UserDetailsService userDetailsService() { //获取登录用户信息 return username -> memberService.loadUserByUsername(username); } } ``` - 第三步,在application.yml中配置下不需要安全保护的资源路径: ```yaml secure: ignored: urls: #安全路径白名单 - /swagger-ui.html - /swagger-resources/** - /swagger/** - /**/v2/api-docs - /**/*.js - /**/*.css - /**/*.png - /**/*.ico - /webjars/springfox-swagger-ui/** - /druid/** - /actuator/** - /sso/** - /home/** ``` - 第四步,在UmsMemberController中实现登录和刷新token的接口: ```java /** * 会员登录注册管理Controller * Created by macro on 2018/8/3. */ @Controller @Api(tags = "UmsMemberController", description = "会员登录注册管理") @RequestMapping("/sso") public class UmsMemberController { @Value("${jwt.tokenHeader}") private String tokenHeader; @Value("${jwt.tokenHead}") private String tokenHead; @Autowired private UmsMemberService memberService; @ApiOperation("会员登录") @RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public CommonResult login(@RequestParam String username, @RequestParam String password) { String token = memberService.login(username, password); if (token == null) { return CommonResult.validateFailed("用户名或密码错误"); } Map tokenMap = new HashMap<>(); tokenMap.put("token", token); tokenMap.put("tokenHead", tokenHead); return CommonResult.success(tokenMap); } @ApiOperation(value = "刷新token") @RequestMapping(value = "/refreshToken", method = RequestMethod.GET) @ResponseBody public CommonResult refreshToken(HttpServletRequest request) { String token = request.getHeader(tokenHeader); String refreshToken = memberService.refreshToken(token); if (refreshToken == null) { return CommonResult.failed("token已经过期!"); } Map tokenMap = new HashMap<>(); tokenMap.put("token", refreshToken); tokenMap.put("tokenHead", tokenHead); return CommonResult.success(tokenMap); } } ``` ## 实现原理 > 将SpringSecurity+JWT的代码封装成通用模块后,就可以方便其他需要登录认证的模块来使用,下面我们来看看它是如何实现的,首先我们看下`mall-security`的目录结构。 ### 目录结构 ``` lua mall-security ├── component | ├── JwtAuthenticationTokenFilter -- JWT登录授权过滤器 | ├── RestAuthenticationEntryPoint -- 自定义返回结果:未登录或登录过期 | └── RestfulAccessDeniedHandler -- 自定义返回结果:没有权限访问时 ├── config | ├── IgnoreUrlsConfig -- 用于配置不需要安全保护的资源路径 | └── SecurityConfig -- SpringSecurity通用配置 └── util └── JwtTokenUtil -- JWT的token处理工具类 ``` ### 做了哪些变化 > 其实我也就添加了两个类,一个IgnoreUrlsConfig,用于从application.yml中获取不需要安全保护的资源路径。一个SecurityConfig提取了一些SpringSecurity的通用配置。 - IgnoreUrlsConfig中的代码: ```java /** * 用于配置不需要保护的资源路径 * Created by macro on 2018/11/5. */ @Getter @Setter @ConfigurationProperties(prefix = "secure.ignored") public class IgnoreUrlsConfig { private List urls = new ArrayList<>(); } ``` - SecurityConfig中的代码: ```java /** * 对SpringSecurity的配置的扩展,支持自定义白名单资源路径和查询用户逻辑 * Created by macro on 2019/11/5. */ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity httpSecurity) throws Exception { ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity .authorizeRequests(); //不需要保护的资源路径允许访问 for (String url : ignoreUrlsConfig().getUrls()) { registry.antMatchers(url).permitAll(); } //允许跨域请求的OPTIONS请求 registry.antMatchers(HttpMethod.OPTIONS) .permitAll(); // 任何请求需要身份认证 registry.and() .authorizeRequests() .anyRequest() .authenticated() // 关闭跨站请求防护及不使用session .and() .csrf() .disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 自定义权限拒绝处理类 .and() .exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler()) .authenticationEntryPoint(restAuthenticationEntryPoint()) // 自定义权限拦截器JWT过滤器 .and() .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()) .passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() { return new JwtAuthenticationTokenFilter(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public RestfulAccessDeniedHandler restfulAccessDeniedHandler() { return new RestfulAccessDeniedHandler(); } @Bean public RestAuthenticationEntryPoint restAuthenticationEntryPoint() { return new RestAuthenticationEntryPoint(); } @Bean public IgnoreUrlsConfig ignoreUrlsConfig() { return new IgnoreUrlsConfig(); } @Bean public JwtTokenUtil jwtTokenUtil() { return new JwtTokenUtil(); } } ``` ## 项目源码地址 [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: docs/technology/swagger_upgrade.md ================================================ 学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线! # 给Swagger升级了新版本,没想到居然有这么多坑! > 看着`mall`项目中古老的Swagger API文档样式,这次我终于下定决心要给它升个级了。升级过程中遇到了好多坑,不过只要用好Maven,这些都不是个事! ## 选择升级版本 首先我们选择下需要升级的版本,直接去Maven仓库看下,哪个版本使用的比较多。虽然有最新版本`2.10.x`,但是几乎没什么人用,而上一个版本`2.9.x`使用的人却很多,看样子还是`2.9.x`版本比较稳定,我们选择升级到`2.9.2`版本。 ![](../images/swagger_upgrade_01.png) ## 升级Swagger > 接下来我们就可以开始升级Swagger版本了,原来项目里用的是`2.7.0`版本。 - 由于`mall`项目使用父项目来统一管理依赖,所以只要修改父项目中的Swagger依赖版本即可,父项目的pom.xml在项目根目录下; ```xml 2.9.2 ``` - 运行`mall-admin`项目发现无法启动,报错信息如下,有个依赖里面的某个方法找不到了,一看是`guava`里面的,估计是版本的问题; ``` *************************** APPLICATION FAILED TO START *************************** Description: An attempt was made to call a method that does not exist. The attempt was made from the following location: springfox.documentation.schema.DefaultModelDependencyProvider.dependentModels(DefaultModelDependencyProvider.java:79) The following method did not exist: com.google.common.collect.FluentIterable.concat(Ljava/lang/Iterable;Ljava/lang/Iterable;)Lcom/google/common/collect/FluentIterable; The method's class, com.google.common.collect.FluentIterable, is available from the following locations: jar:file:/C:/Users/macrozheng/.m2/repository/com/google/guava/guava/18.0/guava-18.0.jar!/com/google/common/collect/FluentIterable.class It was loaded from the following location: file:/C:/Users/macrozheng/.m2/repository/com/google/guava/guava/18.0/guava-18.0.jar Action: Correct the classpath of your application so that it contains a single, compatible version of com.google.common.collect.FluentIterable Process finished with exit code 1 ``` - 当有好几个依赖都使用了不同版本的`guava`包时,Maven是如何选择的呢?Maven是按照就近原则选择的,层级越是浅的依赖越会被选择; ![](../images/swagger_upgrade_06.png) - 此时推荐使用`Maven Helper`这款IDEA插件,直接查看`mall-admin`项目是否存在依赖冲突,guava版本果然冲突了; ![](../images/swagger_upgrade_02.png) - 通过观察可以发现`minio`这个依赖层级最浅,所以使用的是它的guava版本,直接排除掉即可; ```xml io.minio minio guava com.google.guava ``` - 排除完成后发现guava的依赖冲突已经不见了,再次运行`mall-admin`项目,发现已经可以正常运行了; ![](../images/swagger_upgrade_03.png) - 当我们访问Swagger文档时,又发现了一个问题,会报NumberFormatException异常; ``` java.lang.NumberFormatException: For input string: "" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Long.parseLong(Long.java:601) at java.lang.Long.valueOf(Long.java:803) at io.swagger.models.parameters.AbstractSerializableParameter.getExample(AbstractSerializableParameter.java:412) ``` - 原因是当我们使用@ApiModelProperty注解时,作为Long数据类型,如果你不添加`example`属性,默认值是空字符串,空字符串转型自然就会报NumberFormatException异常; ```java /** * 修改订单费用信息参数 * Created by macro on 2018/10/29. */ @Getter @Setter public class OmsMoneyInfoParam { @ApiModelProperty(value = "订单ID",example = "1") private Long orderId; } ``` - 我们已经使用了很多@ApiModelProperty注解,要一个个添加那是不可能的,不过使用新版本的`swagger-annotations`和`swagger-models`依赖包就可以解决了,于是我们的Swagger依赖变成了下面这样的; ```xml io.springfox springfox-swagger2 io.swagger swagger-annotations io.swagger swagger-models io.springfox springfox-swagger-ui io.swagger swagger-models 1.6.0 io.swagger swagger-annotations 1.6.0 ``` - 再次运行`mall-admin`发现该问题已经解决了,我们在maven中一发现不合适的依赖就排除掉,然后引入合适版本的依赖,这样做真的好么? - 其实我们可以利用Maven项目的继承特性,直接在父项目中规定好依赖的版本,这样子项目的依赖版本就能统一了; - 先把原来`pom.xml`中排除guava和swagger的配置给去除了,然后修改根目录下的pom.xml文件,指定版本号; ```xml 2.9.2 1.6.0 1.6.0 20.0 ``` - 在父项目的依赖管理节点下添加需要统一管理的相关依赖,至此Swagger版本升级完成; ```xml io.springfox springfox-swagger2 ${swagger2.version} io.springfox springfox-swagger-ui ${swagger2.version} io.swagger swagger-models ${swagger-models.version} io.swagger swagger-annotations ${swagger-annotations.version} com.google.guava guava ${guava.version} ``` - 当我们配置好Token访问需要权限的接口时,会发现品牌、商品、商品分类下的接口有权限访问,其他提示无权限,那是因为我们使用了如下配置来配置需要登录认证的路径; ```java @Configuration @EnableSwagger2 public class Swagger2Config { private List securityContexts() { //设置需要登录认证的路径 List result = new ArrayList<>(); result.add(getContextByPath("/brand/.*")); result.add(getContextByPath("/product/.*")); result.add(getContextByPath("/productCategory/.*")); return result; } } ``` - 修改为全部路径即可,这个和旧版有点不同,旧版访问所有接口都会在头信息中带Token,而新版只会对配置的路径带Token。 ```java @Configuration @EnableSwagger2 public class Swagger2Config { private List securityContexts() { //设置需要登录认证的路径 List result = new ArrayList<>(); result.add(getContextByPath("/*/.*")); return result; } } ``` ## 新老版本界面对比 > Swagger升级到2.9.2版本后界面瞬间变得美观了,让我们对新老界面来个对比。 ### 老版本 ![](../images/swagger_upgrade_04.png) ### 新版本 ![](../images/swagger_upgrade_05.png) ## 项目源码地址 https://github.com/macrozheng/mall ## 公众号 ![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg) ================================================ FILE: document/json/accounts.json ================================================ {"index":{"_id":"1"}} {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"} {"index":{"_id":"6"}} {"account_number":6,"balance":5686,"firstname":"Hattie","lastname":"Bond","age":36,"gender":"M","address":"671 Bristol Street","employer":"Netagy","email":"hattiebond@netagy.com","city":"Dante","state":"TN"} {"index":{"_id":"13"}} {"account_number":13,"balance":32838,"firstname":"Nanette","lastname":"Bates","age":28,"gender":"F","address":"789 Madison Street","employer":"Quility","email":"nanettebates@quility.com","city":"Nogal","state":"VA"} {"index":{"_id":"18"}} {"account_number":18,"balance":4180,"firstname":"Dale","lastname":"Adams","age":33,"gender":"M","address":"467 Hutchinson Court","employer":"Boink","email":"daleadams@boink.com","city":"Orick","state":"MD"} {"index":{"_id":"20"}} {"account_number":20,"balance":16418,"firstname":"Elinor","lastname":"Ratliff","age":36,"gender":"M","address":"282 Kings Place","employer":"Scentric","email":"elinorratliff@scentric.com","city":"Ribera","state":"WA"} {"index":{"_id":"25"}} {"account_number":25,"balance":40540,"firstname":"Virginia","lastname":"Ayala","age":39,"gender":"F","address":"171 Putnam Avenue","employer":"Filodyne","email":"virginiaayala@filodyne.com","city":"Nicholson","state":"PA"} {"index":{"_id":"32"}} {"account_number":32,"balance":48086,"firstname":"Dillard","lastname":"Mcpherson","age":34,"gender":"F","address":"702 Quentin Street","employer":"Quailcom","email":"dillardmcpherson@quailcom.com","city":"Veguita","state":"IN"} {"index":{"_id":"37"}} {"account_number":37,"balance":18612,"firstname":"Mcgee","lastname":"Mooney","age":39,"gender":"M","address":"826 Fillmore Place","employer":"Reversus","email":"mcgeemooney@reversus.com","city":"Tooleville","state":"OK"} {"index":{"_id":"44"}} {"account_number":44,"balance":34487,"firstname":"Aurelia","lastname":"Harding","age":37,"gender":"M","address":"502 Baycliff Terrace","employer":"Orbalix","email":"aureliaharding@orbalix.com","city":"Yardville","state":"DE"} {"index":{"_id":"49"}} {"account_number":49,"balance":29104,"firstname":"Fulton","lastname":"Holt","age":23,"gender":"F","address":"451 Humboldt Street","employer":"Anocha","email":"fultonholt@anocha.com","city":"Sunriver","state":"RI"} {"index":{"_id":"51"}} {"account_number":51,"balance":14097,"firstname":"Burton","lastname":"Meyers","age":31,"gender":"F","address":"334 River Street","employer":"Bezal","email":"burtonmeyers@bezal.com","city":"Jacksonburg","state":"MO"} {"index":{"_id":"56"}} {"account_number":56,"balance":14992,"firstname":"Josie","lastname":"Nelson","age":32,"gender":"M","address":"857 Tabor Court","employer":"Emtrac","email":"josienelson@emtrac.com","city":"Sunnyside","state":"UT"} {"index":{"_id":"63"}} {"account_number":63,"balance":6077,"firstname":"Hughes","lastname":"Owens","age":30,"gender":"F","address":"510 Sedgwick Street","employer":"Valpreal","email":"hughesowens@valpreal.com","city":"Guilford","state":"KS"} {"index":{"_id":"68"}} {"account_number":68,"balance":44214,"firstname":"Hall","lastname":"Key","age":25,"gender":"F","address":"927 Bay Parkway","employer":"Eventex","email":"hallkey@eventex.com","city":"Shawmut","state":"CA"} {"index":{"_id":"70"}} {"account_number":70,"balance":38172,"firstname":"Deidre","lastname":"Thompson","age":33,"gender":"F","address":"685 School Lane","employer":"Netplode","email":"deidrethompson@netplode.com","city":"Chestnut","state":"GA"} {"index":{"_id":"75"}} {"account_number":75,"balance":40500,"firstname":"Sandoval","lastname":"Kramer","age":22,"gender":"F","address":"166 Irvington Place","employer":"Overfork","email":"sandovalkramer@overfork.com","city":"Limestone","state":"NH"} {"index":{"_id":"82"}} {"account_number":82,"balance":41412,"firstname":"Concetta","lastname":"Barnes","age":39,"gender":"F","address":"195 Bayview Place","employer":"Fitcore","email":"concettabarnes@fitcore.com","city":"Summerfield","state":"NC"} {"index":{"_id":"87"}} {"account_number":87,"balance":1133,"firstname":"Hewitt","lastname":"Kidd","age":22,"gender":"M","address":"446 Halleck Street","employer":"Isologics","email":"hewittkidd@isologics.com","city":"Coalmont","state":"ME"} {"index":{"_id":"94"}} {"account_number":94,"balance":41060,"firstname":"Brittany","lastname":"Cabrera","age":30,"gender":"F","address":"183 Kathleen Court","employer":"Mixers","email":"brittanycabrera@mixers.com","city":"Cornucopia","state":"AZ"} {"index":{"_id":"99"}} {"account_number":99,"balance":47159,"firstname":"Ratliff","lastname":"Heath","age":39,"gender":"F","address":"806 Rockwell Place","employer":"Zappix","email":"ratliffheath@zappix.com","city":"Shaft","state":"ND"} {"index":{"_id":"102"}} {"account_number":102,"balance":29712,"firstname":"Dena","lastname":"Olson","age":27,"gender":"F","address":"759 Newkirk Avenue","employer":"Hinway","email":"denaolson@hinway.com","city":"Choctaw","state":"NJ"} {"index":{"_id":"107"}} {"account_number":107,"balance":48844,"firstname":"Randi","lastname":"Rich","age":28,"gender":"M","address":"694 Jefferson Street","employer":"Netplax","email":"randirich@netplax.com","city":"Bellfountain","state":"SC"} {"index":{"_id":"114"}} {"account_number":114,"balance":43045,"firstname":"Josephine","lastname":"Joseph","age":31,"gender":"F","address":"451 Oriental Court","employer":"Turnabout","email":"josephinejoseph@turnabout.com","city":"Sedley","state":"AL"} {"index":{"_id":"119"}} {"account_number":119,"balance":49222,"firstname":"Laverne","lastname":"Johnson","age":28,"gender":"F","address":"302 Howard Place","employer":"Senmei","email":"lavernejohnson@senmei.com","city":"Herlong","state":"DC"} {"index":{"_id":"121"}} {"account_number":121,"balance":19594,"firstname":"Acevedo","lastname":"Dorsey","age":32,"gender":"M","address":"479 Nova Court","employer":"Netropic","email":"acevedodorsey@netropic.com","city":"Islandia","state":"CT"} {"index":{"_id":"126"}} {"account_number":126,"balance":3607,"firstname":"Effie","lastname":"Gates","age":39,"gender":"F","address":"620 National Drive","employer":"Digitalus","email":"effiegates@digitalus.com","city":"Blodgett","state":"MD"} {"index":{"_id":"133"}} {"account_number":133,"balance":26135,"firstname":"Deena","lastname":"Richmond","age":36,"gender":"F","address":"646 Underhill Avenue","employer":"Sunclipse","email":"deenarichmond@sunclipse.com","city":"Austinburg","state":"SC"} {"index":{"_id":"138"}} {"account_number":138,"balance":9006,"firstname":"Daniel","lastname":"Arnold","age":39,"gender":"F","address":"422 Malbone Street","employer":"Ecstasia","email":"danielarnold@ecstasia.com","city":"Gardiner","state":"MO"} {"index":{"_id":"140"}} {"account_number":140,"balance":26696,"firstname":"Cotton","lastname":"Christensen","age":32,"gender":"M","address":"878 Schermerhorn Street","employer":"Prowaste","email":"cottonchristensen@prowaste.com","city":"Mayfair","state":"LA"} {"index":{"_id":"145"}} {"account_number":145,"balance":47406,"firstname":"Rowena","lastname":"Wilkinson","age":32,"gender":"M","address":"891 Elton Street","employer":"Asimiline","email":"rowenawilkinson@asimiline.com","city":"Ripley","state":"NH"} {"index":{"_id":"152"}} {"account_number":152,"balance":8088,"firstname":"Wolfe","lastname":"Rocha","age":21,"gender":"M","address":"457 Guernsey Street","employer":"Hivedom","email":"wolferocha@hivedom.com","city":"Adelino","state":"MS"} {"index":{"_id":"157"}} {"account_number":157,"balance":39868,"firstname":"Claudia","lastname":"Terry","age":20,"gender":"F","address":"132 Gunnison Court","employer":"Lumbrex","email":"claudiaterry@lumbrex.com","city":"Castleton","state":"MD"} {"index":{"_id":"164"}} {"account_number":164,"balance":9101,"firstname":"Cummings","lastname":"Little","age":26,"gender":"F","address":"308 Schaefer Street","employer":"Comtrak","email":"cummingslittle@comtrak.com","city":"Chaparrito","state":"WI"} {"index":{"_id":"169"}} {"account_number":169,"balance":45953,"firstname":"Hollie","lastname":"Osborn","age":34,"gender":"M","address":"671 Seaview Court","employer":"Musaphics","email":"hollieosborn@musaphics.com","city":"Hanover","state":"GA"} {"index":{"_id":"171"}} {"account_number":171,"balance":7091,"firstname":"Nelda","lastname":"Hopper","age":39,"gender":"M","address":"742 Prospect Place","employer":"Equicom","email":"neldahopper@equicom.com","city":"Finderne","state":"SC"} {"index":{"_id":"176"}} {"account_number":176,"balance":18607,"firstname":"Kemp","lastname":"Walters","age":28,"gender":"F","address":"906 Howard Avenue","employer":"Eyewax","email":"kempwalters@eyewax.com","city":"Why","state":"KY"} {"index":{"_id":"183"}} {"account_number":183,"balance":14223,"firstname":"Hudson","lastname":"English","age":26,"gender":"F","address":"823 Herkimer Place","employer":"Xinware","email":"hudsonenglish@xinware.com","city":"Robbins","state":"ND"} {"index":{"_id":"188"}} {"account_number":188,"balance":41504,"firstname":"Tia","lastname":"Miranda","age":24,"gender":"F","address":"583 Ainslie Street","employer":"Jasper","email":"tiamiranda@jasper.com","city":"Summerset","state":"UT"} {"index":{"_id":"190"}} {"account_number":190,"balance":3150,"firstname":"Blake","lastname":"Davidson","age":30,"gender":"F","address":"636 Diamond Street","employer":"Quantasis","email":"blakedavidson@quantasis.com","city":"Crumpler","state":"KY"} {"index":{"_id":"195"}} {"account_number":195,"balance":5025,"firstname":"Kaye","lastname":"Gibson","age":31,"gender":"M","address":"955 Hopkins Street","employer":"Zork","email":"kayegibson@zork.com","city":"Ola","state":"WY"} {"index":{"_id":"203"}} {"account_number":203,"balance":21890,"firstname":"Eve","lastname":"Wyatt","age":33,"gender":"M","address":"435 Furman Street","employer":"Assitia","email":"evewyatt@assitia.com","city":"Jamestown","state":"MN"} {"index":{"_id":"208"}} {"account_number":208,"balance":40760,"firstname":"Garcia","lastname":"Hess","age":26,"gender":"F","address":"810 Nostrand Avenue","employer":"Quiltigen","email":"garciahess@quiltigen.com","city":"Brooktrails","state":"GA"} {"index":{"_id":"210"}} {"account_number":210,"balance":33946,"firstname":"Cherry","lastname":"Carey","age":24,"gender":"M","address":"539 Tiffany Place","employer":"Martgo","email":"cherrycarey@martgo.com","city":"Fairacres","state":"AK"} {"index":{"_id":"215"}} {"account_number":215,"balance":37427,"firstname":"Copeland","lastname":"Solomon","age":20,"gender":"M","address":"741 McDonald Avenue","employer":"Recognia","email":"copelandsolomon@recognia.com","city":"Edmund","state":"ME"} {"index":{"_id":"222"}} {"account_number":222,"balance":14764,"firstname":"Rachelle","lastname":"Rice","age":36,"gender":"M","address":"333 Narrows Avenue","employer":"Enaut","email":"rachellerice@enaut.com","city":"Wright","state":"AZ"} {"index":{"_id":"227"}} {"account_number":227,"balance":19780,"firstname":"Coleman","lastname":"Berg","age":22,"gender":"M","address":"776 Little Street","employer":"Exoteric","email":"colemanberg@exoteric.com","city":"Eagleville","state":"WV"} {"index":{"_id":"234"}} {"account_number":234,"balance":44207,"firstname":"Betty","lastname":"Hall","age":37,"gender":"F","address":"709 Garfield Place","employer":"Miraclis","email":"bettyhall@miraclis.com","city":"Bendon","state":"NY"} {"index":{"_id":"239"}} {"account_number":239,"balance":25719,"firstname":"Chang","lastname":"Boyer","age":36,"gender":"M","address":"895 Brigham Street","employer":"Qaboos","email":"changboyer@qaboos.com","city":"Belgreen","state":"NH"} {"index":{"_id":"241"}} {"account_number":241,"balance":25379,"firstname":"Schroeder","lastname":"Harrington","age":26,"gender":"M","address":"610 Tapscott Avenue","employer":"Otherway","email":"schroederharrington@otherway.com","city":"Ebro","state":"TX"} {"index":{"_id":"246"}} {"account_number":246,"balance":28405,"firstname":"Katheryn","lastname":"Foster","age":21,"gender":"F","address":"259 Kane Street","employer":"Quantalia","email":"katherynfoster@quantalia.com","city":"Bath","state":"TX"} {"index":{"_id":"253"}} {"account_number":253,"balance":20240,"firstname":"Melissa","lastname":"Gould","age":31,"gender":"M","address":"440 Fuller Place","employer":"Buzzopia","email":"melissagould@buzzopia.com","city":"Lumberton","state":"MD"} {"index":{"_id":"258"}} {"account_number":258,"balance":5712,"firstname":"Lindsey","lastname":"Hawkins","age":37,"gender":"M","address":"706 Frost Street","employer":"Enormo","email":"lindseyhawkins@enormo.com","city":"Gardners","state":"AK"} {"index":{"_id":"260"}} {"account_number":260,"balance":2726,"firstname":"Kari","lastname":"Skinner","age":30,"gender":"F","address":"735 Losee Terrace","employer":"Singavera","email":"kariskinner@singavera.com","city":"Rushford","state":"WV"} {"index":{"_id":"265"}} {"account_number":265,"balance":46910,"firstname":"Marion","lastname":"Schneider","age":26,"gender":"F","address":"574 Everett Avenue","employer":"Evidends","email":"marionschneider@evidends.com","city":"Maplewood","state":"WY"} {"index":{"_id":"272"}} {"account_number":272,"balance":19253,"firstname":"Lilly","lastname":"Morgan","age":25,"gender":"F","address":"689 Fleet Street","employer":"Biolive","email":"lillymorgan@biolive.com","city":"Sunbury","state":"OH"} {"index":{"_id":"277"}} {"account_number":277,"balance":29564,"firstname":"Romero","lastname":"Lott","age":31,"gender":"M","address":"456 Danforth Street","employer":"Plasto","email":"romerolott@plasto.com","city":"Vincent","state":"VT"} {"index":{"_id":"284"}} {"account_number":284,"balance":22806,"firstname":"Randolph","lastname":"Banks","age":29,"gender":"M","address":"875 Hamilton Avenue","employer":"Caxt","email":"randolphbanks@caxt.com","city":"Crawfordsville","state":"WA"} {"index":{"_id":"289"}} {"account_number":289,"balance":7798,"firstname":"Blair","lastname":"Church","age":29,"gender":"M","address":"370 Sutton Street","employer":"Cubix","email":"blairchurch@cubix.com","city":"Nile","state":"NH"} {"index":{"_id":"291"}} {"account_number":291,"balance":19955,"firstname":"Lynn","lastname":"Pollard","age":40,"gender":"F","address":"685 Pierrepont Street","employer":"Slambda","email":"lynnpollard@slambda.com","city":"Mappsville","state":"ID"} {"index":{"_id":"296"}} {"account_number":296,"balance":24606,"firstname":"Rosa","lastname":"Oliver","age":34,"gender":"M","address":"168 Woodbine Street","employer":"Idetica","email":"rosaoliver@idetica.com","city":"Robinson","state":"WY"} {"index":{"_id":"304"}} {"account_number":304,"balance":28647,"firstname":"Palmer","lastname":"Clark","age":35,"gender":"M","address":"866 Boulevard Court","employer":"Maximind","email":"palmerclark@maximind.com","city":"Avalon","state":"NH"} {"index":{"_id":"309"}} {"account_number":309,"balance":3830,"firstname":"Rosemarie","lastname":"Nieves","age":30,"gender":"M","address":"206 Alice Court","employer":"Zounds","email":"rosemarienieves@zounds.com","city":"Ferney","state":"AR"} {"index":{"_id":"311"}} {"account_number":311,"balance":13388,"firstname":"Vinson","lastname":"Ballard","age":23,"gender":"F","address":"960 Glendale Court","employer":"Gynk","email":"vinsonballard@gynk.com","city":"Fairforest","state":"WY"} {"index":{"_id":"316"}} {"account_number":316,"balance":8214,"firstname":"Anita","lastname":"Ewing","age":32,"gender":"M","address":"396 Lombardy Street","employer":"Panzent","email":"anitaewing@panzent.com","city":"Neahkahnie","state":"WY"} {"index":{"_id":"323"}} {"account_number":323,"balance":42230,"firstname":"Chelsea","lastname":"Gamble","age":34,"gender":"F","address":"356 Dare Court","employer":"Isosphere","email":"chelseagamble@isosphere.com","city":"Dundee","state":"MD"} {"index":{"_id":"328"}} {"account_number":328,"balance":12523,"firstname":"Good","lastname":"Campbell","age":27,"gender":"F","address":"438 Hicks Street","employer":"Gracker","email":"goodcampbell@gracker.com","city":"Marion","state":"CA"} {"index":{"_id":"330"}} {"account_number":330,"balance":41620,"firstname":"Yvette","lastname":"Browning","age":34,"gender":"F","address":"431 Beekman Place","employer":"Marketoid","email":"yvettebrowning@marketoid.com","city":"Talpa","state":"CO"} {"index":{"_id":"335"}} {"account_number":335,"balance":35433,"firstname":"Vera","lastname":"Hansen","age":24,"gender":"M","address":"252 Bushwick Avenue","employer":"Zanilla","email":"verahansen@zanilla.com","city":"Manila","state":"TN"} {"index":{"_id":"342"}} {"account_number":342,"balance":33670,"firstname":"Vivian","lastname":"Wells","age":36,"gender":"M","address":"570 Cobek Court","employer":"Nutralab","email":"vivianwells@nutralab.com","city":"Fontanelle","state":"OK"} {"index":{"_id":"347"}} {"account_number":347,"balance":36038,"firstname":"Gould","lastname":"Carson","age":24,"gender":"F","address":"784 Pulaski Street","employer":"Mobildata","email":"gouldcarson@mobildata.com","city":"Goochland","state":"MI"} {"index":{"_id":"354"}} {"account_number":354,"balance":21294,"firstname":"Kidd","lastname":"Mclean","age":22,"gender":"M","address":"691 Saratoga Avenue","employer":"Ronbert","email":"kiddmclean@ronbert.com","city":"Tioga","state":"ME"} {"index":{"_id":"359"}} {"account_number":359,"balance":29927,"firstname":"Vanessa","lastname":"Harvey","age":28,"gender":"F","address":"679 Rutledge Street","employer":"Zentime","email":"vanessaharvey@zentime.com","city":"Williston","state":"IL"} {"index":{"_id":"361"}} {"account_number":361,"balance":23659,"firstname":"Noreen","lastname":"Shelton","age":36,"gender":"M","address":"702 Tillary Street","employer":"Medmex","email":"noreenshelton@medmex.com","city":"Derwood","state":"NH"} {"index":{"_id":"366"}} {"account_number":366,"balance":42368,"firstname":"Lydia","lastname":"Cooke","age":31,"gender":"M","address":"470 Coleman Street","employer":"Comstar","email":"lydiacooke@comstar.com","city":"Datil","state":"TN"} {"index":{"_id":"373"}} {"account_number":373,"balance":9671,"firstname":"Simpson","lastname":"Carpenter","age":21,"gender":"M","address":"837 Horace Court","employer":"Snips","email":"simpsoncarpenter@snips.com","city":"Tolu","state":"MA"} {"index":{"_id":"378"}} {"account_number":378,"balance":27100,"firstname":"Watson","lastname":"Simpson","age":36,"gender":"F","address":"644 Thomas Street","employer":"Wrapture","email":"watsonsimpson@wrapture.com","city":"Keller","state":"TX"} {"index":{"_id":"380"}} {"account_number":380,"balance":35628,"firstname":"Fernandez","lastname":"Reid","age":33,"gender":"F","address":"154 Melba Court","employer":"Cosmosis","email":"fernandezreid@cosmosis.com","city":"Boyd","state":"NE"} {"index":{"_id":"385"}} {"account_number":385,"balance":11022,"firstname":"Rosalinda","lastname":"Valencia","age":22,"gender":"M","address":"933 Lloyd Street","employer":"Zoarere","email":"rosalindavalencia@zoarere.com","city":"Waverly","state":"GA"} {"index":{"_id":"392"}} {"account_number":392,"balance":31613,"firstname":"Dotson","lastname":"Dean","age":35,"gender":"M","address":"136 Ford Street","employer":"Petigems","email":"dotsondean@petigems.com","city":"Chical","state":"SD"} {"index":{"_id":"397"}} {"account_number":397,"balance":37418,"firstname":"Leonard","lastname":"Gray","age":36,"gender":"F","address":"840 Morgan Avenue","employer":"Recritube","email":"leonardgray@recritube.com","city":"Edenburg","state":"AL"} {"index":{"_id":"400"}} {"account_number":400,"balance":20685,"firstname":"Kane","lastname":"King","age":21,"gender":"F","address":"405 Cornelia Street","employer":"Tri@Tribalog","email":"kaneking@tri@tribalog.com","city":"Gulf","state":"VT"} {"index":{"_id":"405"}} {"account_number":405,"balance":5679,"firstname":"Strickland","lastname":"Fuller","age":26,"gender":"M","address":"990 Concord Street","employer":"Digique","email":"stricklandfuller@digique.com","city":"Southmont","state":"NV"} {"index":{"_id":"412"}} {"account_number":412,"balance":27436,"firstname":"Ilene","lastname":"Abbott","age":26,"gender":"M","address":"846 Vine Street","employer":"Typhonica","email":"ileneabbott@typhonica.com","city":"Cedarville","state":"VT"} {"index":{"_id":"417"}} {"account_number":417,"balance":1788,"firstname":"Wheeler","lastname":"Ayers","age":35,"gender":"F","address":"677 Hope Street","employer":"Fortean","email":"wheelerayers@fortean.com","city":"Ironton","state":"PA"} {"index":{"_id":"424"}} {"account_number":424,"balance":36818,"firstname":"Tracie","lastname":"Gregory","age":34,"gender":"M","address":"112 Hunterfly Place","employer":"Comstruct","email":"traciegregory@comstruct.com","city":"Onton","state":"TN"} {"index":{"_id":"429"}} {"account_number":429,"balance":46970,"firstname":"Cantu","lastname":"Lindsey","age":31,"gender":"M","address":"404 Willoughby Avenue","employer":"Inquala","email":"cantulindsey@inquala.com","city":"Cowiche","state":"IA"} {"index":{"_id":"431"}} {"account_number":431,"balance":13136,"firstname":"Laurie","lastname":"Shaw","age":26,"gender":"F","address":"263 Aviation Road","employer":"Zillanet","email":"laurieshaw@zillanet.com","city":"Harmon","state":"WV"} {"index":{"_id":"436"}} {"account_number":436,"balance":27585,"firstname":"Alexander","lastname":"Sargent","age":23,"gender":"M","address":"363 Albemarle Road","employer":"Fangold","email":"alexandersargent@fangold.com","city":"Calpine","state":"OR"} {"index":{"_id":"443"}} {"account_number":443,"balance":7588,"firstname":"Huff","lastname":"Thomas","age":23,"gender":"M","address":"538 Erskine Loop","employer":"Accufarm","email":"huffthomas@accufarm.com","city":"Corinne","state":"AL"} {"index":{"_id":"448"}} {"account_number":448,"balance":22776,"firstname":"Adriana","lastname":"Mcfadden","age":35,"gender":"F","address":"984 Woodside Avenue","employer":"Telequiet","email":"adrianamcfadden@telequiet.com","city":"Darrtown","state":"WI"} {"index":{"_id":"450"}} {"account_number":450,"balance":2643,"firstname":"Bradford","lastname":"Nielsen","age":25,"gender":"M","address":"487 Keen Court","employer":"Exovent","email":"bradfordnielsen@exovent.com","city":"Hamilton","state":"DE"} {"index":{"_id":"455"}} {"account_number":455,"balance":39556,"firstname":"Lynn","lastname":"Tran","age":36,"gender":"M","address":"741 Richmond Street","employer":"Optyk","email":"lynntran@optyk.com","city":"Clinton","state":"WV"} {"index":{"_id":"462"}} {"account_number":462,"balance":10871,"firstname":"Calderon","lastname":"Day","age":27,"gender":"M","address":"810 Milford Street","employer":"Cofine","email":"calderonday@cofine.com","city":"Kula","state":"OK"} {"index":{"_id":"467"}} {"account_number":467,"balance":6312,"firstname":"Angelica","lastname":"May","age":32,"gender":"F","address":"384 Karweg Place","employer":"Keeg","email":"angelicamay@keeg.com","city":"Tetherow","state":"IA"} {"index":{"_id":"474"}} {"account_number":474,"balance":35896,"firstname":"Obrien","lastname":"Walton","age":40,"gender":"F","address":"192 Ide Court","employer":"Suremax","email":"obrienwalton@suremax.com","city":"Crucible","state":"UT"} {"index":{"_id":"479"}} {"account_number":479,"balance":31865,"firstname":"Cameron","lastname":"Ross","age":40,"gender":"M","address":"904 Bouck Court","employer":"Telpod","email":"cameronross@telpod.com","city":"Nord","state":"MO"} {"index":{"_id":"481"}} {"account_number":481,"balance":20024,"firstname":"Lina","lastname":"Stanley","age":33,"gender":"M","address":"361 Hanover Place","employer":"Strozen","email":"linastanley@strozen.com","city":"Wyoming","state":"NC"} {"index":{"_id":"486"}} {"account_number":486,"balance":35902,"firstname":"Dixie","lastname":"Fuentes","age":22,"gender":"F","address":"991 Applegate Court","employer":"Portico","email":"dixiefuentes@portico.com","city":"Salix","state":"VA"} {"index":{"_id":"493"}} {"account_number":493,"balance":5871,"firstname":"Campbell","lastname":"Best","age":24,"gender":"M","address":"297 Friel Place","employer":"Fanfare","email":"campbellbest@fanfare.com","city":"Kidder","state":"GA"} {"index":{"_id":"498"}} {"account_number":498,"balance":10516,"firstname":"Stella","lastname":"Hinton","age":39,"gender":"F","address":"649 Columbia Place","employer":"Flyboyz","email":"stellahinton@flyboyz.com","city":"Crenshaw","state":"SC"} {"index":{"_id":"501"}} {"account_number":501,"balance":16572,"firstname":"Kelley","lastname":"Ochoa","age":36,"gender":"M","address":"451 Clifton Place","employer":"Bluplanet","email":"kelleyochoa@bluplanet.com","city":"Gouglersville","state":"CT"} {"index":{"_id":"506"}} {"account_number":506,"balance":43440,"firstname":"Davidson","lastname":"Salas","age":28,"gender":"M","address":"731 Cleveland Street","employer":"Sequitur","email":"davidsonsalas@sequitur.com","city":"Lloyd","state":"ME"} {"index":{"_id":"513"}} {"account_number":513,"balance":30040,"firstname":"Maryellen","lastname":"Rose","age":37,"gender":"F","address":"428 Durland Place","employer":"Waterbaby","email":"maryellenrose@waterbaby.com","city":"Kiskimere","state":"RI"} {"index":{"_id":"518"}} {"account_number":518,"balance":48954,"firstname":"Finch","lastname":"Curtis","age":29,"gender":"F","address":"137 Ryder Street","employer":"Viagrand","email":"finchcurtis@viagrand.com","city":"Riverton","state":"MO"} {"index":{"_id":"520"}} {"account_number":520,"balance":27987,"firstname":"Brandy","lastname":"Calhoun","age":32,"gender":"M","address":"818 Harden Street","employer":"Maxemia","email":"brandycalhoun@maxemia.com","city":"Sidman","state":"OR"} {"index":{"_id":"525"}} {"account_number":525,"balance":23545,"firstname":"Holly","lastname":"Miles","age":25,"gender":"M","address":"746 Ludlam Place","employer":"Xurban","email":"hollymiles@xurban.com","city":"Harold","state":"AR"} {"index":{"_id":"532"}} {"account_number":532,"balance":17207,"firstname":"Hardin","lastname":"Kirk","age":26,"gender":"M","address":"268 Canarsie Road","employer":"Exposa","email":"hardinkirk@exposa.com","city":"Stouchsburg","state":"IL"} {"index":{"_id":"537"}} {"account_number":537,"balance":31069,"firstname":"Morin","lastname":"Frost","age":29,"gender":"M","address":"910 Lake Street","employer":"Primordia","email":"morinfrost@primordia.com","city":"Rivera","state":"DE"} {"index":{"_id":"544"}} {"account_number":544,"balance":41735,"firstname":"Short","lastname":"Dennis","age":21,"gender":"F","address":"908 Glen Street","employer":"Minga","email":"shortdennis@minga.com","city":"Dale","state":"KY"} {"index":{"_id":"549"}} {"account_number":549,"balance":1932,"firstname":"Jacqueline","lastname":"Maxwell","age":40,"gender":"M","address":"444 Schenck Place","employer":"Fuelworks","email":"jacquelinemaxwell@fuelworks.com","city":"Oretta","state":"OR"} {"index":{"_id":"551"}} {"account_number":551,"balance":21732,"firstname":"Milagros","lastname":"Travis","age":27,"gender":"F","address":"380 Murdock Court","employer":"Sloganaut","email":"milagrostravis@sloganaut.com","city":"Homeland","state":"AR"} {"index":{"_id":"556"}} {"account_number":556,"balance":36420,"firstname":"Collier","lastname":"Odonnell","age":35,"gender":"M","address":"591 Nolans Lane","employer":"Sultraxin","email":"collierodonnell@sultraxin.com","city":"Fulford","state":"MD"} {"index":{"_id":"563"}} {"account_number":563,"balance":43403,"firstname":"Morgan","lastname":"Torres","age":30,"gender":"F","address":"672 Belvidere Street","employer":"Quonata","email":"morgantorres@quonata.com","city":"Hollymead","state":"KY"} {"index":{"_id":"568"}} {"account_number":568,"balance":36628,"firstname":"Lesa","lastname":"Maynard","age":29,"gender":"F","address":"295 Whitty Lane","employer":"Coash","email":"lesamaynard@coash.com","city":"Broadlands","state":"VT"} {"index":{"_id":"570"}} {"account_number":570,"balance":26751,"firstname":"Church","lastname":"Mercado","age":24,"gender":"F","address":"892 Wyckoff Street","employer":"Xymonk","email":"churchmercado@xymonk.com","city":"Gloucester","state":"KY"} {"index":{"_id":"575"}} {"account_number":575,"balance":12588,"firstname":"Buchanan","lastname":"Pope","age":39,"gender":"M","address":"581 Sumner Place","employer":"Stucco","email":"buchananpope@stucco.com","city":"Ellerslie","state":"MD"} {"index":{"_id":"582"}} {"account_number":582,"balance":33371,"firstname":"Manning","lastname":"Guthrie","age":24,"gender":"F","address":"271 Jodie Court","employer":"Xerex","email":"manningguthrie@xerex.com","city":"Breinigsville","state":"NM"} {"index":{"_id":"587"}} {"account_number":587,"balance":3468,"firstname":"Carly","lastname":"Johns","age":33,"gender":"M","address":"390 Noll Street","employer":"Gallaxia","email":"carlyjohns@gallaxia.com","city":"Emison","state":"DC"} {"index":{"_id":"594"}} {"account_number":594,"balance":28194,"firstname":"Golden","lastname":"Donovan","age":26,"gender":"M","address":"199 Jewel Street","employer":"Organica","email":"goldendonovan@organica.com","city":"Macdona","state":"RI"} {"index":{"_id":"599"}} {"account_number":599,"balance":11944,"firstname":"Joanna","lastname":"Jennings","age":36,"gender":"F","address":"318 Irving Street","employer":"Extremo","email":"joannajennings@extremo.com","city":"Bartley","state":"MI"} {"index":{"_id":"602"}} {"account_number":602,"balance":38699,"firstname":"Mcgowan","lastname":"Mcclain","age":33,"gender":"M","address":"361 Stoddard Place","employer":"Oatfarm","email":"mcgowanmcclain@oatfarm.com","city":"Kapowsin","state":"MI"} {"index":{"_id":"607"}} {"account_number":607,"balance":38350,"firstname":"White","lastname":"Small","age":38,"gender":"F","address":"736 Judge Street","employer":"Immunics","email":"whitesmall@immunics.com","city":"Fairfield","state":"HI"} {"index":{"_id":"614"}} {"account_number":614,"balance":13157,"firstname":"Salazar","lastname":"Howard","age":35,"gender":"F","address":"847 Imlay Street","employer":"Retrack","email":"salazarhoward@retrack.com","city":"Grill","state":"FL"} {"index":{"_id":"619"}} {"account_number":619,"balance":48755,"firstname":"Grimes","lastname":"Reynolds","age":36,"gender":"M","address":"378 Denton Place","employer":"Frenex","email":"grimesreynolds@frenex.com","city":"Murillo","state":"LA"} {"index":{"_id":"621"}} {"account_number":621,"balance":35480,"firstname":"Leslie","lastname":"Sloan","age":26,"gender":"F","address":"336 Kansas Place","employer":"Dancity","email":"lesliesloan@dancity.com","city":"Corriganville","state":"AR"} {"index":{"_id":"626"}} {"account_number":626,"balance":19498,"firstname":"Ava","lastname":"Richardson","age":31,"gender":"F","address":"666 Nautilus Avenue","employer":"Cinaster","email":"avarichardson@cinaster.com","city":"Sutton","state":"AL"} {"index":{"_id":"633"}} {"account_number":633,"balance":35874,"firstname":"Conner","lastname":"Ramos","age":34,"gender":"M","address":"575 Agate Court","employer":"Insource","email":"connerramos@insource.com","city":"Madaket","state":"OK"} {"index":{"_id":"638"}} {"account_number":638,"balance":2658,"firstname":"Bridget","lastname":"Gallegos","age":31,"gender":"M","address":"383 Wogan Terrace","employer":"Songlines","email":"bridgetgallegos@songlines.com","city":"Linganore","state":"WA"} {"index":{"_id":"640"}} {"account_number":640,"balance":35596,"firstname":"Candace","lastname":"Hancock","age":25,"gender":"M","address":"574 Riverdale Avenue","employer":"Animalia","email":"candacehancock@animalia.com","city":"Blandburg","state":"KY"} {"index":{"_id":"645"}} {"account_number":645,"balance":29362,"firstname":"Edwina","lastname":"Hutchinson","age":26,"gender":"F","address":"892 Pacific Street","employer":"Essensia","email":"edwinahutchinson@essensia.com","city":"Dowling","state":"NE"} {"index":{"_id":"652"}} {"account_number":652,"balance":17363,"firstname":"Bonner","lastname":"Garner","age":26,"gender":"M","address":"219 Grafton Street","employer":"Utarian","email":"bonnergarner@utarian.com","city":"Vandiver","state":"PA"} {"index":{"_id":"657"}} {"account_number":657,"balance":40475,"firstname":"Kathleen","lastname":"Wilder","age":34,"gender":"F","address":"286 Sutter Avenue","employer":"Solgan","email":"kathleenwilder@solgan.com","city":"Graniteville","state":"MI"} {"index":{"_id":"664"}} {"account_number":664,"balance":16163,"firstname":"Hart","lastname":"Mccormick","age":40,"gender":"M","address":"144 Guider Avenue","employer":"Dyno","email":"hartmccormick@dyno.com","city":"Carbonville","state":"ID"} {"index":{"_id":"669"}} {"account_number":669,"balance":16934,"firstname":"Jewel","lastname":"Estrada","age":28,"gender":"M","address":"896 Meeker Avenue","employer":"Zilla","email":"jewelestrada@zilla.com","city":"Goodville","state":"PA"} {"index":{"_id":"671"}} {"account_number":671,"balance":29029,"firstname":"Antoinette","lastname":"Cook","age":34,"gender":"M","address":"375 Cumberland Street","employer":"Harmoney","email":"antoinettecook@harmoney.com","city":"Bergoo","state":"VT"} {"index":{"_id":"676"}} {"account_number":676,"balance":23842,"firstname":"Lisa","lastname":"Dudley","age":34,"gender":"M","address":"506 Vanderveer Street","employer":"Tropoli","email":"lisadudley@tropoli.com","city":"Konterra","state":"NY"} {"index":{"_id":"683"}} {"account_number":683,"balance":4381,"firstname":"Matilda","lastname":"Berger","age":39,"gender":"M","address":"884 Noble Street","employer":"Fibrodyne","email":"matildaberger@fibrodyne.com","city":"Shepardsville","state":"TN"} {"index":{"_id":"688"}} {"account_number":688,"balance":17931,"firstname":"Freeman","lastname":"Zamora","age":22,"gender":"F","address":"114 Herzl Street","employer":"Elemantra","email":"freemanzamora@elemantra.com","city":"Libertytown","state":"NM"} {"index":{"_id":"690"}} {"account_number":690,"balance":18127,"firstname":"Russo","lastname":"Swanson","age":35,"gender":"F","address":"256 Roebling Street","employer":"Zaj","email":"russoswanson@zaj.com","city":"Hoagland","state":"MI"} {"index":{"_id":"695"}} {"account_number":695,"balance":36800,"firstname":"Gonzales","lastname":"Mcfarland","age":26,"gender":"F","address":"647 Louisa Street","employer":"Songbird","email":"gonzalesmcfarland@songbird.com","city":"Crisman","state":"ID"} {"index":{"_id":"703"}} {"account_number":703,"balance":27443,"firstname":"Dona","lastname":"Burton","age":29,"gender":"M","address":"489 Flatlands Avenue","employer":"Cytrex","email":"donaburton@cytrex.com","city":"Reno","state":"VA"} {"index":{"_id":"708"}} {"account_number":708,"balance":34002,"firstname":"May","lastname":"Ortiz","age":28,"gender":"F","address":"244 Chauncey Street","employer":"Syntac","email":"mayortiz@syntac.com","city":"Munjor","state":"ID"} {"index":{"_id":"710"}} {"account_number":710,"balance":33650,"firstname":"Shelton","lastname":"Stark","age":37,"gender":"M","address":"404 Ovington Avenue","employer":"Kraggle","email":"sheltonstark@kraggle.com","city":"Ogema","state":"TN"} {"index":{"_id":"715"}} {"account_number":715,"balance":23734,"firstname":"Tammi","lastname":"Hodge","age":24,"gender":"M","address":"865 Church Lane","employer":"Netur","email":"tammihodge@netur.com","city":"Lacomb","state":"KS"} {"index":{"_id":"722"}} {"account_number":722,"balance":27256,"firstname":"Roberts","lastname":"Beasley","age":34,"gender":"F","address":"305 Kings Hwy","employer":"Quintity","email":"robertsbeasley@quintity.com","city":"Hayden","state":"PA"} {"index":{"_id":"727"}} {"account_number":727,"balance":27263,"firstname":"Natasha","lastname":"Knapp","age":36,"gender":"M","address":"723 Hubbard Street","employer":"Exostream","email":"natashaknapp@exostream.com","city":"Trexlertown","state":"LA"} {"index":{"_id":"734"}} {"account_number":734,"balance":20325,"firstname":"Keri","lastname":"Kinney","age":23,"gender":"M","address":"490 Balfour Place","employer":"Retrotex","email":"kerikinney@retrotex.com","city":"Salunga","state":"PA"} {"index":{"_id":"739"}} {"account_number":739,"balance":39063,"firstname":"Gwen","lastname":"Hardy","age":33,"gender":"F","address":"733 Stuart Street","employer":"Exozent","email":"gwenhardy@exozent.com","city":"Drytown","state":"NY"} {"index":{"_id":"741"}} {"account_number":741,"balance":33074,"firstname":"Nielsen","lastname":"Good","age":22,"gender":"M","address":"404 Norfolk Street","employer":"Kiggle","email":"nielsengood@kiggle.com","city":"Cumberland","state":"WA"} {"index":{"_id":"746"}} {"account_number":746,"balance":15970,"firstname":"Marguerite","lastname":"Wall","age":28,"gender":"F","address":"364 Crosby Avenue","employer":"Aquoavo","email":"margueritewall@aquoavo.com","city":"Jeff","state":"MI"} {"index":{"_id":"753"}} {"account_number":753,"balance":33340,"firstname":"Katina","lastname":"Alford","age":21,"gender":"F","address":"690 Ross Street","employer":"Intrawear","email":"katinaalford@intrawear.com","city":"Grimsley","state":"OK"} {"index":{"_id":"758"}} {"account_number":758,"balance":15739,"firstname":"Berta","lastname":"Short","age":28,"gender":"M","address":"149 Surf Avenue","employer":"Ozean","email":"bertashort@ozean.com","city":"Odessa","state":"UT"} {"index":{"_id":"760"}} {"account_number":760,"balance":40996,"firstname":"Rhea","lastname":"Blair","age":37,"gender":"F","address":"440 Hubbard Place","employer":"Bicol","email":"rheablair@bicol.com","city":"Stockwell","state":"LA"} {"index":{"_id":"765"}} {"account_number":765,"balance":31278,"firstname":"Knowles","lastname":"Cunningham","age":23,"gender":"M","address":"753 Macdougal Street","employer":"Thredz","email":"knowlescunningham@thredz.com","city":"Thomasville","state":"WA"} {"index":{"_id":"772"}} {"account_number":772,"balance":37849,"firstname":"Eloise","lastname":"Sparks","age":21,"gender":"M","address":"608 Willow Street","employer":"Satiance","email":"eloisesparks@satiance.com","city":"Richford","state":"NY"} {"index":{"_id":"777"}} {"account_number":777,"balance":48294,"firstname":"Adkins","lastname":"Mejia","age":32,"gender":"M","address":"186 Oxford Walk","employer":"Datagen","email":"adkinsmejia@datagen.com","city":"Faywood","state":"OK"} {"index":{"_id":"784"}} {"account_number":784,"balance":25291,"firstname":"Mabel","lastname":"Thornton","age":21,"gender":"M","address":"124 Louisiana Avenue","employer":"Zolavo","email":"mabelthornton@zolavo.com","city":"Lynn","state":"AL"} {"index":{"_id":"789"}} {"account_number":789,"balance":8760,"firstname":"Cunningham","lastname":"Kerr","age":27,"gender":"F","address":"154 Sharon Street","employer":"Polarium","email":"cunninghamkerr@polarium.com","city":"Tuskahoma","state":"MS"} {"index":{"_id":"791"}} {"account_number":791,"balance":48249,"firstname":"Janine","lastname":"Huber","age":38,"gender":"F","address":"348 Porter Avenue","employer":"Viocular","email":"janinehuber@viocular.com","city":"Fivepointville","state":"MA"} {"index":{"_id":"796"}} {"account_number":796,"balance":23503,"firstname":"Mona","lastname":"Craft","age":35,"gender":"F","address":"511 Henry Street","employer":"Opticom","email":"monacraft@opticom.com","city":"Websterville","state":"IN"} {"index":{"_id":"804"}} {"account_number":804,"balance":23610,"firstname":"Rojas","lastname":"Oneal","age":27,"gender":"M","address":"669 Sandford Street","employer":"Glukgluk","email":"rojasoneal@glukgluk.com","city":"Wheaton","state":"ME"} {"index":{"_id":"809"}} {"account_number":809,"balance":47812,"firstname":"Christie","lastname":"Strickland","age":30,"gender":"M","address":"346 Bancroft Place","employer":"Anarco","email":"christiestrickland@anarco.com","city":"Baden","state":"NV"} {"index":{"_id":"811"}} {"account_number":811,"balance":26007,"firstname":"Walls","lastname":"Rogers","age":28,"gender":"F","address":"352 Freeman Street","employer":"Geekmosis","email":"wallsrogers@geekmosis.com","city":"Caroleen","state":"NV"} {"index":{"_id":"816"}} {"account_number":816,"balance":9567,"firstname":"Cornelia","lastname":"Lane","age":20,"gender":"F","address":"384 Bainbridge Street","employer":"Sulfax","email":"cornelialane@sulfax.com","city":"Elizaville","state":"MS"} {"index":{"_id":"823"}} {"account_number":823,"balance":48726,"firstname":"Celia","lastname":"Bernard","age":33,"gender":"F","address":"466 Amboy Street","employer":"Mitroc","email":"celiabernard@mitroc.com","city":"Skyland","state":"GA"} {"index":{"_id":"828"}} {"account_number":828,"balance":44890,"firstname":"Blanche","lastname":"Holmes","age":33,"gender":"F","address":"605 Stryker Court","employer":"Motovate","email":"blancheholmes@motovate.com","city":"Loomis","state":"KS"} {"index":{"_id":"830"}} {"account_number":830,"balance":45210,"firstname":"Louella","lastname":"Chan","age":23,"gender":"M","address":"511 Heath Place","employer":"Conferia","email":"louellachan@conferia.com","city":"Brookfield","state":"OK"} {"index":{"_id":"835"}} {"account_number":835,"balance":46558,"firstname":"Glover","lastname":"Rutledge","age":25,"gender":"F","address":"641 Royce Street","employer":"Ginkogene","email":"gloverrutledge@ginkogene.com","city":"Dixonville","state":"VA"} {"index":{"_id":"842"}} {"account_number":842,"balance":49587,"firstname":"Meagan","lastname":"Buckner","age":23,"gender":"F","address":"833 Bushwick Court","employer":"Biospan","email":"meaganbuckner@biospan.com","city":"Craig","state":"TX"} {"index":{"_id":"847"}} {"account_number":847,"balance":8652,"firstname":"Antonia","lastname":"Duncan","age":23,"gender":"M","address":"644 Stryker Street","employer":"Talae","email":"antoniaduncan@talae.com","city":"Dawn","state":"MO"} {"index":{"_id":"854"}} {"account_number":854,"balance":49795,"firstname":"Jimenez","lastname":"Barry","age":25,"gender":"F","address":"603 Cooper Street","employer":"Verton","email":"jimenezbarry@verton.com","city":"Moscow","state":"AL"} {"index":{"_id":"859"}} {"account_number":859,"balance":20734,"firstname":"Beulah","lastname":"Stuart","age":24,"gender":"F","address":"651 Albemarle Terrace","employer":"Hatology","email":"beulahstuart@hatology.com","city":"Waiohinu","state":"RI"} {"index":{"_id":"861"}} {"account_number":861,"balance":44173,"firstname":"Jaime","lastname":"Wilson","age":35,"gender":"M","address":"680 Richardson Street","employer":"Temorak","email":"jaimewilson@temorak.com","city":"Fidelis","state":"FL"} {"index":{"_id":"866"}} {"account_number":866,"balance":45565,"firstname":"Araceli","lastname":"Woodward","age":28,"gender":"M","address":"326 Meadow Street","employer":"Olympix","email":"araceliwoodward@olympix.com","city":"Dana","state":"KS"} {"index":{"_id":"873"}} {"account_number":873,"balance":43931,"firstname":"Tisha","lastname":"Cotton","age":39,"gender":"F","address":"432 Lincoln Road","employer":"Buzzmaker","email":"tishacotton@buzzmaker.com","city":"Bluetown","state":"GA"} {"index":{"_id":"878"}} {"account_number":878,"balance":49159,"firstname":"Battle","lastname":"Blackburn","age":40,"gender":"F","address":"234 Hendrix Street","employer":"Zilphur","email":"battleblackburn@zilphur.com","city":"Wanamie","state":"PA"} {"index":{"_id":"880"}} {"account_number":880,"balance":22575,"firstname":"Christian","lastname":"Myers","age":35,"gender":"M","address":"737 Crown Street","employer":"Combogen","email":"christianmyers@combogen.com","city":"Abrams","state":"OK"} {"index":{"_id":"885"}} {"account_number":885,"balance":31661,"firstname":"Valdez","lastname":"Roberson","age":40,"gender":"F","address":"227 Scholes Street","employer":"Delphide","email":"valdezroberson@delphide.com","city":"Chilton","state":"MT"} {"index":{"_id":"892"}} {"account_number":892,"balance":44974,"firstname":"Hill","lastname":"Hayes","age":29,"gender":"M","address":"721 Dooley Street","employer":"Fuelton","email":"hillhayes@fuelton.com","city":"Orason","state":"MT"} {"index":{"_id":"897"}} {"account_number":897,"balance":45973,"firstname":"Alyson","lastname":"Irwin","age":25,"gender":"M","address":"731 Poplar Street","employer":"Quizka","email":"alysonirwin@quizka.com","city":"Singer","state":"VA"} {"index":{"_id":"900"}} {"account_number":900,"balance":6124,"firstname":"Gonzalez","lastname":"Watson","age":23,"gender":"M","address":"624 Sullivan Street","employer":"Marvane","email":"gonzalezwatson@marvane.com","city":"Wikieup","state":"IL"} {"index":{"_id":"905"}} {"account_number":905,"balance":29438,"firstname":"Schultz","lastname":"Moreno","age":20,"gender":"F","address":"761 Cedar Street","employer":"Paragonia","email":"schultzmoreno@paragonia.com","city":"Glenshaw","state":"SC"} {"index":{"_id":"912"}} {"account_number":912,"balance":13675,"firstname":"Flora","lastname":"Alvarado","age":26,"gender":"M","address":"771 Vandervoort Avenue","employer":"Boilicon","email":"floraalvarado@boilicon.com","city":"Vivian","state":"ID"} {"index":{"_id":"917"}} {"account_number":917,"balance":47782,"firstname":"Parks","lastname":"Hurst","age":24,"gender":"M","address":"933 Cozine Avenue","employer":"Pyramis","email":"parkshurst@pyramis.com","city":"Lindcove","state":"GA"} {"index":{"_id":"924"}} {"account_number":924,"balance":3811,"firstname":"Hilary","lastname":"Leonard","age":24,"gender":"M","address":"235 Hegeman Avenue","employer":"Metroz","email":"hilaryleonard@metroz.com","city":"Roosevelt","state":"ME"} {"index":{"_id":"929"}} {"account_number":929,"balance":34708,"firstname":"Willie","lastname":"Hickman","age":35,"gender":"M","address":"430 Devoe Street","employer":"Apextri","email":"williehickman@apextri.com","city":"Clay","state":"MS"} {"index":{"_id":"931"}} {"account_number":931,"balance":8244,"firstname":"Ingrid","lastname":"Garcia","age":23,"gender":"F","address":"674 Indiana Place","employer":"Balooba","email":"ingridgarcia@balooba.com","city":"Interlochen","state":"AZ"} {"index":{"_id":"936"}} {"account_number":936,"balance":22430,"firstname":"Beth","lastname":"Frye","age":36,"gender":"M","address":"462 Thatford Avenue","employer":"Puria","email":"bethfrye@puria.com","city":"Hiseville","state":"LA"} {"index":{"_id":"943"}} {"account_number":943,"balance":24187,"firstname":"Wagner","lastname":"Griffin","age":23,"gender":"M","address":"489 Ellery Street","employer":"Gazak","email":"wagnergriffin@gazak.com","city":"Lorraine","state":"HI"} {"index":{"_id":"948"}} {"account_number":948,"balance":37074,"firstname":"Sargent","lastname":"Powers","age":40,"gender":"M","address":"532 Fiske Place","employer":"Accuprint","email":"sargentpowers@accuprint.com","city":"Umapine","state":"AK"} {"index":{"_id":"950"}} {"account_number":950,"balance":30916,"firstname":"Sherrie","lastname":"Patel","age":32,"gender":"F","address":"658 Langham Street","employer":"Futurize","email":"sherriepatel@futurize.com","city":"Garfield","state":"OR"} {"index":{"_id":"955"}} {"account_number":955,"balance":41621,"firstname":"Klein","lastname":"Kemp","age":33,"gender":"M","address":"370 Vanderbilt Avenue","employer":"Synkgen","email":"kleinkemp@synkgen.com","city":"Bonanza","state":"FL"} {"index":{"_id":"962"}} {"account_number":962,"balance":32096,"firstname":"Trujillo","lastname":"Wilcox","age":21,"gender":"F","address":"914 Duffield Street","employer":"Extragene","email":"trujillowilcox@extragene.com","city":"Golconda","state":"MA"} {"index":{"_id":"967"}} {"account_number":967,"balance":19161,"firstname":"Carrie","lastname":"Huffman","age":36,"gender":"F","address":"240 Sands Street","employer":"Injoy","email":"carriehuffman@injoy.com","city":"Leroy","state":"CA"} {"index":{"_id":"974"}} {"account_number":974,"balance":38082,"firstname":"Deborah","lastname":"Yang","age":26,"gender":"F","address":"463 Goodwin Place","employer":"Entogrok","email":"deborahyang@entogrok.com","city":"Herald","state":"KY"} {"index":{"_id":"979"}} {"account_number":979,"balance":43130,"firstname":"Vaughn","lastname":"Pittman","age":29,"gender":"M","address":"446 Tompkins Place","employer":"Phormula","email":"vaughnpittman@phormula.com","city":"Fingerville","state":"WI"} {"index":{"_id":"981"}} {"account_number":981,"balance":20278,"firstname":"Nolan","lastname":"Warner","age":29,"gender":"F","address":"753 Channel Avenue","employer":"Interodeo","email":"nolanwarner@interodeo.com","city":"Layhill","state":"MT"} {"index":{"_id":"986"}} {"account_number":986,"balance":35086,"firstname":"Norris","lastname":"Hubbard","age":31,"gender":"M","address":"600 Celeste Court","employer":"Printspan","email":"norrishubbard@printspan.com","city":"Cassel","state":"MI"} {"index":{"_id":"993"}} {"account_number":993,"balance":26487,"firstname":"Campos","lastname":"Olsen","age":37,"gender":"M","address":"873 Covert Street","employer":"Isbol","email":"camposolsen@isbol.com","city":"Glendale","state":"AK"} {"index":{"_id":"998"}} {"account_number":998,"balance":16869,"firstname":"Letha","lastname":"Baker","age":40,"gender":"F","address":"206 Llama Court","employer":"Dognosis","email":"lethabaker@dognosis.com","city":"Dunlo","state":"WV"} {"index":{"_id":"2"}} {"account_number":2,"balance":28838,"firstname":"Roberta","lastname":"Bender","age":22,"gender":"F","address":"560 Kingsway Place","employer":"Chillium","email":"robertabender@chillium.com","city":"Bennett","state":"LA"} {"index":{"_id":"7"}} {"account_number":7,"balance":39121,"firstname":"Levy","lastname":"Richard","age":22,"gender":"M","address":"820 Logan Street","employer":"Teraprene","email":"levyrichard@teraprene.com","city":"Shrewsbury","state":"MO"} {"index":{"_id":"14"}} {"account_number":14,"balance":20480,"firstname":"Erma","lastname":"Kane","age":39,"gender":"F","address":"661 Vista Place","employer":"Stockpost","email":"ermakane@stockpost.com","city":"Chamizal","state":"NY"} {"index":{"_id":"19"}} {"account_number":19,"balance":27894,"firstname":"Schwartz","lastname":"Buchanan","age":28,"gender":"F","address":"449 Mersereau Court","employer":"Sybixtex","email":"schwartzbuchanan@sybixtex.com","city":"Greenwich","state":"KS"} {"index":{"_id":"21"}} {"account_number":21,"balance":7004,"firstname":"Estella","lastname":"Paul","age":38,"gender":"M","address":"859 Portal Street","employer":"Zillatide","email":"estellapaul@zillatide.com","city":"Churchill","state":"WV"} {"index":{"_id":"26"}} {"account_number":26,"balance":14127,"firstname":"Lorraine","lastname":"Mccullough","age":39,"gender":"F","address":"157 Dupont Street","employer":"Zosis","email":"lorrainemccullough@zosis.com","city":"Dennard","state":"NH"} {"index":{"_id":"33"}} {"account_number":33,"balance":35439,"firstname":"Savannah","lastname":"Kirby","age":30,"gender":"F","address":"372 Malta Street","employer":"Musanpoly","email":"savannahkirby@musanpoly.com","city":"Muse","state":"AK"} {"index":{"_id":"38"}} {"account_number":38,"balance":10511,"firstname":"Erna","lastname":"Fields","age":32,"gender":"M","address":"357 Maple Street","employer":"Eweville","email":"ernafields@eweville.com","city":"Twilight","state":"MS"} {"index":{"_id":"40"}} {"account_number":40,"balance":33882,"firstname":"Pace","lastname":"Molina","age":40,"gender":"M","address":"263 Ovington Court","employer":"Cytrak","email":"pacemolina@cytrak.com","city":"Silkworth","state":"OR"} {"index":{"_id":"45"}} {"account_number":45,"balance":44478,"firstname":"Geneva","lastname":"Morin","age":21,"gender":"F","address":"357 Herkimer Street","employer":"Ezent","email":"genevamorin@ezent.com","city":"Blanco","state":"AZ"} {"index":{"_id":"52"}} {"account_number":52,"balance":46425,"firstname":"Kayla","lastname":"Bradshaw","age":31,"gender":"M","address":"449 Barlow Drive","employer":"Magnemo","email":"kaylabradshaw@magnemo.com","city":"Wawona","state":"AZ"} {"index":{"_id":"57"}} {"account_number":57,"balance":8705,"firstname":"Powell","lastname":"Herring","age":21,"gender":"M","address":"263 Merit Court","employer":"Digiprint","email":"powellherring@digiprint.com","city":"Coral","state":"MT"} {"index":{"_id":"64"}} {"account_number":64,"balance":44036,"firstname":"Miles","lastname":"Battle","age":35,"gender":"F","address":"988 Homecrest Avenue","employer":"Koffee","email":"milesbattle@koffee.com","city":"Motley","state":"ID"} {"index":{"_id":"69"}} {"account_number":69,"balance":14253,"firstname":"Desiree","lastname":"Harrison","age":24,"gender":"M","address":"694 Garland Court","employer":"Barkarama","email":"desireeharrison@barkarama.com","city":"Hackneyville","state":"GA"} {"index":{"_id":"71"}} {"account_number":71,"balance":38201,"firstname":"Sharpe","lastname":"Hoffman","age":39,"gender":"F","address":"450 Conklin Avenue","employer":"Centree","email":"sharpehoffman@centree.com","city":"Urbana","state":"WY"} {"index":{"_id":"76"}} {"account_number":76,"balance":38345,"firstname":"Claudette","lastname":"Beard","age":24,"gender":"F","address":"748 Dorset Street","employer":"Repetwire","email":"claudettebeard@repetwire.com","city":"Caln","state":"TX"} {"index":{"_id":"83"}} {"account_number":83,"balance":35928,"firstname":"Mayo","lastname":"Cleveland","age":28,"gender":"M","address":"720 Brooklyn Road","employer":"Indexia","email":"mayocleveland@indexia.com","city":"Roberts","state":"ND"} {"index":{"_id":"88"}} {"account_number":88,"balance":26418,"firstname":"Adela","lastname":"Tyler","age":21,"gender":"F","address":"737 Clove Road","employer":"Surelogic","email":"adelatyler@surelogic.com","city":"Boling","state":"SD"} {"index":{"_id":"90"}} {"account_number":90,"balance":25332,"firstname":"Herman","lastname":"Snyder","age":22,"gender":"F","address":"737 College Place","employer":"Lunchpod","email":"hermansnyder@lunchpod.com","city":"Flintville","state":"IA"} {"index":{"_id":"95"}} {"account_number":95,"balance":1650,"firstname":"Dominguez","lastname":"Le","age":20,"gender":"M","address":"539 Grace Court","employer":"Portica","email":"dominguezle@portica.com","city":"Wollochet","state":"KS"} {"index":{"_id":"103"}} {"account_number":103,"balance":11253,"firstname":"Calhoun","lastname":"Bruce","age":33,"gender":"F","address":"731 Clarkson Avenue","employer":"Automon","email":"calhounbruce@automon.com","city":"Marienthal","state":"IL"} {"index":{"_id":"108"}} {"account_number":108,"balance":19015,"firstname":"Christensen","lastname":"Weaver","age":21,"gender":"M","address":"398 Dearborn Court","employer":"Quilk","email":"christensenweaver@quilk.com","city":"Belvoir","state":"TX"} {"index":{"_id":"110"}} {"account_number":110,"balance":4850,"firstname":"Daphne","lastname":"Byrd","age":23,"gender":"F","address":"239 Conover Street","employer":"Freakin","email":"daphnebyrd@freakin.com","city":"Taft","state":"MN"} {"index":{"_id":"115"}} {"account_number":115,"balance":18750,"firstname":"Nikki","lastname":"Doyle","age":31,"gender":"F","address":"537 Clara Street","employer":"Fossiel","email":"nikkidoyle@fossiel.com","city":"Caron","state":"MS"} {"index":{"_id":"122"}} {"account_number":122,"balance":17128,"firstname":"Aurora","lastname":"Fry","age":31,"gender":"F","address":"227 Knapp Street","employer":"Makingway","email":"aurorafry@makingway.com","city":"Maybell","state":"NE"} {"index":{"_id":"127"}} {"account_number":127,"balance":48734,"firstname":"Diann","lastname":"Mclaughlin","age":33,"gender":"F","address":"340 Clermont Avenue","employer":"Enomen","email":"diannmclaughlin@enomen.com","city":"Rutherford","state":"ND"} {"index":{"_id":"134"}} {"account_number":134,"balance":33829,"firstname":"Madelyn","lastname":"Norris","age":30,"gender":"F","address":"176 Noel Avenue","employer":"Endicil","email":"madelynnorris@endicil.com","city":"Walker","state":"NE"} {"index":{"_id":"139"}} {"account_number":139,"balance":18444,"firstname":"Rios","lastname":"Todd","age":35,"gender":"F","address":"281 Georgia Avenue","employer":"Uberlux","email":"riostodd@uberlux.com","city":"Hannasville","state":"PA"} {"index":{"_id":"141"}} {"account_number":141,"balance":20790,"firstname":"Liliana","lastname":"Caldwell","age":29,"gender":"M","address":"414 Huron Street","employer":"Rubadub","email":"lilianacaldwell@rubadub.com","city":"Hiwasse","state":"OK"} {"index":{"_id":"146"}} {"account_number":146,"balance":39078,"firstname":"Lang","lastname":"Kaufman","age":32,"gender":"F","address":"626 Beverley Road","employer":"Rodeomad","email":"langkaufman@rodeomad.com","city":"Mahtowa","state":"RI"} {"index":{"_id":"153"}} {"account_number":153,"balance":32074,"firstname":"Bird","lastname":"Cochran","age":31,"gender":"F","address":"691 Bokee Court","employer":"Supremia","email":"birdcochran@supremia.com","city":"Barrelville","state":"NE"} {"index":{"_id":"158"}} {"account_number":158,"balance":9380,"firstname":"Natalie","lastname":"Mcdowell","age":27,"gender":"M","address":"953 Roder Avenue","employer":"Myopium","email":"nataliemcdowell@myopium.com","city":"Savage","state":"ND"} {"index":{"_id":"160"}} {"account_number":160,"balance":48974,"firstname":"Hull","lastname":"Cherry","age":23,"gender":"F","address":"275 Beaumont Street","employer":"Noralex","email":"hullcherry@noralex.com","city":"Whipholt","state":"WA"} {"index":{"_id":"165"}} {"account_number":165,"balance":18956,"firstname":"Sims","lastname":"Mckay","age":40,"gender":"F","address":"205 Jackson Street","employer":"Comtour","email":"simsmckay@comtour.com","city":"Tilden","state":"DC"} {"index":{"_id":"172"}} {"account_number":172,"balance":18356,"firstname":"Marie","lastname":"Whitehead","age":20,"gender":"M","address":"704 Monaco Place","employer":"Sultrax","email":"mariewhitehead@sultrax.com","city":"Dragoon","state":"IL"} {"index":{"_id":"177"}} {"account_number":177,"balance":48972,"firstname":"Harris","lastname":"Gross","age":40,"gender":"F","address":"468 Suydam Street","employer":"Kidstock","email":"harrisgross@kidstock.com","city":"Yettem","state":"KY"} {"index":{"_id":"184"}} {"account_number":184,"balance":9157,"firstname":"Cathy","lastname":"Morrison","age":27,"gender":"M","address":"882 Pine Street","employer":"Zytrek","email":"cathymorrison@zytrek.com","city":"Fedora","state":"FL"} {"index":{"_id":"189"}} {"account_number":189,"balance":20167,"firstname":"Ada","lastname":"Cortez","age":38,"gender":"F","address":"700 Forest Place","employer":"Micronaut","email":"adacortez@micronaut.com","city":"Eagletown","state":"TX"} {"index":{"_id":"191"}} {"account_number":191,"balance":26172,"firstname":"Barr","lastname":"Sharpe","age":28,"gender":"M","address":"428 Auburn Place","employer":"Ziggles","email":"barrsharpe@ziggles.com","city":"Springdale","state":"KS"} {"index":{"_id":"196"}} {"account_number":196,"balance":29931,"firstname":"Caldwell","lastname":"Daniel","age":28,"gender":"F","address":"405 Oliver Street","employer":"Furnigeer","email":"caldwelldaniel@furnigeer.com","city":"Zortman","state":"NE"} {"index":{"_id":"204"}} {"account_number":204,"balance":27714,"firstname":"Mavis","lastname":"Deleon","age":39,"gender":"F","address":"400 Waldane Court","employer":"Lotron","email":"mavisdeleon@lotron.com","city":"Stollings","state":"LA"} {"index":{"_id":"209"}} {"account_number":209,"balance":31052,"firstname":"Myers","lastname":"Noel","age":30,"gender":"F","address":"691 Alton Place","employer":"Greeker","email":"myersnoel@greeker.com","city":"Hinsdale","state":"KY"} {"index":{"_id":"211"}} {"account_number":211,"balance":21539,"firstname":"Graciela","lastname":"Vaughan","age":22,"gender":"M","address":"558 Montauk Court","employer":"Fishland","email":"gracielavaughan@fishland.com","city":"Madrid","state":"PA"} {"index":{"_id":"216"}} {"account_number":216,"balance":11422,"firstname":"Price","lastname":"Haley","age":35,"gender":"M","address":"233 Portland Avenue","employer":"Zeam","email":"pricehaley@zeam.com","city":"Titanic","state":"UT"} {"index":{"_id":"223"}} {"account_number":223,"balance":9528,"firstname":"Newton","lastname":"Fletcher","age":26,"gender":"F","address":"654 Dewitt Avenue","employer":"Assistia","email":"newtonfletcher@assistia.com","city":"Nipinnawasee","state":"AK"} {"index":{"_id":"228"}} {"account_number":228,"balance":10543,"firstname":"Rosella","lastname":"Albert","age":20,"gender":"M","address":"185 Gotham Avenue","employer":"Isoplex","email":"rosellaalbert@isoplex.com","city":"Finzel","state":"NY"} {"index":{"_id":"230"}} {"account_number":230,"balance":10829,"firstname":"Chris","lastname":"Raymond","age":28,"gender":"F","address":"464 Remsen Street","employer":"Cogentry","email":"chrisraymond@cogentry.com","city":"Bowmansville","state":"SD"} {"index":{"_id":"235"}} {"account_number":235,"balance":17729,"firstname":"Mcpherson","lastname":"Mueller","age":31,"gender":"M","address":"541 Strong Place","employer":"Tingles","email":"mcphersonmueller@tingles.com","city":"Brantleyville","state":"AR"} {"index":{"_id":"242"}} {"account_number":242,"balance":42318,"firstname":"Berger","lastname":"Roach","age":21,"gender":"M","address":"125 Wakeman Place","employer":"Ovium","email":"bergerroach@ovium.com","city":"Hessville","state":"WI"} {"index":{"_id":"247"}} {"account_number":247,"balance":45123,"firstname":"Mccormick","lastname":"Moon","age":37,"gender":"M","address":"582 Brighton Avenue","employer":"Norsup","email":"mccormickmoon@norsup.com","city":"Forestburg","state":"DE"} {"index":{"_id":"254"}} {"account_number":254,"balance":35104,"firstname":"Yang","lastname":"Dodson","age":21,"gender":"M","address":"531 Lott Street","employer":"Mondicil","email":"yangdodson@mondicil.com","city":"Enoree","state":"UT"} {"index":{"_id":"259"}} {"account_number":259,"balance":41877,"firstname":"Eleanor","lastname":"Gonzalez","age":30,"gender":"M","address":"800 Sumpter Street","employer":"Futuris","email":"eleanorgonzalez@futuris.com","city":"Jenkinsville","state":"ID"} {"index":{"_id":"261"}} {"account_number":261,"balance":39998,"firstname":"Millicent","lastname":"Pickett","age":34,"gender":"F","address":"722 Montieth Street","employer":"Gushkool","email":"millicentpickett@gushkool.com","city":"Norwood","state":"MS"} {"index":{"_id":"266"}} {"account_number":266,"balance":2777,"firstname":"Monique","lastname":"Conner","age":35,"gender":"F","address":"489 Metrotech Courtr","employer":"Flotonic","email":"moniqueconner@flotonic.com","city":"Retsof","state":"MD"} {"index":{"_id":"273"}} {"account_number":273,"balance":11181,"firstname":"Murphy","lastname":"Chandler","age":20,"gender":"F","address":"569 Bradford Street","employer":"Zilch","email":"murphychandler@zilch.com","city":"Vicksburg","state":"FL"} {"index":{"_id":"278"}} {"account_number":278,"balance":22530,"firstname":"Tamra","lastname":"Navarro","age":27,"gender":"F","address":"175 Woodruff Avenue","employer":"Norsul","email":"tamranavarro@norsul.com","city":"Glasgow","state":"VT"} {"index":{"_id":"280"}} {"account_number":280,"balance":3380,"firstname":"Vilma","lastname":"Shields","age":26,"gender":"F","address":"133 Berriman Street","employer":"Applidec","email":"vilmashields@applidec.com","city":"Adamstown","state":"ME"} {"index":{"_id":"285"}} {"account_number":285,"balance":47369,"firstname":"Hilda","lastname":"Phillips","age":28,"gender":"F","address":"618 Nixon Court","employer":"Comcur","email":"hildaphillips@comcur.com","city":"Siglerville","state":"NC"} {"index":{"_id":"292"}} {"account_number":292,"balance":26679,"firstname":"Morrow","lastname":"Greene","age":20,"gender":"F","address":"691 Nassau Street","employer":"Columella","email":"morrowgreene@columella.com","city":"Sanborn","state":"FL"} {"index":{"_id":"297"}} {"account_number":297,"balance":20508,"firstname":"Tucker","lastname":"Patrick","age":35,"gender":"F","address":"978 Whitwell Place","employer":"Valreda","email":"tuckerpatrick@valreda.com","city":"Deseret","state":"CO"} {"index":{"_id":"300"}} {"account_number":300,"balance":25654,"firstname":"Lane","lastname":"Tate","age":26,"gender":"F","address":"632 Kay Court","employer":"Genesynk","email":"lanetate@genesynk.com","city":"Lowell","state":"MO"} {"index":{"_id":"305"}} {"account_number":305,"balance":11655,"firstname":"Augusta","lastname":"Winters","age":29,"gender":"F","address":"377 Paerdegat Avenue","employer":"Vendblend","email":"augustawinters@vendblend.com","city":"Gwynn","state":"MA"} {"index":{"_id":"312"}} {"account_number":312,"balance":8511,"firstname":"Burgess","lastname":"Gentry","age":25,"gender":"F","address":"382 Bergen Court","employer":"Orbixtar","email":"burgessgentry@orbixtar.com","city":"Conestoga","state":"WI"} {"index":{"_id":"317"}} {"account_number":317,"balance":31968,"firstname":"Ruiz","lastname":"Morris","age":31,"gender":"F","address":"972 Dean Street","employer":"Apex","email":"ruizmorris@apex.com","city":"Jacksonwald","state":"WV"} {"index":{"_id":"324"}} {"account_number":324,"balance":44976,"firstname":"Gladys","lastname":"Erickson","age":22,"gender":"M","address":"250 Battery Avenue","employer":"Eternis","email":"gladyserickson@eternis.com","city":"Marne","state":"IA"} {"index":{"_id":"329"}} {"account_number":329,"balance":31138,"firstname":"Nellie","lastname":"Mercer","age":25,"gender":"M","address":"967 Ebony Court","employer":"Scenty","email":"nelliemercer@scenty.com","city":"Jardine","state":"AK"} {"index":{"_id":"331"}} {"account_number":331,"balance":46004,"firstname":"Gibson","lastname":"Potts","age":34,"gender":"F","address":"994 Dahill Road","employer":"Zensus","email":"gibsonpotts@zensus.com","city":"Frizzleburg","state":"CO"} {"index":{"_id":"336"}} {"account_number":336,"balance":40891,"firstname":"Dudley","lastname":"Avery","age":25,"gender":"M","address":"405 Powers Street","employer":"Genmom","email":"dudleyavery@genmom.com","city":"Clarksburg","state":"CO"} {"index":{"_id":"343"}} {"account_number":343,"balance":37684,"firstname":"Robbie","lastname":"Logan","age":29,"gender":"M","address":"488 Linden Boulevard","employer":"Hydrocom","email":"robbielogan@hydrocom.com","city":"Stockdale","state":"TN"} {"index":{"_id":"348"}} {"account_number":348,"balance":1360,"firstname":"Karina","lastname":"Russell","age":37,"gender":"M","address":"797 Moffat Street","employer":"Limozen","email":"karinarussell@limozen.com","city":"Riegelwood","state":"RI"} {"index":{"_id":"350"}} {"account_number":350,"balance":4267,"firstname":"Wyatt","lastname":"Wise","age":22,"gender":"F","address":"896 Bleecker Street","employer":"Rockyard","email":"wyattwise@rockyard.com","city":"Joes","state":"MS"} {"index":{"_id":"355"}} {"account_number":355,"balance":40961,"firstname":"Gregory","lastname":"Delacruz","age":38,"gender":"M","address":"876 Cortelyou Road","employer":"Oulu","email":"gregorydelacruz@oulu.com","city":"Waterloo","state":"WV"} {"index":{"_id":"362"}} {"account_number":362,"balance":14938,"firstname":"Jimmie","lastname":"Dejesus","age":26,"gender":"M","address":"351 Navy Walk","employer":"Ecolight","email":"jimmiedejesus@ecolight.com","city":"Berlin","state":"ME"} {"index":{"_id":"367"}} {"account_number":367,"balance":40458,"firstname":"Elaine","lastname":"Workman","age":20,"gender":"M","address":"188 Ridge Boulevard","employer":"Colaire","email":"elaineworkman@colaire.com","city":"Herbster","state":"AK"} {"index":{"_id":"374"}} {"account_number":374,"balance":19521,"firstname":"Blanchard","lastname":"Stein","age":30,"gender":"M","address":"313 Bartlett Street","employer":"Cujo","email":"blanchardstein@cujo.com","city":"Cascades","state":"OR"} {"index":{"_id":"379"}} {"account_number":379,"balance":12962,"firstname":"Ruthie","lastname":"Lamb","age":21,"gender":"M","address":"796 Rockaway Avenue","employer":"Incubus","email":"ruthielamb@incubus.com","city":"Hickory","state":"TX"} {"index":{"_id":"381"}} {"account_number":381,"balance":40978,"firstname":"Sophie","lastname":"Mays","age":31,"gender":"M","address":"261 Varanda Place","employer":"Uneeq","email":"sophiemays@uneeq.com","city":"Cressey","state":"AR"} {"index":{"_id":"386"}} {"account_number":386,"balance":42588,"firstname":"Wallace","lastname":"Barr","age":39,"gender":"F","address":"246 Beverly Road","employer":"Concility","email":"wallacebarr@concility.com","city":"Durham","state":"IN"} {"index":{"_id":"393"}} {"account_number":393,"balance":43936,"firstname":"William","lastname":"Kelly","age":24,"gender":"M","address":"178 Lawrence Avenue","employer":"Techtrix","email":"williamkelly@techtrix.com","city":"Orin","state":"PA"} {"index":{"_id":"398"}} {"account_number":398,"balance":8543,"firstname":"Leticia","lastname":"Duran","age":35,"gender":"F","address":"305 Senator Street","employer":"Xleen","email":"leticiaduran@xleen.com","city":"Cavalero","state":"PA"} {"index":{"_id":"401"}} {"account_number":401,"balance":29408,"firstname":"Contreras","lastname":"Randolph","age":38,"gender":"M","address":"104 Lewis Avenue","employer":"Inrt","email":"contrerasrandolph@inrt.com","city":"Chesapeake","state":"CT"} {"index":{"_id":"406"}} {"account_number":406,"balance":28127,"firstname":"Mccarthy","lastname":"Dunlap","age":28,"gender":"F","address":"684 Seacoast Terrace","employer":"Canopoly","email":"mccarthydunlap@canopoly.com","city":"Elliott","state":"NC"} {"index":{"_id":"413"}} {"account_number":413,"balance":15631,"firstname":"Pugh","lastname":"Hamilton","age":39,"gender":"F","address":"124 Euclid Avenue","employer":"Techade","email":"pughhamilton@techade.com","city":"Beaulieu","state":"CA"} {"index":{"_id":"418"}} {"account_number":418,"balance":10207,"firstname":"Reed","lastname":"Goff","age":32,"gender":"M","address":"959 Everit Street","employer":"Zillan","email":"reedgoff@zillan.com","city":"Hiko","state":"WV"} {"index":{"_id":"420"}} {"account_number":420,"balance":44699,"firstname":"Brandie","lastname":"Hayden","age":22,"gender":"M","address":"291 Ash Street","employer":"Digifad","email":"brandiehayden@digifad.com","city":"Spelter","state":"NM"} {"index":{"_id":"425"}} {"account_number":425,"balance":41308,"firstname":"Queen","lastname":"Leach","age":30,"gender":"M","address":"105 Fair Street","employer":"Magneato","email":"queenleach@magneato.com","city":"Barronett","state":"NH"} {"index":{"_id":"432"}} {"account_number":432,"balance":28969,"firstname":"Preston","lastname":"Ferguson","age":40,"gender":"F","address":"239 Greenwood Avenue","employer":"Bitendrex","email":"prestonferguson@bitendrex.com","city":"Idledale","state":"ND"} {"index":{"_id":"437"}} {"account_number":437,"balance":41225,"firstname":"Rosales","lastname":"Marquez","age":29,"gender":"M","address":"873 Ryerson Street","employer":"Ronelon","email":"rosalesmarquez@ronelon.com","city":"Allendale","state":"CA"} {"index":{"_id":"444"}} {"account_number":444,"balance":44219,"firstname":"Dolly","lastname":"Finch","age":24,"gender":"F","address":"974 Interborough Parkway","employer":"Zytrac","email":"dollyfinch@zytrac.com","city":"Vowinckel","state":"WY"} {"index":{"_id":"449"}} {"account_number":449,"balance":41950,"firstname":"Barnett","lastname":"Cantrell","age":39,"gender":"F","address":"945 Bedell Lane","employer":"Zentility","email":"barnettcantrell@zentility.com","city":"Swartzville","state":"ND"} {"index":{"_id":"451"}} {"account_number":451,"balance":31950,"firstname":"Mason","lastname":"Mcleod","age":31,"gender":"F","address":"438 Havemeyer Street","employer":"Omatom","email":"masonmcleod@omatom.com","city":"Ryderwood","state":"NE"} {"index":{"_id":"456"}} {"account_number":456,"balance":21419,"firstname":"Solis","lastname":"Kline","age":33,"gender":"M","address":"818 Ashford Street","employer":"Vetron","email":"soliskline@vetron.com","city":"Ruffin","state":"NY"} {"index":{"_id":"463"}} {"account_number":463,"balance":36672,"firstname":"Heidi","lastname":"Acosta","age":20,"gender":"F","address":"692 Kenmore Terrace","employer":"Elpro","email":"heidiacosta@elpro.com","city":"Ezel","state":"SD"} {"index":{"_id":"468"}} {"account_number":468,"balance":18400,"firstname":"Foreman","lastname":"Fowler","age":40,"gender":"M","address":"443 Jackson Court","employer":"Zillactic","email":"foremanfowler@zillactic.com","city":"Wakarusa","state":"WA"} {"index":{"_id":"470"}} {"account_number":470,"balance":20455,"firstname":"Schneider","lastname":"Hull","age":35,"gender":"M","address":"724 Apollo Street","employer":"Exospeed","email":"schneiderhull@exospeed.com","city":"Watchtower","state":"ID"} {"index":{"_id":"475"}} {"account_number":475,"balance":24427,"firstname":"Morales","lastname":"Jacobs","age":22,"gender":"F","address":"225 Desmond Court","employer":"Oronoko","email":"moralesjacobs@oronoko.com","city":"Clayville","state":"CT"} {"index":{"_id":"482"}} {"account_number":482,"balance":14834,"firstname":"Janie","lastname":"Bass","age":39,"gender":"M","address":"781 Grattan Street","employer":"Manglo","email":"janiebass@manglo.com","city":"Kenwood","state":"IA"} {"index":{"_id":"487"}} {"account_number":487,"balance":30718,"firstname":"Sawyer","lastname":"Vincent","age":26,"gender":"F","address":"238 Lancaster Avenue","employer":"Brainquil","email":"sawyervincent@brainquil.com","city":"Galesville","state":"MS"} {"index":{"_id":"494"}} {"account_number":494,"balance":3592,"firstname":"Holden","lastname":"Bowen","age":30,"gender":"M","address":"374 Elmwood Avenue","employer":"Endipine","email":"holdenbowen@endipine.com","city":"Rosine","state":"ID"} {"index":{"_id":"499"}} {"account_number":499,"balance":26060,"firstname":"Lara","lastname":"Perkins","age":26,"gender":"M","address":"703 Monroe Street","employer":"Paprikut","email":"laraperkins@paprikut.com","city":"Barstow","state":"NY"} {"index":{"_id":"502"}} {"account_number":502,"balance":31898,"firstname":"Woodard","lastname":"Bailey","age":31,"gender":"F","address":"585 Albee Square","employer":"Imperium","email":"woodardbailey@imperium.com","city":"Matheny","state":"MT"} {"index":{"_id":"507"}} {"account_number":507,"balance":27675,"firstname":"Blankenship","lastname":"Ramirez","age":31,"gender":"M","address":"630 Graham Avenue","employer":"Bytrex","email":"blankenshipramirez@bytrex.com","city":"Bancroft","state":"CT"} {"index":{"_id":"514"}} {"account_number":514,"balance":30125,"firstname":"Solomon","lastname":"Bush","age":34,"gender":"M","address":"409 Harkness Avenue","employer":"Snacktion","email":"solomonbush@snacktion.com","city":"Grayhawk","state":"TX"} {"index":{"_id":"519"}} {"account_number":519,"balance":3282,"firstname":"Lorna","lastname":"Franco","age":31,"gender":"F","address":"722 Schenck Court","employer":"Zentia","email":"lornafranco@zentia.com","city":"National","state":"FL"} {"index":{"_id":"521"}} {"account_number":521,"balance":16348,"firstname":"Josefa","lastname":"Buckley","age":34,"gender":"F","address":"848 Taylor Street","employer":"Mazuda","email":"josefabuckley@mazuda.com","city":"Saranap","state":"NM"} {"index":{"_id":"526"}} {"account_number":526,"balance":35375,"firstname":"Sweeney","lastname":"Fulton","age":33,"gender":"F","address":"550 Martense Street","employer":"Cormoran","email":"sweeneyfulton@cormoran.com","city":"Chalfant","state":"IA"} {"index":{"_id":"533"}} {"account_number":533,"balance":13761,"firstname":"Margarita","lastname":"Diaz","age":23,"gender":"M","address":"295 Tapscott Street","employer":"Zilodyne","email":"margaritadiaz@zilodyne.com","city":"Hondah","state":"ID"} {"index":{"_id":"538"}} {"account_number":538,"balance":16416,"firstname":"Koch","lastname":"Barker","age":21,"gender":"M","address":"919 Gerry Street","employer":"Xplor","email":"kochbarker@xplor.com","city":"Dixie","state":"WY"} {"index":{"_id":"540"}} {"account_number":540,"balance":40235,"firstname":"Tammy","lastname":"Wiggins","age":32,"gender":"F","address":"186 Schenectady Avenue","employer":"Speedbolt","email":"tammywiggins@speedbolt.com","city":"Salvo","state":"LA"} {"index":{"_id":"545"}} {"account_number":545,"balance":27011,"firstname":"Lena","lastname":"Lucas","age":20,"gender":"M","address":"110 Lamont Court","employer":"Kindaloo","email":"lenalucas@kindaloo.com","city":"Harleigh","state":"KY"} {"index":{"_id":"552"}} {"account_number":552,"balance":14727,"firstname":"Kate","lastname":"Estes","age":39,"gender":"M","address":"785 Willmohr Street","employer":"Rodeocean","email":"kateestes@rodeocean.com","city":"Elfrida","state":"HI"} {"index":{"_id":"557"}} {"account_number":557,"balance":3119,"firstname":"Landry","lastname":"Buck","age":20,"gender":"M","address":"558 Schweikerts Walk","employer":"Protodyne","email":"landrybuck@protodyne.com","city":"Edneyville","state":"AL"} {"index":{"_id":"564"}} {"account_number":564,"balance":43631,"firstname":"Owens","lastname":"Bowers","age":22,"gender":"M","address":"842 Congress Street","employer":"Nspire","email":"owensbowers@nspire.com","city":"Machias","state":"VA"} {"index":{"_id":"569"}} {"account_number":569,"balance":40019,"firstname":"Sherri","lastname":"Rowe","age":39,"gender":"F","address":"591 Arlington Place","employer":"Netility","email":"sherrirowe@netility.com","city":"Bridgetown","state":"SC"} {"index":{"_id":"571"}} {"account_number":571,"balance":3014,"firstname":"Ayers","lastname":"Duffy","age":28,"gender":"F","address":"721 Wortman Avenue","employer":"Aquasseur","email":"ayersduffy@aquasseur.com","city":"Tilleda","state":"MS"} {"index":{"_id":"576"}} {"account_number":576,"balance":29682,"firstname":"Helena","lastname":"Robertson","age":33,"gender":"F","address":"774 Devon Avenue","employer":"Vicon","email":"helenarobertson@vicon.com","city":"Dyckesville","state":"NV"} {"index":{"_id":"583"}} {"account_number":583,"balance":26558,"firstname":"Castro","lastname":"West","age":34,"gender":"F","address":"814 Williams Avenue","employer":"Cipromox","email":"castrowest@cipromox.com","city":"Nescatunga","state":"IL"} {"index":{"_id":"588"}} {"account_number":588,"balance":43531,"firstname":"Martina","lastname":"Collins","age":31,"gender":"M","address":"301 Anna Court","employer":"Geekwagon","email":"martinacollins@geekwagon.com","city":"Oneida","state":"VA"} {"index":{"_id":"590"}} {"account_number":590,"balance":4652,"firstname":"Ladonna","lastname":"Tucker","age":31,"gender":"F","address":"162 Kane Place","employer":"Infotrips","email":"ladonnatucker@infotrips.com","city":"Utting","state":"IA"} {"index":{"_id":"595"}} {"account_number":595,"balance":12478,"firstname":"Mccall","lastname":"Britt","age":36,"gender":"F","address":"823 Hill Street","employer":"Cablam","email":"mccallbritt@cablam.com","city":"Vernon","state":"CA"} {"index":{"_id":"603"}} {"account_number":603,"balance":28145,"firstname":"Janette","lastname":"Guzman","age":31,"gender":"F","address":"976 Kingston Avenue","employer":"Splinx","email":"janetteguzman@splinx.com","city":"Boomer","state":"NC"} {"index":{"_id":"608"}} {"account_number":608,"balance":47091,"firstname":"Carey","lastname":"Whitley","age":32,"gender":"F","address":"976 Lawrence Street","employer":"Poshome","email":"careywhitley@poshome.com","city":"Weogufka","state":"NE"} {"index":{"_id":"610"}} {"account_number":610,"balance":40571,"firstname":"Foster","lastname":"Weber","age":24,"gender":"F","address":"323 Rochester Avenue","employer":"Firewax","email":"fosterweber@firewax.com","city":"Winston","state":"NY"} {"index":{"_id":"615"}} {"account_number":615,"balance":28726,"firstname":"Delgado","lastname":"Curry","age":28,"gender":"F","address":"706 Butler Street","employer":"Zoxy","email":"delgadocurry@zoxy.com","city":"Gracey","state":"SD"} {"index":{"_id":"622"}} {"account_number":622,"balance":9661,"firstname":"Paulette","lastname":"Hartman","age":38,"gender":"M","address":"375 Emerald Street","employer":"Locazone","email":"paulettehartman@locazone.com","city":"Canterwood","state":"OH"} {"index":{"_id":"627"}} {"account_number":627,"balance":47546,"firstname":"Crawford","lastname":"Sears","age":37,"gender":"F","address":"686 Eastern Parkway","employer":"Updat","email":"crawfordsears@updat.com","city":"Bison","state":"VT"} {"index":{"_id":"634"}} {"account_number":634,"balance":29805,"firstname":"Deloris","lastname":"Levy","age":38,"gender":"M","address":"838 Foster Avenue","employer":"Homelux","email":"delorislevy@homelux.com","city":"Kempton","state":"PA"} {"index":{"_id":"639"}} {"account_number":639,"balance":28875,"firstname":"Caitlin","lastname":"Clements","age":32,"gender":"F","address":"627 Aster Court","employer":"Bunga","email":"caitlinclements@bunga.com","city":"Cetronia","state":"SC"} {"index":{"_id":"641"}} {"account_number":641,"balance":18345,"firstname":"Sheppard","lastname":"Everett","age":39,"gender":"F","address":"791 Norwood Avenue","employer":"Roboid","email":"sheppardeverett@roboid.com","city":"Selma","state":"AK"} {"index":{"_id":"646"}} {"account_number":646,"balance":15559,"firstname":"Lavonne","lastname":"Reyes","age":31,"gender":"F","address":"983 Newport Street","employer":"Parcoe","email":"lavonnereyes@parcoe.com","city":"Monument","state":"LA"} {"index":{"_id":"653"}} {"account_number":653,"balance":7606,"firstname":"Marcia","lastname":"Bennett","age":33,"gender":"F","address":"455 Bragg Street","employer":"Opticall","email":"marciabennett@opticall.com","city":"Magnolia","state":"NC"} {"index":{"_id":"658"}} {"account_number":658,"balance":10210,"firstname":"Bass","lastname":"Mcconnell","age":32,"gender":"F","address":"274 Ocean Avenue","employer":"Combot","email":"bassmcconnell@combot.com","city":"Beyerville","state":"OH"} {"index":{"_id":"660"}} {"account_number":660,"balance":46427,"firstname":"Moon","lastname":"Wood","age":33,"gender":"F","address":"916 Amersfort Place","employer":"Olucore","email":"moonwood@olucore.com","city":"Como","state":"VA"} {"index":{"_id":"665"}} {"account_number":665,"balance":15215,"firstname":"Britney","lastname":"Young","age":36,"gender":"M","address":"766 Sackman Street","employer":"Geoforma","email":"britneyyoung@geoforma.com","city":"Tuttle","state":"WI"} {"index":{"_id":"672"}} {"account_number":672,"balance":12621,"firstname":"Camille","lastname":"Munoz","age":36,"gender":"F","address":"959 Lewis Place","employer":"Vantage","email":"camillemunoz@vantage.com","city":"Whitmer","state":"IN"} {"index":{"_id":"677"}} {"account_number":677,"balance":8491,"firstname":"Snider","lastname":"Benton","age":26,"gender":"M","address":"827 Evans Street","employer":"Medicroix","email":"sniderbenton@medicroix.com","city":"Kaka","state":"UT"} {"index":{"_id":"684"}} {"account_number":684,"balance":46091,"firstname":"Warren","lastname":"Snow","age":25,"gender":"M","address":"756 Oakland Place","employer":"Bizmatic","email":"warrensnow@bizmatic.com","city":"Hatteras","state":"NE"} {"index":{"_id":"689"}} {"account_number":689,"balance":14985,"firstname":"Ines","lastname":"Chaney","age":28,"gender":"M","address":"137 Dikeman Street","employer":"Zidant","email":"ineschaney@zidant.com","city":"Nettie","state":"DC"} {"index":{"_id":"691"}} {"account_number":691,"balance":10792,"firstname":"Mclean","lastname":"Colon","age":22,"gender":"M","address":"876 Classon Avenue","employer":"Elentrix","email":"mcleancolon@elentrix.com","city":"Unionville","state":"OK"} {"index":{"_id":"696"}} {"account_number":696,"balance":17568,"firstname":"Crane","lastname":"Matthews","age":32,"gender":"F","address":"721 Gerritsen Avenue","employer":"Intradisk","email":"cranematthews@intradisk.com","city":"Brewster","state":"WV"} {"index":{"_id":"704"}} {"account_number":704,"balance":45347,"firstname":"Peters","lastname":"Kent","age":22,"gender":"F","address":"871 Independence Avenue","employer":"Extragen","email":"peterskent@extragen.com","city":"Morriston","state":"CA"} {"index":{"_id":"709"}} {"account_number":709,"balance":11015,"firstname":"Abbott","lastname":"Odom","age":29,"gender":"M","address":"893 Union Street","employer":"Jimbies","email":"abbottodom@jimbies.com","city":"Leeper","state":"NJ"} {"index":{"_id":"711"}} {"account_number":711,"balance":26939,"firstname":"Villarreal","lastname":"Horton","age":35,"gender":"F","address":"861 Creamer Street","employer":"Lexicondo","email":"villarrealhorton@lexicondo.com","city":"Lydia","state":"MS"} {"index":{"_id":"716"}} {"account_number":716,"balance":19789,"firstname":"Paul","lastname":"Mason","age":34,"gender":"F","address":"618 Nichols Avenue","employer":"Slax","email":"paulmason@slax.com","city":"Snowville","state":"OK"} {"index":{"_id":"723"}} {"account_number":723,"balance":16421,"firstname":"Nixon","lastname":"Moran","age":27,"gender":"M","address":"569 Campus Place","employer":"Cuizine","email":"nixonmoran@cuizine.com","city":"Buxton","state":"DC"} {"index":{"_id":"728"}} {"account_number":728,"balance":44818,"firstname":"Conley","lastname":"Preston","age":28,"gender":"M","address":"450 Coventry Road","employer":"Obones","email":"conleypreston@obones.com","city":"Alden","state":"CO"} {"index":{"_id":"730"}} {"account_number":730,"balance":41299,"firstname":"Moore","lastname":"Lee","age":30,"gender":"M","address":"797 Turner Place","employer":"Orbean","email":"moorelee@orbean.com","city":"Highland","state":"DE"} {"index":{"_id":"735"}} {"account_number":735,"balance":3984,"firstname":"Loraine","lastname":"Willis","age":32,"gender":"F","address":"928 Grove Street","employer":"Gadtron","email":"lorainewillis@gadtron.com","city":"Lowgap","state":"NY"} {"index":{"_id":"742"}} {"account_number":742,"balance":24765,"firstname":"Merle","lastname":"Wooten","age":26,"gender":"M","address":"317 Pooles Lane","employer":"Tropolis","email":"merlewooten@tropolis.com","city":"Bentley","state":"ND"} {"index":{"_id":"747"}} {"account_number":747,"balance":16617,"firstname":"Diaz","lastname":"Austin","age":38,"gender":"M","address":"676 Harway Avenue","employer":"Irack","email":"diazaustin@irack.com","city":"Cliff","state":"HI"} {"index":{"_id":"754"}} {"account_number":754,"balance":10779,"firstname":"Jones","lastname":"Vega","age":25,"gender":"F","address":"795 India Street","employer":"Gluid","email":"jonesvega@gluid.com","city":"Tyhee","state":"FL"} {"index":{"_id":"759"}} {"account_number":759,"balance":38007,"firstname":"Rose","lastname":"Carlson","age":27,"gender":"M","address":"987 Navy Street","employer":"Aquasure","email":"rosecarlson@aquasure.com","city":"Carlton","state":"CT"} {"index":{"_id":"761"}} {"account_number":761,"balance":7663,"firstname":"Rae","lastname":"Juarez","age":34,"gender":"F","address":"560 Gilmore Court","employer":"Entropix","email":"raejuarez@entropix.com","city":"Northchase","state":"ID"} {"index":{"_id":"766"}} {"account_number":766,"balance":21957,"firstname":"Thomas","lastname":"Gillespie","age":38,"gender":"M","address":"993 Williams Place","employer":"Octocore","email":"thomasgillespie@octocore.com","city":"Defiance","state":"MS"} {"index":{"_id":"773"}} {"account_number":773,"balance":31126,"firstname":"Liza","lastname":"Coffey","age":36,"gender":"F","address":"540 Bulwer Place","employer":"Assurity","email":"lizacoffey@assurity.com","city":"Gilgo","state":"WV"} {"index":{"_id":"778"}} {"account_number":778,"balance":46007,"firstname":"Underwood","lastname":"Wheeler","age":28,"gender":"M","address":"477 Provost Street","employer":"Decratex","email":"underwoodwheeler@decratex.com","city":"Sardis","state":"ID"} {"index":{"_id":"780"}} {"account_number":780,"balance":4682,"firstname":"Maryanne","lastname":"Hendricks","age":26,"gender":"F","address":"709 Wolcott Street","employer":"Sarasonic","email":"maryannehendricks@sarasonic.com","city":"Santel","state":"NH"} {"index":{"_id":"785"}} {"account_number":785,"balance":25078,"firstname":"Fields","lastname":"Lester","age":29,"gender":"M","address":"808 Chestnut Avenue","employer":"Visualix","email":"fieldslester@visualix.com","city":"Rowe","state":"PA"} {"index":{"_id":"792"}} {"account_number":792,"balance":13109,"firstname":"Becky","lastname":"Jimenez","age":40,"gender":"F","address":"539 Front Street","employer":"Isologia","email":"beckyjimenez@isologia.com","city":"Summertown","state":"MI"} {"index":{"_id":"797"}} {"account_number":797,"balance":6854,"firstname":"Lindsay","lastname":"Mills","age":26,"gender":"F","address":"919 Quay Street","employer":"Zoinage","email":"lindsaymills@zoinage.com","city":"Elliston","state":"VA"} {"index":{"_id":"800"}} {"account_number":800,"balance":26217,"firstname":"Candy","lastname":"Oconnor","age":28,"gender":"M","address":"200 Newel Street","employer":"Radiantix","email":"candyoconnor@radiantix.com","city":"Sandston","state":"OH"} {"index":{"_id":"805"}} {"account_number":805,"balance":18426,"firstname":"Jackson","lastname":"Sampson","age":27,"gender":"F","address":"722 Kenmore Court","employer":"Daido","email":"jacksonsampson@daido.com","city":"Bellamy","state":"ME"} {"index":{"_id":"812"}} {"account_number":812,"balance":42593,"firstname":"Graves","lastname":"Newman","age":32,"gender":"F","address":"916 Joralemon Street","employer":"Ecrater","email":"gravesnewman@ecrater.com","city":"Crown","state":"PA"} {"index":{"_id":"817"}} {"account_number":817,"balance":36582,"firstname":"Padilla","lastname":"Bauer","age":36,"gender":"F","address":"310 Cadman Plaza","employer":"Exoblue","email":"padillabauer@exoblue.com","city":"Ahwahnee","state":"MN"} {"index":{"_id":"824"}} {"account_number":824,"balance":6053,"firstname":"Dyer","lastname":"Henson","age":33,"gender":"M","address":"650 Seaview Avenue","employer":"Nitracyr","email":"dyerhenson@nitracyr.com","city":"Gibsonia","state":"KS"} {"index":{"_id":"829"}} {"account_number":829,"balance":20263,"firstname":"Althea","lastname":"Bell","age":37,"gender":"M","address":"319 Cook Street","employer":"Hyplex","email":"altheabell@hyplex.com","city":"Wadsworth","state":"DC"} {"index":{"_id":"831"}} {"account_number":831,"balance":25375,"firstname":"Wendy","lastname":"Savage","age":37,"gender":"M","address":"421 Veranda Place","employer":"Neurocell","email":"wendysavage@neurocell.com","city":"Fresno","state":"MS"} {"index":{"_id":"836"}} {"account_number":836,"balance":20797,"firstname":"Lloyd","lastname":"Lindsay","age":25,"gender":"F","address":"953 Dinsmore Place","employer":"Suretech","email":"lloydlindsay@suretech.com","city":"Conway","state":"VA"} {"index":{"_id":"843"}} {"account_number":843,"balance":15555,"firstname":"Patricia","lastname":"Barton","age":34,"gender":"F","address":"406 Seabring Street","employer":"Providco","email":"patriciabarton@providco.com","city":"Avoca","state":"RI"} {"index":{"_id":"848"}} {"account_number":848,"balance":15443,"firstname":"Carmella","lastname":"Cash","age":38,"gender":"M","address":"988 Exeter Street","employer":"Bristo","email":"carmellacash@bristo.com","city":"Northridge","state":"ID"} {"index":{"_id":"850"}} {"account_number":850,"balance":6531,"firstname":"Carlene","lastname":"Gaines","age":37,"gender":"F","address":"753 Monroe Place","employer":"Naxdis","email":"carlenegaines@naxdis.com","city":"Genoa","state":"OR"} {"index":{"_id":"855"}} {"account_number":855,"balance":40170,"firstname":"Mia","lastname":"Stevens","age":31,"gender":"F","address":"326 Driggs Avenue","employer":"Aeora","email":"miastevens@aeora.com","city":"Delwood","state":"IL"} {"index":{"_id":"862"}} {"account_number":862,"balance":38792,"firstname":"Clayton","lastname":"Golden","age":38,"gender":"F","address":"620 Regent Place","employer":"Accusage","email":"claytongolden@accusage.com","city":"Ona","state":"NC"} {"index":{"_id":"867"}} {"account_number":867,"balance":45453,"firstname":"Blanca","lastname":"Ellison","age":23,"gender":"F","address":"593 McKibben Street","employer":"Koogle","email":"blancaellison@koogle.com","city":"Frystown","state":"WY"} {"index":{"_id":"874"}} {"account_number":874,"balance":23079,"firstname":"Lynette","lastname":"Higgins","age":22,"gender":"M","address":"377 McKinley Avenue","employer":"Menbrain","email":"lynettehiggins@menbrain.com","city":"Manitou","state":"TX"} {"index":{"_id":"879"}} {"account_number":879,"balance":48332,"firstname":"Sabrina","lastname":"Lancaster","age":31,"gender":"F","address":"382 Oak Street","employer":"Webiotic","email":"sabrinalancaster@webiotic.com","city":"Lindisfarne","state":"AZ"} {"index":{"_id":"881"}} {"account_number":881,"balance":26684,"firstname":"Barnes","lastname":"Ware","age":38,"gender":"F","address":"666 Hooper Street","employer":"Norali","email":"barnesware@norali.com","city":"Cazadero","state":"GA"} {"index":{"_id":"886"}} {"account_number":886,"balance":14867,"firstname":"Willa","lastname":"Leblanc","age":38,"gender":"F","address":"773 Bergen Street","employer":"Nurali","email":"willaleblanc@nurali.com","city":"Hilltop","state":"NC"} {"index":{"_id":"893"}} {"account_number":893,"balance":42584,"firstname":"Moses","lastname":"Campos","age":38,"gender":"F","address":"991 Bevy Court","employer":"Trollery","email":"mosescampos@trollery.com","city":"Freetown","state":"AK"} {"index":{"_id":"898"}} {"account_number":898,"balance":12019,"firstname":"Lori","lastname":"Stevenson","age":29,"gender":"M","address":"910 Coles Street","employer":"Honotron","email":"loristevenson@honotron.com","city":"Shindler","state":"VT"} {"index":{"_id":"901"}} {"account_number":901,"balance":35038,"firstname":"Irma","lastname":"Dotson","age":23,"gender":"F","address":"245 Mayfair Drive","employer":"Bleeko","email":"irmadotson@bleeko.com","city":"Lodoga","state":"UT"} {"index":{"_id":"906"}} {"account_number":906,"balance":24073,"firstname":"Vicki","lastname":"Suarez","age":36,"gender":"M","address":"829 Roosevelt Place","employer":"Utara","email":"vickisuarez@utara.com","city":"Albrightsville","state":"AR"} {"index":{"_id":"913"}} {"account_number":913,"balance":47657,"firstname":"Margery","lastname":"Monroe","age":25,"gender":"M","address":"941 Fanchon Place","employer":"Exerta","email":"margerymonroe@exerta.com","city":"Bannock","state":"MD"} {"index":{"_id":"918"}} {"account_number":918,"balance":36776,"firstname":"Dianna","lastname":"Hernandez","age":25,"gender":"M","address":"499 Moultrie Street","employer":"Isologica","email":"diannahernandez@isologica.com","city":"Falconaire","state":"ID"} {"index":{"_id":"920"}} {"account_number":920,"balance":41513,"firstname":"Jerri","lastname":"Mitchell","age":26,"gender":"M","address":"831 Kent Street","employer":"Tasmania","email":"jerrimitchell@tasmania.com","city":"Cotopaxi","state":"IA"} {"index":{"_id":"925"}} {"account_number":925,"balance":18295,"firstname":"Rosario","lastname":"Jackson","age":24,"gender":"M","address":"178 Leonora Court","employer":"Progenex","email":"rosariojackson@progenex.com","city":"Rivereno","state":"DE"} {"index":{"_id":"932"}} {"account_number":932,"balance":3111,"firstname":"Summer","lastname":"Porter","age":33,"gender":"F","address":"949 Grand Avenue","employer":"Multiflex","email":"summerporter@multiflex.com","city":"Spokane","state":"OK"} {"index":{"_id":"937"}} {"account_number":937,"balance":43491,"firstname":"Selma","lastname":"Anderson","age":24,"gender":"M","address":"205 Reed Street","employer":"Dadabase","email":"selmaanderson@dadabase.com","city":"Malo","state":"AL"} {"index":{"_id":"944"}} {"account_number":944,"balance":46478,"firstname":"Donaldson","lastname":"Woodard","age":38,"gender":"F","address":"498 Laurel Avenue","employer":"Zogak","email":"donaldsonwoodard@zogak.com","city":"Hasty","state":"ID"} {"index":{"_id":"949"}} {"account_number":949,"balance":48703,"firstname":"Latasha","lastname":"Mullins","age":29,"gender":"F","address":"272 Lefferts Place","employer":"Zenolux","email":"latashamullins@zenolux.com","city":"Kieler","state":"MN"} {"index":{"_id":"951"}} {"account_number":951,"balance":36337,"firstname":"Tran","lastname":"Burris","age":25,"gender":"F","address":"561 Rutland Road","employer":"Geoform","email":"tranburris@geoform.com","city":"Longbranch","state":"IL"} {"index":{"_id":"956"}} {"account_number":956,"balance":19477,"firstname":"Randall","lastname":"Lynch","age":22,"gender":"F","address":"490 Madison Place","employer":"Cosmetex","email":"randalllynch@cosmetex.com","city":"Wells","state":"SD"} {"index":{"_id":"963"}} {"account_number":963,"balance":30461,"firstname":"Griffin","lastname":"Sheppard","age":20,"gender":"M","address":"682 Linden Street","employer":"Zanymax","email":"griffinsheppard@zanymax.com","city":"Fannett","state":"NM"} {"index":{"_id":"968"}} {"account_number":968,"balance":32371,"firstname":"Luella","lastname":"Burch","age":39,"gender":"M","address":"684 Arkansas Drive","employer":"Krag","email":"luellaburch@krag.com","city":"Brambleton","state":"SD"} {"index":{"_id":"970"}} {"account_number":970,"balance":19648,"firstname":"Forbes","lastname":"Wallace","age":28,"gender":"M","address":"990 Mill Road","employer":"Pheast","email":"forbeswallace@pheast.com","city":"Lopezo","state":"AK"} {"index":{"_id":"975"}} {"account_number":975,"balance":5239,"firstname":"Delores","lastname":"Booker","age":27,"gender":"F","address":"328 Conselyea Street","employer":"Centice","email":"deloresbooker@centice.com","city":"Williams","state":"HI"} {"index":{"_id":"982"}} {"account_number":982,"balance":16511,"firstname":"Buck","lastname":"Robinson","age":24,"gender":"M","address":"301 Melrose Street","employer":"Calcu","email":"buckrobinson@calcu.com","city":"Welch","state":"PA"} {"index":{"_id":"987"}} {"account_number":987,"balance":4072,"firstname":"Brock","lastname":"Sandoval","age":20,"gender":"F","address":"977 Gem Street","employer":"Fiberox","email":"brocksandoval@fiberox.com","city":"Celeryville","state":"NY"} {"index":{"_id":"994"}} {"account_number":994,"balance":33298,"firstname":"Madge","lastname":"Holcomb","age":31,"gender":"M","address":"612 Hawthorne Street","employer":"Escenta","email":"madgeholcomb@escenta.com","city":"Alafaya","state":"OR"} {"index":{"_id":"999"}} {"account_number":999,"balance":6087,"firstname":"Dorothy","lastname":"Barron","age":22,"gender":"F","address":"499 Laurel Avenue","employer":"Xurban","email":"dorothybarron@xurban.com","city":"Belvoir","state":"CA"} {"index":{"_id":"4"}} {"account_number":4,"balance":27658,"firstname":"Rodriquez","lastname":"Flores","age":31,"gender":"F","address":"986 Wyckoff Avenue","employer":"Tourmania","email":"rodriquezflores@tourmania.com","city":"Eastvale","state":"HI"} {"index":{"_id":"9"}} {"account_number":9,"balance":24776,"firstname":"Opal","lastname":"Meadows","age":39,"gender":"M","address":"963 Neptune Avenue","employer":"Cedward","email":"opalmeadows@cedward.com","city":"Olney","state":"OH"} {"index":{"_id":"11"}} {"account_number":11,"balance":20203,"firstname":"Jenkins","lastname":"Haney","age":20,"gender":"M","address":"740 Ferry Place","employer":"Qimonk","email":"jenkinshaney@qimonk.com","city":"Steinhatchee","state":"GA"} {"index":{"_id":"16"}} {"account_number":16,"balance":35883,"firstname":"Adrian","lastname":"Pitts","age":34,"gender":"F","address":"963 Fay Court","employer":"Combogene","email":"adrianpitts@combogene.com","city":"Remington","state":"SD"} {"index":{"_id":"23"}} {"account_number":23,"balance":42374,"firstname":"Kirsten","lastname":"Fox","age":20,"gender":"M","address":"330 Dumont Avenue","employer":"Codax","email":"kirstenfox@codax.com","city":"Walton","state":"AK"} {"index":{"_id":"28"}} {"account_number":28,"balance":42112,"firstname":"Vega","lastname":"Flynn","age":20,"gender":"M","address":"647 Hyman Court","employer":"Accupharm","email":"vegaflynn@accupharm.com","city":"Masthope","state":"OH"} {"index":{"_id":"30"}} {"account_number":30,"balance":19087,"firstname":"Lamb","lastname":"Townsend","age":26,"gender":"M","address":"169 Lyme Avenue","employer":"Geeknet","email":"lambtownsend@geeknet.com","city":"Epworth","state":"AL"} {"index":{"_id":"35"}} {"account_number":35,"balance":42039,"firstname":"Darla","lastname":"Bridges","age":27,"gender":"F","address":"315 Central Avenue","employer":"Xeronk","email":"darlabridges@xeronk.com","city":"Woodlake","state":"RI"} {"index":{"_id":"42"}} {"account_number":42,"balance":21137,"firstname":"Harding","lastname":"Hobbs","age":26,"gender":"F","address":"474 Ridgewood Place","employer":"Xth","email":"hardinghobbs@xth.com","city":"Heil","state":"ND"} {"index":{"_id":"47"}} {"account_number":47,"balance":33044,"firstname":"Georgia","lastname":"Wilkerson","age":23,"gender":"M","address":"369 Herbert Street","employer":"Endipin","email":"georgiawilkerson@endipin.com","city":"Dellview","state":"WI"} {"index":{"_id":"54"}} {"account_number":54,"balance":23406,"firstname":"Angel","lastname":"Mann","age":22,"gender":"F","address":"229 Ferris Street","employer":"Amtas","email":"angelmann@amtas.com","city":"Calverton","state":"WA"} {"index":{"_id":"59"}} {"account_number":59,"balance":37728,"firstname":"Malone","lastname":"Justice","age":37,"gender":"F","address":"721 Russell Street","employer":"Emoltra","email":"malonejustice@emoltra.com","city":"Trucksville","state":"HI"} {"index":{"_id":"61"}} {"account_number":61,"balance":6856,"firstname":"Shawn","lastname":"Baird","age":20,"gender":"M","address":"605 Monument Walk","employer":"Moltonic","email":"shawnbaird@moltonic.com","city":"Darlington","state":"MN"} {"index":{"_id":"66"}} {"account_number":66,"balance":25939,"firstname":"Franks","lastname":"Salinas","age":28,"gender":"M","address":"437 Hamilton Walk","employer":"Cowtown","email":"frankssalinas@cowtown.com","city":"Chase","state":"VT"} {"index":{"_id":"73"}} {"account_number":73,"balance":33457,"firstname":"Irene","lastname":"Stephenson","age":32,"gender":"M","address":"684 Miller Avenue","employer":"Hawkster","email":"irenestephenson@hawkster.com","city":"Levant","state":"AR"} {"index":{"_id":"78"}} {"account_number":78,"balance":48656,"firstname":"Elvira","lastname":"Patterson","age":23,"gender":"F","address":"834 Amber Street","employer":"Assistix","email":"elvirapatterson@assistix.com","city":"Dunbar","state":"TN"} {"index":{"_id":"80"}} {"account_number":80,"balance":13445,"firstname":"Lacey","lastname":"Blanchard","age":30,"gender":"F","address":"823 Himrod Street","employer":"Comdom","email":"laceyblanchard@comdom.com","city":"Matthews","state":"MO"} {"index":{"_id":"85"}} {"account_number":85,"balance":48735,"firstname":"Wilcox","lastname":"Sellers","age":20,"gender":"M","address":"212 Irving Avenue","employer":"Confrenzy","email":"wilcoxsellers@confrenzy.com","city":"Kipp","state":"MT"} {"index":{"_id":"92"}} {"account_number":92,"balance":26753,"firstname":"Gay","lastname":"Brewer","age":34,"gender":"M","address":"369 Ditmars Street","employer":"Savvy","email":"gaybrewer@savvy.com","city":"Moquino","state":"HI"} {"index":{"_id":"97"}} {"account_number":97,"balance":49671,"firstname":"Karen","lastname":"Trujillo","age":40,"gender":"F","address":"512 Cumberland Walk","employer":"Tsunamia","email":"karentrujillo@tsunamia.com","city":"Fredericktown","state":"MO"} {"index":{"_id":"100"}} {"account_number":100,"balance":29869,"firstname":"Madden","lastname":"Woods","age":32,"gender":"F","address":"696 Ryder Avenue","employer":"Slumberia","email":"maddenwoods@slumberia.com","city":"Deercroft","state":"ME"} {"index":{"_id":"105"}} {"account_number":105,"balance":29654,"firstname":"Castillo","lastname":"Dickerson","age":33,"gender":"F","address":"673 Oxford Street","employer":"Tellifly","email":"castillodickerson@tellifly.com","city":"Succasunna","state":"NY"} {"index":{"_id":"112"}} {"account_number":112,"balance":38395,"firstname":"Frederick","lastname":"Case","age":30,"gender":"F","address":"580 Lexington Avenue","employer":"Talkalot","email":"frederickcase@talkalot.com","city":"Orovada","state":"MA"} {"index":{"_id":"117"}} {"account_number":117,"balance":48831,"firstname":"Robin","lastname":"Hays","age":38,"gender":"F","address":"347 Hornell Loop","employer":"Pasturia","email":"robinhays@pasturia.com","city":"Sims","state":"WY"} {"index":{"_id":"124"}} {"account_number":124,"balance":16425,"firstname":"Fern","lastname":"Lambert","age":20,"gender":"M","address":"511 Jay Street","employer":"Furnitech","email":"fernlambert@furnitech.com","city":"Cloverdale","state":"FL"} {"index":{"_id":"129"}} {"account_number":129,"balance":42409,"firstname":"Alexandria","lastname":"Sanford","age":33,"gender":"F","address":"934 Ridgecrest Terrace","employer":"Kyagoro","email":"alexandriasanford@kyagoro.com","city":"Concho","state":"UT"} {"index":{"_id":"131"}} {"account_number":131,"balance":28030,"firstname":"Dollie","lastname":"Koch","age":22,"gender":"F","address":"287 Manhattan Avenue","employer":"Skinserve","email":"dolliekoch@skinserve.com","city":"Shasta","state":"PA"} {"index":{"_id":"136"}} {"account_number":136,"balance":45801,"firstname":"Winnie","lastname":"Holland","age":38,"gender":"M","address":"198 Mill Lane","employer":"Neteria","email":"winnieholland@neteria.com","city":"Urie","state":"IL"} {"index":{"_id":"143"}} {"account_number":143,"balance":43093,"firstname":"Cohen","lastname":"Noble","age":39,"gender":"M","address":"454 Nelson Street","employer":"Buzzworks","email":"cohennoble@buzzworks.com","city":"Norvelt","state":"CO"} {"index":{"_id":"148"}} {"account_number":148,"balance":3662,"firstname":"Annmarie","lastname":"Snider","age":34,"gender":"F","address":"857 Lafayette Walk","employer":"Edecine","email":"annmariesnider@edecine.com","city":"Hollins","state":"OH"} {"index":{"_id":"150"}} {"account_number":150,"balance":15306,"firstname":"Ortega","lastname":"Dalton","age":20,"gender":"M","address":"237 Mermaid Avenue","employer":"Rameon","email":"ortegadalton@rameon.com","city":"Maxville","state":"NH"} {"index":{"_id":"155"}} {"account_number":155,"balance":27878,"firstname":"Atkinson","lastname":"Hudson","age":39,"gender":"F","address":"434 Colin Place","employer":"Qualitern","email":"atkinsonhudson@qualitern.com","city":"Hoehne","state":"OH"} {"index":{"_id":"162"}} {"account_number":162,"balance":6302,"firstname":"Griffith","lastname":"Calderon","age":35,"gender":"M","address":"871 Vandervoort Place","employer":"Quotezart","email":"griffithcalderon@quotezart.com","city":"Barclay","state":"FL"} {"index":{"_id":"167"}} {"account_number":167,"balance":42051,"firstname":"Hampton","lastname":"Ryan","age":20,"gender":"M","address":"618 Fleet Place","employer":"Zipak","email":"hamptonryan@zipak.com","city":"Irwin","state":"KS"} {"index":{"_id":"174"}} {"account_number":174,"balance":1464,"firstname":"Gamble","lastname":"Pierce","age":23,"gender":"F","address":"650 Eagle Street","employer":"Matrixity","email":"gamblepierce@matrixity.com","city":"Abiquiu","state":"OR"} {"index":{"_id":"179"}} {"account_number":179,"balance":13265,"firstname":"Elise","lastname":"Drake","age":25,"gender":"M","address":"305 Christopher Avenue","employer":"Turnling","email":"elisedrake@turnling.com","city":"Loretto","state":"LA"} {"index":{"_id":"181"}} {"account_number":181,"balance":27983,"firstname":"Bennett","lastname":"Hampton","age":22,"gender":"F","address":"435 Billings Place","employer":"Voipa","email":"bennetthampton@voipa.com","city":"Rodman","state":"WY"} {"index":{"_id":"186"}} {"account_number":186,"balance":18373,"firstname":"Kline","lastname":"Joyce","age":32,"gender":"M","address":"285 Falmouth Street","employer":"Tetratrex","email":"klinejoyce@tetratrex.com","city":"Klondike","state":"SD"} {"index":{"_id":"193"}} {"account_number":193,"balance":13412,"firstname":"Patty","lastname":"Petty","age":34,"gender":"F","address":"251 Vermont Street","employer":"Kinetica","email":"pattypetty@kinetica.com","city":"Grantville","state":"MS"} {"index":{"_id":"198"}} {"account_number":198,"balance":19686,"firstname":"Rachael","lastname":"Sharp","age":38,"gender":"F","address":"443 Vernon Avenue","employer":"Powernet","email":"rachaelsharp@powernet.com","city":"Canoochee","state":"UT"} {"index":{"_id":"201"}} {"account_number":201,"balance":14586,"firstname":"Ronda","lastname":"Perry","age":25,"gender":"F","address":"856 Downing Street","employer":"Artiq","email":"rondaperry@artiq.com","city":"Colton","state":"WV"} {"index":{"_id":"206"}} {"account_number":206,"balance":47423,"firstname":"Kelli","lastname":"Francis","age":20,"gender":"M","address":"671 George Street","employer":"Exoswitch","email":"kellifrancis@exoswitch.com","city":"Babb","state":"NJ"} {"index":{"_id":"213"}} {"account_number":213,"balance":34172,"firstname":"Bauer","lastname":"Summers","age":27,"gender":"M","address":"257 Boynton Place","employer":"Voratak","email":"bauersummers@voratak.com","city":"Oceola","state":"NC"} {"index":{"_id":"218"}} {"account_number":218,"balance":26702,"firstname":"Garrison","lastname":"Bryan","age":24,"gender":"F","address":"478 Greenpoint Avenue","employer":"Uniworld","email":"garrisonbryan@uniworld.com","city":"Comptche","state":"WI"} {"index":{"_id":"220"}} {"account_number":220,"balance":3086,"firstname":"Tania","lastname":"Middleton","age":22,"gender":"F","address":"541 Gunther Place","employer":"Zerology","email":"taniamiddleton@zerology.com","city":"Linwood","state":"IN"} {"index":{"_id":"225"}} {"account_number":225,"balance":21949,"firstname":"Maryann","lastname":"Murphy","age":24,"gender":"F","address":"894 Bridgewater Street","employer":"Cinesanct","email":"maryannmurphy@cinesanct.com","city":"Cartwright","state":"RI"} {"index":{"_id":"232"}} {"account_number":232,"balance":11984,"firstname":"Carr","lastname":"Jensen","age":34,"gender":"F","address":"995 Micieli Place","employer":"Biohab","email":"carrjensen@biohab.com","city":"Waikele","state":"OH"} {"index":{"_id":"237"}} {"account_number":237,"balance":5603,"firstname":"Kirby","lastname":"Watkins","age":27,"gender":"F","address":"348 Blake Court","employer":"Sonique","email":"kirbywatkins@sonique.com","city":"Freelandville","state":"PA"} {"index":{"_id":"244"}} {"account_number":244,"balance":8048,"firstname":"Judith","lastname":"Riggs","age":27,"gender":"F","address":"590 Kosciusko Street","employer":"Arctiq","email":"judithriggs@arctiq.com","city":"Gorham","state":"DC"} {"index":{"_id":"249"}} {"account_number":249,"balance":16822,"firstname":"Mckinney","lastname":"Gallagher","age":38,"gender":"F","address":"939 Seigel Court","employer":"Premiant","email":"mckinneygallagher@premiant.com","city":"Catharine","state":"NH"} {"index":{"_id":"251"}} {"account_number":251,"balance":13475,"firstname":"Marks","lastname":"Graves","age":39,"gender":"F","address":"427 Lawn Court","employer":"Dentrex","email":"marksgraves@dentrex.com","city":"Waukeenah","state":"IL"} {"index":{"_id":"256"}} {"account_number":256,"balance":48318,"firstname":"Simon","lastname":"Hogan","age":31,"gender":"M","address":"789 Suydam Place","employer":"Dancerity","email":"simonhogan@dancerity.com","city":"Dargan","state":"GA"} {"index":{"_id":"263"}} {"account_number":263,"balance":12837,"firstname":"Thornton","lastname":"Meyer","age":29,"gender":"M","address":"575 Elliott Place","employer":"Peticular","email":"thorntonmeyer@peticular.com","city":"Dotsero","state":"NH"} {"index":{"_id":"268"}} {"account_number":268,"balance":20925,"firstname":"Avis","lastname":"Blackwell","age":36,"gender":"M","address":"569 Jerome Avenue","employer":"Magnina","email":"avisblackwell@magnina.com","city":"Bethany","state":"MD"} {"index":{"_id":"270"}} {"account_number":270,"balance":43951,"firstname":"Moody","lastname":"Harmon","age":39,"gender":"F","address":"233 Vanderbilt Street","employer":"Otherside","email":"moodyharmon@otherside.com","city":"Elwood","state":"MT"} {"index":{"_id":"275"}} {"account_number":275,"balance":2384,"firstname":"Reynolds","lastname":"Barnett","age":31,"gender":"M","address":"394 Stockton Street","employer":"Austex","email":"reynoldsbarnett@austex.com","city":"Grandview","state":"MS"} {"index":{"_id":"282"}} {"account_number":282,"balance":38540,"firstname":"Gay","lastname":"Schultz","age":25,"gender":"F","address":"805 Claver Place","employer":"Handshake","email":"gayschultz@handshake.com","city":"Tampico","state":"MA"} {"index":{"_id":"287"}} {"account_number":287,"balance":10845,"firstname":"Valerie","lastname":"Lang","age":35,"gender":"F","address":"423 Midwood Street","employer":"Quarx","email":"valerielang@quarx.com","city":"Cannondale","state":"VT"} {"index":{"_id":"294"}} {"account_number":294,"balance":29582,"firstname":"Pitts","lastname":"Haynes","age":26,"gender":"M","address":"901 Broome Street","employer":"Aquazure","email":"pittshaynes@aquazure.com","city":"Turah","state":"SD"} {"index":{"_id":"299"}} {"account_number":299,"balance":40825,"firstname":"Angela","lastname":"Talley","age":36,"gender":"F","address":"822 Bills Place","employer":"Remold","email":"angelatalley@remold.com","city":"Bethpage","state":"DC"} {"index":{"_id":"302"}} {"account_number":302,"balance":11298,"firstname":"Isabella","lastname":"Hewitt","age":40,"gender":"M","address":"455 Bedford Avenue","employer":"Cincyr","email":"isabellahewitt@cincyr.com","city":"Blanford","state":"IN"} {"index":{"_id":"307"}} {"account_number":307,"balance":43355,"firstname":"Enid","lastname":"Ashley","age":23,"gender":"M","address":"412 Emerson Place","employer":"Avenetro","email":"enidashley@avenetro.com","city":"Catherine","state":"WI"} {"index":{"_id":"314"}} {"account_number":314,"balance":5848,"firstname":"Norton","lastname":"Norton","age":35,"gender":"M","address":"252 Ditmas Avenue","employer":"Talkola","email":"nortonnorton@talkola.com","city":"Veyo","state":"SC"} {"index":{"_id":"319"}} {"account_number":319,"balance":15430,"firstname":"Ferrell","lastname":"Mckinney","age":36,"gender":"M","address":"874 Cranberry Street","employer":"Portaline","email":"ferrellmckinney@portaline.com","city":"Rose","state":"WV"} {"index":{"_id":"321"}} {"account_number":321,"balance":43370,"firstname":"Marta","lastname":"Larsen","age":35,"gender":"M","address":"617 Williams Court","employer":"Manufact","email":"martalarsen@manufact.com","city":"Sisquoc","state":"MA"} {"index":{"_id":"326"}} {"account_number":326,"balance":9692,"firstname":"Pearl","lastname":"Reese","age":30,"gender":"F","address":"451 Colonial Court","employer":"Accruex","email":"pearlreese@accruex.com","city":"Westmoreland","state":"MD"} {"index":{"_id":"333"}} {"account_number":333,"balance":22778,"firstname":"Trudy","lastname":"Sweet","age":27,"gender":"F","address":"881 Kiely Place","employer":"Acumentor","email":"trudysweet@acumentor.com","city":"Kent","state":"IA"} {"index":{"_id":"338"}} {"account_number":338,"balance":6969,"firstname":"Pierce","lastname":"Lawrence","age":35,"gender":"M","address":"318 Gallatin Place","employer":"Lunchpad","email":"piercelawrence@lunchpad.com","city":"Iola","state":"MD"} {"index":{"_id":"340"}} {"account_number":340,"balance":42072,"firstname":"Juarez","lastname":"Gutierrez","age":40,"gender":"F","address":"802 Seba Avenue","employer":"Billmed","email":"juarezgutierrez@billmed.com","city":"Malott","state":"OH"} {"index":{"_id":"345"}} {"account_number":345,"balance":9812,"firstname":"Parker","lastname":"Hines","age":38,"gender":"M","address":"715 Mill Avenue","employer":"Baluba","email":"parkerhines@baluba.com","city":"Blackgum","state":"KY"} {"index":{"_id":"352"}} {"account_number":352,"balance":20290,"firstname":"Kendra","lastname":"Mcintosh","age":31,"gender":"F","address":"963 Wolf Place","employer":"Orboid","email":"kendramcintosh@orboid.com","city":"Bladensburg","state":"AK"} {"index":{"_id":"357"}} {"account_number":357,"balance":15102,"firstname":"Adele","lastname":"Carroll","age":39,"gender":"F","address":"381 Arion Place","employer":"Aquafire","email":"adelecarroll@aquafire.com","city":"Springville","state":"RI"} {"index":{"_id":"364"}} {"account_number":364,"balance":35247,"firstname":"Felicia","lastname":"Merrill","age":40,"gender":"F","address":"229 Branton Street","employer":"Prosely","email":"feliciamerrill@prosely.com","city":"Dola","state":"MA"} {"index":{"_id":"369"}} {"account_number":369,"balance":17047,"firstname":"Mcfadden","lastname":"Guy","age":28,"gender":"F","address":"445 Lott Avenue","employer":"Kangle","email":"mcfaddenguy@kangle.com","city":"Greenbackville","state":"DE"} {"index":{"_id":"371"}} {"account_number":371,"balance":19751,"firstname":"Barker","lastname":"Allen","age":32,"gender":"F","address":"295 Wallabout Street","employer":"Nexgene","email":"barkerallen@nexgene.com","city":"Nanafalia","state":"NE"} {"index":{"_id":"376"}} {"account_number":376,"balance":44407,"firstname":"Mcmillan","lastname":"Dunn","age":21,"gender":"F","address":"771 Dorchester Road","employer":"Eargo","email":"mcmillandunn@eargo.com","city":"Yogaville","state":"RI"} {"index":{"_id":"383"}} {"account_number":383,"balance":48889,"firstname":"Knox","lastname":"Larson","age":28,"gender":"F","address":"962 Bartlett Place","employer":"Bostonic","email":"knoxlarson@bostonic.com","city":"Smeltertown","state":"TX"} {"index":{"_id":"388"}} {"account_number":388,"balance":9606,"firstname":"Julianne","lastname":"Nicholson","age":26,"gender":"F","address":"338 Crescent Street","employer":"Viasia","email":"juliannenicholson@viasia.com","city":"Alleghenyville","state":"MO"} {"index":{"_id":"390"}} {"account_number":390,"balance":7464,"firstname":"Ramona","lastname":"Roy","age":32,"gender":"M","address":"135 Banner Avenue","employer":"Deminimum","email":"ramonaroy@deminimum.com","city":"Dodge","state":"ID"} {"index":{"_id":"395"}} {"account_number":395,"balance":18679,"firstname":"Juliet","lastname":"Whitaker","age":31,"gender":"M","address":"128 Remsen Avenue","employer":"Toyletry","email":"julietwhitaker@toyletry.com","city":"Yonah","state":"LA"} {"index":{"_id":"403"}} {"account_number":403,"balance":18833,"firstname":"Williamson","lastname":"Horn","age":32,"gender":"M","address":"223 Strickland Avenue","employer":"Nimon","email":"williamsonhorn@nimon.com","city":"Bawcomville","state":"NJ"} {"index":{"_id":"408"}} {"account_number":408,"balance":34666,"firstname":"Lidia","lastname":"Guerrero","age":30,"gender":"M","address":"254 Stratford Road","employer":"Snowpoke","email":"lidiaguerrero@snowpoke.com","city":"Fairlee","state":"LA"} {"index":{"_id":"410"}} {"account_number":410,"balance":31200,"firstname":"Fox","lastname":"Cardenas","age":39,"gender":"M","address":"987 Monitor Street","employer":"Corpulse","email":"foxcardenas@corpulse.com","city":"Southview","state":"NE"} {"index":{"_id":"415"}} {"account_number":415,"balance":19449,"firstname":"Martinez","lastname":"Benson","age":36,"gender":"M","address":"172 Berkeley Place","employer":"Enersol","email":"martinezbenson@enersol.com","city":"Chumuckla","state":"AL"} {"index":{"_id":"422"}} {"account_number":422,"balance":40162,"firstname":"Brigitte","lastname":"Scott","age":26,"gender":"M","address":"662 Vermont Court","employer":"Waretel","email":"brigittescott@waretel.com","city":"Elrama","state":"VA"} {"index":{"_id":"427"}} {"account_number":427,"balance":1463,"firstname":"Rebekah","lastname":"Garrison","age":36,"gender":"F","address":"837 Hampton Avenue","employer":"Niquent","email":"rebekahgarrison@niquent.com","city":"Zarephath","state":"NY"} {"index":{"_id":"434"}} {"account_number":434,"balance":11329,"firstname":"Christa","lastname":"Huff","age":25,"gender":"M","address":"454 Oriental Boulevard","employer":"Earthpure","email":"christahuff@earthpure.com","city":"Stevens","state":"DC"} {"index":{"_id":"439"}} {"account_number":439,"balance":22752,"firstname":"Lula","lastname":"Williams","age":35,"gender":"M","address":"630 Furman Avenue","employer":"Vinch","email":"lulawilliams@vinch.com","city":"Newcastle","state":"ME"} {"index":{"_id":"441"}} {"account_number":441,"balance":47947,"firstname":"Dickson","lastname":"Mcgee","age":29,"gender":"M","address":"478 Knight Court","employer":"Gogol","email":"dicksonmcgee@gogol.com","city":"Laurelton","state":"AR"} {"index":{"_id":"446"}} {"account_number":446,"balance":23071,"firstname":"Lolita","lastname":"Fleming","age":32,"gender":"F","address":"918 Bridge Street","employer":"Vidto","email":"lolitafleming@vidto.com","city":"Brownlee","state":"HI"} {"index":{"_id":"453"}} {"account_number":453,"balance":21520,"firstname":"Hood","lastname":"Powell","age":24,"gender":"F","address":"479 Brevoort Place","employer":"Vortexaco","email":"hoodpowell@vortexaco.com","city":"Alderpoint","state":"CT"} {"index":{"_id":"458"}} {"account_number":458,"balance":8865,"firstname":"Aida","lastname":"Wolf","age":21,"gender":"F","address":"403 Thames Street","employer":"Isis","email":"aidawolf@isis.com","city":"Bordelonville","state":"ME"} {"index":{"_id":"460"}} {"account_number":460,"balance":37734,"firstname":"Aguirre","lastname":"White","age":21,"gender":"F","address":"190 Crooke Avenue","employer":"Unq","email":"aguirrewhite@unq.com","city":"Albany","state":"NJ"} {"index":{"_id":"465"}} {"account_number":465,"balance":10681,"firstname":"Pearlie","lastname":"Holman","age":29,"gender":"M","address":"916 Evergreen Avenue","employer":"Hometown","email":"pearlieholman@hometown.com","city":"Needmore","state":"UT"} {"index":{"_id":"472"}} {"account_number":472,"balance":25571,"firstname":"Lee","lastname":"Long","age":32,"gender":"F","address":"288 Mill Street","employer":"Comverges","email":"leelong@comverges.com","city":"Movico","state":"MT"} {"index":{"_id":"477"}} {"account_number":477,"balance":25892,"firstname":"Holcomb","lastname":"Cobb","age":40,"gender":"M","address":"369 Marconi Place","employer":"Steeltab","email":"holcombcobb@steeltab.com","city":"Byrnedale","state":"CA"} {"index":{"_id":"484"}} {"account_number":484,"balance":3274,"firstname":"Staci","lastname":"Melendez","age":35,"gender":"F","address":"751 Otsego Street","employer":"Namebox","email":"stacimelendez@namebox.com","city":"Harborton","state":"NV"} {"index":{"_id":"489"}} {"account_number":489,"balance":7879,"firstname":"Garrett","lastname":"Langley","age":36,"gender":"M","address":"331 Bowne Street","employer":"Zillidium","email":"garrettlangley@zillidium.com","city":"Riviera","state":"LA"} {"index":{"_id":"491"}} {"account_number":491,"balance":42942,"firstname":"Teresa","lastname":"Owen","age":24,"gender":"F","address":"713 Canton Court","employer":"Plasmos","email":"teresaowen@plasmos.com","city":"Bartonsville","state":"NH"} {"index":{"_id":"496"}} {"account_number":496,"balance":14869,"firstname":"Alison","lastname":"Conrad","age":35,"gender":"F","address":"347 Varet Street","employer":"Perkle","email":"alisonconrad@perkle.com","city":"Cliffside","state":"OH"} {"index":{"_id":"504"}} {"account_number":504,"balance":49205,"firstname":"Shanna","lastname":"Chambers","age":23,"gender":"M","address":"220 Beard Street","employer":"Corporana","email":"shannachambers@corporana.com","city":"Cashtown","state":"AZ"} {"index":{"_id":"509"}} {"account_number":509,"balance":34754,"firstname":"Durham","lastname":"Pacheco","age":40,"gender":"M","address":"129 Plymouth Street","employer":"Datacator","email":"durhampacheco@datacator.com","city":"Loveland","state":"NC"} {"index":{"_id":"511"}} {"account_number":511,"balance":40908,"firstname":"Elba","lastname":"Grant","age":24,"gender":"F","address":"157 Bijou Avenue","employer":"Dognost","email":"elbagrant@dognost.com","city":"Coyote","state":"MT"} {"index":{"_id":"516"}} {"account_number":516,"balance":44940,"firstname":"Roy","lastname":"Smith","age":37,"gender":"M","address":"770 Cherry Street","employer":"Parleynet","email":"roysmith@parleynet.com","city":"Carrsville","state":"RI"} {"index":{"_id":"523"}} {"account_number":523,"balance":28729,"firstname":"Amalia","lastname":"Benjamin","age":40,"gender":"F","address":"173 Bushwick Place","employer":"Sentia","email":"amaliabenjamin@sentia.com","city":"Jacumba","state":"OK"} {"index":{"_id":"528"}} {"account_number":528,"balance":4071,"firstname":"Thompson","lastname":"Hoover","age":27,"gender":"F","address":"580 Garden Street","employer":"Portalis","email":"thompsonhoover@portalis.com","city":"Knowlton","state":"AL"} {"index":{"_id":"530"}} {"account_number":530,"balance":8840,"firstname":"Kathrine","lastname":"Evans","age":37,"gender":"M","address":"422 Division Place","employer":"Spherix","email":"kathrineevans@spherix.com","city":"Biddle","state":"CO"} {"index":{"_id":"535"}} {"account_number":535,"balance":8715,"firstname":"Fry","lastname":"George","age":34,"gender":"M","address":"722 Green Street","employer":"Ewaves","email":"frygeorge@ewaves.com","city":"Kenmar","state":"DE"} {"index":{"_id":"542"}} {"account_number":542,"balance":23285,"firstname":"Michelle","lastname":"Mayo","age":35,"gender":"M","address":"657 Caton Place","employer":"Biflex","email":"michellemayo@biflex.com","city":"Beaverdale","state":"WY"} {"index":{"_id":"547"}} {"account_number":547,"balance":12870,"firstname":"Eaton","lastname":"Rios","age":32,"gender":"M","address":"744 Withers Street","employer":"Podunk","email":"eatonrios@podunk.com","city":"Chelsea","state":"IA"} {"index":{"_id":"554"}} {"account_number":554,"balance":33163,"firstname":"Townsend","lastname":"Atkins","age":39,"gender":"M","address":"566 Ira Court","employer":"Acruex","email":"townsendatkins@acruex.com","city":"Valle","state":"IA"} {"index":{"_id":"559"}} {"account_number":559,"balance":11450,"firstname":"Tonia","lastname":"Schmidt","age":38,"gender":"F","address":"508 Sheffield Avenue","employer":"Extro","email":"toniaschmidt@extro.com","city":"Newry","state":"CT"} {"index":{"_id":"561"}} {"account_number":561,"balance":12370,"firstname":"Sellers","lastname":"Davis","age":30,"gender":"M","address":"860 Madoc Avenue","employer":"Isodrive","email":"sellersdavis@isodrive.com","city":"Trail","state":"KS"} {"index":{"_id":"566"}} {"account_number":566,"balance":6183,"firstname":"Cox","lastname":"Roman","age":37,"gender":"M","address":"349 Winthrop Street","employer":"Medcom","email":"coxroman@medcom.com","city":"Rosewood","state":"WY"} {"index":{"_id":"573"}} {"account_number":573,"balance":32171,"firstname":"Callie","lastname":"Castaneda","age":36,"gender":"M","address":"799 Scott Avenue","employer":"Earthwax","email":"calliecastaneda@earthwax.com","city":"Marshall","state":"NH"} {"index":{"_id":"578"}} {"account_number":578,"balance":34259,"firstname":"Holmes","lastname":"Mcknight","age":37,"gender":"M","address":"969 Metropolitan Avenue","employer":"Cubicide","email":"holmesmcknight@cubicide.com","city":"Aguila","state":"PA"} {"index":{"_id":"580"}} {"account_number":580,"balance":13716,"firstname":"Mcmahon","lastname":"York","age":34,"gender":"M","address":"475 Beacon Court","employer":"Zillar","email":"mcmahonyork@zillar.com","city":"Farmington","state":"MO"} {"index":{"_id":"585"}} {"account_number":585,"balance":26745,"firstname":"Nieves","lastname":"Nolan","age":32,"gender":"M","address":"115 Seagate Terrace","employer":"Jumpstack","email":"nievesnolan@jumpstack.com","city":"Eastmont","state":"UT"} {"index":{"_id":"592"}} {"account_number":592,"balance":32968,"firstname":"Head","lastname":"Webster","age":36,"gender":"F","address":"987 Lefferts Avenue","employer":"Empirica","email":"headwebster@empirica.com","city":"Rockingham","state":"TN"} {"index":{"_id":"597"}} {"account_number":597,"balance":11246,"firstname":"Penny","lastname":"Knowles","age":33,"gender":"M","address":"139 Forbell Street","employer":"Ersum","email":"pennyknowles@ersum.com","city":"Vallonia","state":"IA"} {"index":{"_id":"600"}} {"account_number":600,"balance":10336,"firstname":"Simmons","lastname":"Byers","age":37,"gender":"M","address":"250 Dictum Court","employer":"Qualitex","email":"simmonsbyers@qualitex.com","city":"Wanship","state":"OH"} {"index":{"_id":"605"}} {"account_number":605,"balance":38427,"firstname":"Mcclain","lastname":"Manning","age":24,"gender":"M","address":"832 Leonard Street","employer":"Qiao","email":"mcclainmanning@qiao.com","city":"Calvary","state":"TX"} {"index":{"_id":"612"}} {"account_number":612,"balance":11868,"firstname":"Dunn","lastname":"Cameron","age":32,"gender":"F","address":"156 Lorimer Street","employer":"Isonus","email":"dunncameron@isonus.com","city":"Virgie","state":"ND"} {"index":{"_id":"617"}} {"account_number":617,"balance":35445,"firstname":"Kitty","lastname":"Cooley","age":22,"gender":"M","address":"788 Seagate Avenue","employer":"Ultrimax","email":"kittycooley@ultrimax.com","city":"Clarktown","state":"MD"} {"index":{"_id":"624"}} {"account_number":624,"balance":27538,"firstname":"Roxanne","lastname":"Franklin","age":39,"gender":"F","address":"299 Woodrow Court","employer":"Silodyne","email":"roxannefranklin@silodyne.com","city":"Roulette","state":"VA"} {"index":{"_id":"629"}} {"account_number":629,"balance":32987,"firstname":"Mcclure","lastname":"Rodgers","age":26,"gender":"M","address":"806 Pierrepont Place","employer":"Elita","email":"mcclurerodgers@elita.com","city":"Brownsville","state":"MI"} {"index":{"_id":"631"}} {"account_number":631,"balance":21657,"firstname":"Corrine","lastname":"Barber","age":32,"gender":"F","address":"447 Hunts Lane","employer":"Quarmony","email":"corrinebarber@quarmony.com","city":"Wyano","state":"IL"} {"index":{"_id":"636"}} {"account_number":636,"balance":8036,"firstname":"Agnes","lastname":"Hooper","age":25,"gender":"M","address":"865 Hanson Place","employer":"Digial","email":"agneshooper@digial.com","city":"Sperryville","state":"OK"} {"index":{"_id":"643"}} {"account_number":643,"balance":8057,"firstname":"Hendricks","lastname":"Stokes","age":23,"gender":"F","address":"142 Barbey Street","employer":"Remotion","email":"hendricksstokes@remotion.com","city":"Lewis","state":"MA"} {"index":{"_id":"648"}} {"account_number":648,"balance":11506,"firstname":"Terry","lastname":"Montgomery","age":21,"gender":"F","address":"115 Franklin Avenue","employer":"Enervate","email":"terrymontgomery@enervate.com","city":"Bascom","state":"MA"} {"index":{"_id":"650"}} {"account_number":650,"balance":18091,"firstname":"Benton","lastname":"Knight","age":28,"gender":"F","address":"850 Aitken Place","employer":"Pholio","email":"bentonknight@pholio.com","city":"Cobbtown","state":"AL"} {"index":{"_id":"655"}} {"account_number":655,"balance":22912,"firstname":"Eula","lastname":"Taylor","age":30,"gender":"M","address":"520 Orient Avenue","employer":"Miracula","email":"eulataylor@miracula.com","city":"Wacissa","state":"IN"} {"index":{"_id":"662"}} {"account_number":662,"balance":10138,"firstname":"Daisy","lastname":"Burnett","age":33,"gender":"M","address":"114 Norman Avenue","employer":"Liquicom","email":"daisyburnett@liquicom.com","city":"Grahamtown","state":"MD"} {"index":{"_id":"667"}} {"account_number":667,"balance":22559,"firstname":"Juliana","lastname":"Chase","age":32,"gender":"M","address":"496 Coleridge Street","employer":"Comtract","email":"julianachase@comtract.com","city":"Wilsonia","state":"NJ"} {"index":{"_id":"674"}} {"account_number":674,"balance":36038,"firstname":"Watts","lastname":"Shannon","age":22,"gender":"F","address":"600 Story Street","employer":"Joviold","email":"wattsshannon@joviold.com","city":"Fairhaven","state":"ID"} {"index":{"_id":"679"}} {"account_number":679,"balance":20149,"firstname":"Henrietta","lastname":"Bonner","age":33,"gender":"M","address":"461 Bond Street","employer":"Geekol","email":"henriettabonner@geekol.com","city":"Richville","state":"WA"} {"index":{"_id":"681"}} {"account_number":681,"balance":34244,"firstname":"Velazquez","lastname":"Wolfe","age":33,"gender":"M","address":"773 Eckford Street","employer":"Zisis","email":"velazquezwolfe@zisis.com","city":"Smock","state":"ME"} {"index":{"_id":"686"}} {"account_number":686,"balance":10116,"firstname":"Decker","lastname":"Mcclure","age":30,"gender":"F","address":"236 Commerce Street","employer":"Everest","email":"deckermcclure@everest.com","city":"Gibbsville","state":"TN"} {"index":{"_id":"693"}} {"account_number":693,"balance":31233,"firstname":"Tabatha","lastname":"Zimmerman","age":30,"gender":"F","address":"284 Emmons Avenue","employer":"Pushcart","email":"tabathazimmerman@pushcart.com","city":"Esmont","state":"NC"} {"index":{"_id":"698"}} {"account_number":698,"balance":14965,"firstname":"Baker","lastname":"Armstrong","age":36,"gender":"F","address":"796 Tehama Street","employer":"Nurplex","email":"bakerarmstrong@nurplex.com","city":"Starks","state":"UT"} {"index":{"_id":"701"}} {"account_number":701,"balance":23772,"firstname":"Gardner","lastname":"Griffith","age":27,"gender":"M","address":"187 Moore Place","employer":"Vertide","email":"gardnergriffith@vertide.com","city":"Coventry","state":"NV"} {"index":{"_id":"706"}} {"account_number":706,"balance":5282,"firstname":"Eliza","lastname":"Potter","age":39,"gender":"M","address":"945 Dunham Place","employer":"Playce","email":"elizapotter@playce.com","city":"Woodruff","state":"AK"} {"index":{"_id":"713"}} {"account_number":713,"balance":20054,"firstname":"Iris","lastname":"Mcguire","age":21,"gender":"F","address":"508 Benson Avenue","employer":"Duflex","email":"irismcguire@duflex.com","city":"Hillsboro","state":"MO"} {"index":{"_id":"718"}} {"account_number":718,"balance":13876,"firstname":"Hickman","lastname":"Dillard","age":22,"gender":"F","address":"132 Etna Street","employer":"Genmy","email":"hickmandillard@genmy.com","city":"Curtice","state":"NV"} {"index":{"_id":"720"}} {"account_number":720,"balance":31356,"firstname":"Ruth","lastname":"Vance","age":32,"gender":"F","address":"229 Adams Street","employer":"Zilidium","email":"ruthvance@zilidium.com","city":"Allison","state":"IA"} {"index":{"_id":"725"}} {"account_number":725,"balance":14677,"firstname":"Reeves","lastname":"Tillman","age":26,"gender":"M","address":"674 Ivan Court","employer":"Cemention","email":"reevestillman@cemention.com","city":"Navarre","state":"MA"} {"index":{"_id":"732"}} {"account_number":732,"balance":38445,"firstname":"Delia","lastname":"Cruz","age":37,"gender":"F","address":"870 Cheever Place","employer":"Multron","email":"deliacruz@multron.com","city":"Cresaptown","state":"NH"} {"index":{"_id":"737"}} {"account_number":737,"balance":40431,"firstname":"Sampson","lastname":"Yates","age":23,"gender":"F","address":"214 Cox Place","employer":"Signidyne","email":"sampsonyates@signidyne.com","city":"Brazos","state":"GA"} {"index":{"_id":"744"}} {"account_number":744,"balance":8690,"firstname":"Bernard","lastname":"Martinez","age":21,"gender":"M","address":"148 Dunne Place","employer":"Dragbot","email":"bernardmartinez@dragbot.com","city":"Moraida","state":"MN"} {"index":{"_id":"749"}} {"account_number":749,"balance":1249,"firstname":"Rush","lastname":"Boyle","age":36,"gender":"M","address":"310 Argyle Road","employer":"Sportan","email":"rushboyle@sportan.com","city":"Brady","state":"WA"} {"index":{"_id":"751"}} {"account_number":751,"balance":49252,"firstname":"Patrick","lastname":"Osborne","age":23,"gender":"M","address":"915 Prospect Avenue","employer":"Gynko","email":"patrickosborne@gynko.com","city":"Takilma","state":"MO"} {"index":{"_id":"756"}} {"account_number":756,"balance":40006,"firstname":"Jasmine","lastname":"Howell","age":32,"gender":"M","address":"605 Elliott Walk","employer":"Ecratic","email":"jasminehowell@ecratic.com","city":"Harrodsburg","state":"OH"} {"index":{"_id":"763"}} {"account_number":763,"balance":12091,"firstname":"Liz","lastname":"Bentley","age":22,"gender":"F","address":"933 Debevoise Avenue","employer":"Nipaz","email":"lizbentley@nipaz.com","city":"Glenville","state":"NJ"} {"index":{"_id":"768"}} {"account_number":768,"balance":2213,"firstname":"Sondra","lastname":"Soto","age":21,"gender":"M","address":"625 Colonial Road","employer":"Navir","email":"sondrasoto@navir.com","city":"Benson","state":"VA"} {"index":{"_id":"770"}} {"account_number":770,"balance":39505,"firstname":"Joann","lastname":"Crane","age":26,"gender":"M","address":"798 Farragut Place","employer":"Lingoage","email":"joanncrane@lingoage.com","city":"Kirk","state":"MA"} {"index":{"_id":"775"}} {"account_number":775,"balance":27943,"firstname":"Wilson","lastname":"Merritt","age":33,"gender":"F","address":"288 Thornton Street","employer":"Geeky","email":"wilsonmerritt@geeky.com","city":"Holtville","state":"HI"} {"index":{"_id":"782"}} {"account_number":782,"balance":3960,"firstname":"Maldonado","lastname":"Craig","age":36,"gender":"F","address":"345 Myrtle Avenue","employer":"Zilencio","email":"maldonadocraig@zilencio.com","city":"Yukon","state":"ID"} {"index":{"_id":"787"}} {"account_number":787,"balance":11876,"firstname":"Harper","lastname":"Wynn","age":21,"gender":"F","address":"139 Oceanic Avenue","employer":"Interfind","email":"harperwynn@interfind.com","city":"Gerber","state":"ND"} {"index":{"_id":"794"}} {"account_number":794,"balance":16491,"firstname":"Walker","lastname":"Charles","age":32,"gender":"M","address":"215 Kenilworth Place","employer":"Orbin","email":"walkercharles@orbin.com","city":"Rivers","state":"WI"} {"index":{"_id":"799"}} {"account_number":799,"balance":2889,"firstname":"Myra","lastname":"Guerra","age":28,"gender":"F","address":"625 Dahlgreen Place","employer":"Digigene","email":"myraguerra@digigene.com","city":"Draper","state":"CA"} {"index":{"_id":"802"}} {"account_number":802,"balance":19630,"firstname":"Gracie","lastname":"Foreman","age":40,"gender":"F","address":"219 Kent Avenue","employer":"Supportal","email":"gracieforeman@supportal.com","city":"Westboro","state":"NH"} {"index":{"_id":"807"}} {"account_number":807,"balance":29206,"firstname":"Hatfield","lastname":"Lowe","age":23,"gender":"M","address":"499 Adler Place","employer":"Lovepad","email":"hatfieldlowe@lovepad.com","city":"Wiscon","state":"DC"} {"index":{"_id":"814"}} {"account_number":814,"balance":9838,"firstname":"Morse","lastname":"Mcbride","age":26,"gender":"F","address":"776 Calyer Street","employer":"Inear","email":"morsemcbride@inear.com","city":"Kingstowne","state":"ND"} {"index":{"_id":"819"}} {"account_number":819,"balance":3971,"firstname":"Karyn","lastname":"Medina","age":24,"gender":"F","address":"417 Utica Avenue","employer":"Qnekt","email":"karynmedina@qnekt.com","city":"Kerby","state":"WY"} {"index":{"_id":"821"}} {"account_number":821,"balance":33271,"firstname":"Trisha","lastname":"Blankenship","age":22,"gender":"M","address":"329 Jamaica Avenue","employer":"Chorizon","email":"trishablankenship@chorizon.com","city":"Sexton","state":"VT"} {"index":{"_id":"826"}} {"account_number":826,"balance":11548,"firstname":"Summers","lastname":"Vinson","age":22,"gender":"F","address":"742 Irwin Street","employer":"Globoil","email":"summersvinson@globoil.com","city":"Callaghan","state":"WY"} {"index":{"_id":"833"}} {"account_number":833,"balance":46154,"firstname":"Woodward","lastname":"Hood","age":22,"gender":"M","address":"398 Atkins Avenue","employer":"Zedalis","email":"woodwardhood@zedalis.com","city":"Stonybrook","state":"NE"} {"index":{"_id":"838"}} {"account_number":838,"balance":24629,"firstname":"Latonya","lastname":"Blake","age":37,"gender":"F","address":"531 Milton Street","employer":"Rugstars","email":"latonyablake@rugstars.com","city":"Tedrow","state":"WA"} {"index":{"_id":"840"}} {"account_number":840,"balance":39615,"firstname":"Boone","lastname":"Gomez","age":38,"gender":"M","address":"256 Hampton Place","employer":"Geekular","email":"boonegomez@geekular.com","city":"Westerville","state":"HI"} {"index":{"_id":"845"}} {"account_number":845,"balance":35422,"firstname":"Tracy","lastname":"Vaughn","age":39,"gender":"M","address":"645 Rockaway Parkway","employer":"Andryx","email":"tracyvaughn@andryx.com","city":"Wilmington","state":"ME"} {"index":{"_id":"852"}} {"account_number":852,"balance":6041,"firstname":"Allen","lastname":"Hammond","age":26,"gender":"M","address":"793 Essex Street","employer":"Tersanki","email":"allenhammond@tersanki.com","city":"Osmond","state":"NC"} {"index":{"_id":"857"}} {"account_number":857,"balance":39678,"firstname":"Alyce","lastname":"Douglas","age":23,"gender":"M","address":"326 Robert Street","employer":"Earbang","email":"alycedouglas@earbang.com","city":"Thornport","state":"GA"} {"index":{"_id":"864"}} {"account_number":864,"balance":21804,"firstname":"Duffy","lastname":"Anthony","age":23,"gender":"M","address":"582 Cooke Court","employer":"Schoolio","email":"duffyanthony@schoolio.com","city":"Brenton","state":"CO"} {"index":{"_id":"869"}} {"account_number":869,"balance":43544,"firstname":"Corinne","lastname":"Robbins","age":25,"gender":"F","address":"732 Quentin Road","employer":"Orbaxter","email":"corinnerobbins@orbaxter.com","city":"Roy","state":"TN"} {"index":{"_id":"871"}} {"account_number":871,"balance":35854,"firstname":"Norma","lastname":"Burt","age":32,"gender":"M","address":"934 Cyrus Avenue","employer":"Magnafone","email":"normaburt@magnafone.com","city":"Eden","state":"TN"} {"index":{"_id":"876"}} {"account_number":876,"balance":48568,"firstname":"Brady","lastname":"Glover","age":21,"gender":"F","address":"565 Oceanview Avenue","employer":"Comvex","email":"bradyglover@comvex.com","city":"Noblestown","state":"ID"} {"index":{"_id":"883"}} {"account_number":883,"balance":33679,"firstname":"Austin","lastname":"Jefferson","age":34,"gender":"M","address":"846 Lincoln Avenue","employer":"Polarax","email":"austinjefferson@polarax.com","city":"Savannah","state":"CT"} {"index":{"_id":"888"}} {"account_number":888,"balance":22277,"firstname":"Myrna","lastname":"Herman","age":39,"gender":"F","address":"649 Harwood Place","employer":"Enthaze","email":"myrnaherman@enthaze.com","city":"Idamay","state":"AR"} {"index":{"_id":"890"}} {"account_number":890,"balance":31198,"firstname":"Alvarado","lastname":"Pate","age":25,"gender":"M","address":"269 Ashland Place","employer":"Ovolo","email":"alvaradopate@ovolo.com","city":"Volta","state":"MI"} {"index":{"_id":"895"}} {"account_number":895,"balance":7327,"firstname":"Lara","lastname":"Mcdaniel","age":36,"gender":"M","address":"854 Willow Place","employer":"Acusage","email":"laramcdaniel@acusage.com","city":"Imperial","state":"NC"} {"index":{"_id":"903"}} {"account_number":903,"balance":10238,"firstname":"Wade","lastname":"Page","age":35,"gender":"F","address":"685 Waldorf Court","employer":"Eplosion","email":"wadepage@eplosion.com","city":"Welda","state":"AL"} {"index":{"_id":"908"}} {"account_number":908,"balance":45975,"firstname":"Mosley","lastname":"Holloway","age":31,"gender":"M","address":"929 Eldert Lane","employer":"Anivet","email":"mosleyholloway@anivet.com","city":"Biehle","state":"MS"} {"index":{"_id":"910"}} {"account_number":910,"balance":36831,"firstname":"Esmeralda","lastname":"James","age":23,"gender":"F","address":"535 High Street","employer":"Terrasys","email":"esmeraldajames@terrasys.com","city":"Dubois","state":"IN"} {"index":{"_id":"915"}} {"account_number":915,"balance":19816,"firstname":"Farrell","lastname":"French","age":35,"gender":"F","address":"126 McKibbin Street","employer":"Techmania","email":"farrellfrench@techmania.com","city":"Wescosville","state":"AL"} {"index":{"_id":"922"}} {"account_number":922,"balance":39347,"firstname":"Irwin","lastname":"Pugh","age":32,"gender":"M","address":"463 Shale Street","employer":"Idego","email":"irwinpugh@idego.com","city":"Ivanhoe","state":"ID"} {"index":{"_id":"927"}} {"account_number":927,"balance":19976,"firstname":"Jeanette","lastname":"Acevedo","age":26,"gender":"M","address":"694 Polhemus Place","employer":"Halap","email":"jeanetteacevedo@halap.com","city":"Harrison","state":"MO"} {"index":{"_id":"934"}} {"account_number":934,"balance":43987,"firstname":"Freida","lastname":"Daniels","age":34,"gender":"M","address":"448 Cove Lane","employer":"Vurbo","email":"freidadaniels@vurbo.com","city":"Snelling","state":"NJ"} {"index":{"_id":"939"}} {"account_number":939,"balance":31228,"firstname":"Hodges","lastname":"Massey","age":37,"gender":"F","address":"431 Dahl Court","employer":"Kegular","email":"hodgesmassey@kegular.com","city":"Katonah","state":"MD"} {"index":{"_id":"941"}} {"account_number":941,"balance":38796,"firstname":"Kim","lastname":"Moss","age":28,"gender":"F","address":"105 Onderdonk Avenue","employer":"Digirang","email":"kimmoss@digirang.com","city":"Centerville","state":"TX"} {"index":{"_id":"946"}} {"account_number":946,"balance":42794,"firstname":"Ina","lastname":"Obrien","age":36,"gender":"M","address":"339 Rewe Street","employer":"Eclipsent","email":"inaobrien@eclipsent.com","city":"Soham","state":"RI"} {"index":{"_id":"953"}} {"account_number":953,"balance":1110,"firstname":"Baxter","lastname":"Black","age":27,"gender":"M","address":"720 Stillwell Avenue","employer":"Uplinx","email":"baxterblack@uplinx.com","city":"Drummond","state":"MN"} {"index":{"_id":"958"}} {"account_number":958,"balance":32849,"firstname":"Brown","lastname":"Wilkins","age":40,"gender":"M","address":"686 Delmonico Place","employer":"Medesign","email":"brownwilkins@medesign.com","city":"Shelby","state":"WY"} {"index":{"_id":"960"}} {"account_number":960,"balance":2905,"firstname":"Curry","lastname":"Vargas","age":40,"gender":"M","address":"242 Blake Avenue","employer":"Pearlesex","email":"curryvargas@pearlesex.com","city":"Henrietta","state":"NH"} {"index":{"_id":"965"}} {"account_number":965,"balance":21882,"firstname":"Patrica","lastname":"Melton","age":28,"gender":"M","address":"141 Rodney Street","employer":"Flexigen","email":"patricamelton@flexigen.com","city":"Klagetoh","state":"MD"} {"index":{"_id":"972"}} {"account_number":972,"balance":24719,"firstname":"Leona","lastname":"Christian","age":26,"gender":"F","address":"900 Woodpoint Road","employer":"Extrawear","email":"leonachristian@extrawear.com","city":"Roderfield","state":"MA"} {"index":{"_id":"977"}} {"account_number":977,"balance":6744,"firstname":"Rodgers","lastname":"Mccray","age":21,"gender":"F","address":"612 Duryea Place","employer":"Papricut","email":"rodgersmccray@papricut.com","city":"Marenisco","state":"MD"} {"index":{"_id":"984"}} {"account_number":984,"balance":1904,"firstname":"Viola","lastname":"Crawford","age":35,"gender":"F","address":"354 Linwood Street","employer":"Ginkle","email":"violacrawford@ginkle.com","city":"Witmer","state":"AR"} {"index":{"_id":"989"}} {"account_number":989,"balance":48622,"firstname":"Franklin","lastname":"Frank","age":38,"gender":"M","address":"270 Carlton Avenue","employer":"Shopabout","email":"franklinfrank@shopabout.com","city":"Guthrie","state":"NC"} {"index":{"_id":"991"}} {"account_number":991,"balance":4239,"firstname":"Connie","lastname":"Berry","age":28,"gender":"F","address":"647 Gardner Avenue","employer":"Flumbo","email":"connieberry@flumbo.com","city":"Frierson","state":"MO"} {"index":{"_id":"996"}} {"account_number":996,"balance":17541,"firstname":"Andrews","lastname":"Herrera","age":30,"gender":"F","address":"570 Vandam Street","employer":"Klugger","email":"andrewsherrera@klugger.com","city":"Whitehaven","state":"MN"} {"index":{"_id":"0"}} {"account_number":0,"balance":16623,"firstname":"Bradshaw","lastname":"Mckenzie","age":29,"gender":"F","address":"244 Columbus Place","employer":"Euron","email":"bradshawmckenzie@euron.com","city":"Hobucken","state":"CO"} {"index":{"_id":"5"}} {"account_number":5,"balance":29342,"firstname":"Leola","lastname":"Stewart","age":30,"gender":"F","address":"311 Elm Place","employer":"Diginetic","email":"leolastewart@diginetic.com","city":"Fairview","state":"NJ"} {"index":{"_id":"12"}} {"account_number":12,"balance":37055,"firstname":"Stafford","lastname":"Brock","age":20,"gender":"F","address":"296 Wythe Avenue","employer":"Uncorp","email":"staffordbrock@uncorp.com","city":"Bend","state":"AL"} {"index":{"_id":"17"}} {"account_number":17,"balance":7831,"firstname":"Bessie","lastname":"Orr","age":31,"gender":"F","address":"239 Hinsdale Street","employer":"Skyplex","email":"bessieorr@skyplex.com","city":"Graball","state":"FL"} {"index":{"_id":"24"}} {"account_number":24,"balance":44182,"firstname":"Wood","lastname":"Dale","age":39,"gender":"M","address":"582 Gelston Avenue","employer":"Besto","email":"wooddale@besto.com","city":"Juntura","state":"MI"} {"index":{"_id":"29"}} {"account_number":29,"balance":27323,"firstname":"Leah","lastname":"Santiago","age":33,"gender":"M","address":"193 Schenck Avenue","employer":"Isologix","email":"leahsantiago@isologix.com","city":"Gerton","state":"ND"} {"index":{"_id":"31"}} {"account_number":31,"balance":30443,"firstname":"Kristen","lastname":"Santana","age":22,"gender":"F","address":"130 Middagh Street","employer":"Dogspa","email":"kristensantana@dogspa.com","city":"Vale","state":"MA"} {"index":{"_id":"36"}} {"account_number":36,"balance":15902,"firstname":"Alexandra","lastname":"Nguyen","age":39,"gender":"F","address":"389 Elizabeth Place","employer":"Bittor","email":"alexandranguyen@bittor.com","city":"Hemlock","state":"KY"} {"index":{"_id":"43"}} {"account_number":43,"balance":33474,"firstname":"Ryan","lastname":"Howe","age":25,"gender":"M","address":"660 Huntington Street","employer":"Microluxe","email":"ryanhowe@microluxe.com","city":"Clara","state":"CT"} {"index":{"_id":"48"}} {"account_number":48,"balance":40608,"firstname":"Peck","lastname":"Downs","age":39,"gender":"F","address":"594 Dwight Street","employer":"Ramjob","email":"peckdowns@ramjob.com","city":"Coloma","state":"WA"} {"index":{"_id":"50"}} {"account_number":50,"balance":43695,"firstname":"Sheena","lastname":"Kirkland","age":33,"gender":"M","address":"598 Bank Street","employer":"Zerbina","email":"sheenakirkland@zerbina.com","city":"Walland","state":"IN"} {"index":{"_id":"55"}} {"account_number":55,"balance":22020,"firstname":"Shelia","lastname":"Puckett","age":33,"gender":"M","address":"265 Royce Place","employer":"Izzby","email":"sheliapuckett@izzby.com","city":"Slovan","state":"HI"} {"index":{"_id":"62"}} {"account_number":62,"balance":43065,"firstname":"Lester","lastname":"Stanton","age":37,"gender":"M","address":"969 Doughty Street","employer":"Geekko","email":"lesterstanton@geekko.com","city":"Itmann","state":"DC"} {"index":{"_id":"67"}} {"account_number":67,"balance":39430,"firstname":"Isabelle","lastname":"Spence","age":39,"gender":"M","address":"718 Troy Avenue","employer":"Geeketron","email":"isabellespence@geeketron.com","city":"Camptown","state":"WA"} {"index":{"_id":"74"}} {"account_number":74,"balance":47167,"firstname":"Lauri","lastname":"Saunders","age":38,"gender":"F","address":"768 Lynch Street","employer":"Securia","email":"laurisaunders@securia.com","city":"Caroline","state":"TN"} {"index":{"_id":"79"}} {"account_number":79,"balance":28185,"firstname":"Booker","lastname":"Lowery","age":29,"gender":"M","address":"817 Campus Road","employer":"Sensate","email":"bookerlowery@sensate.com","city":"Carlos","state":"MT"} {"index":{"_id":"81"}} {"account_number":81,"balance":46568,"firstname":"Dennis","lastname":"Gilbert","age":40,"gender":"M","address":"619 Minna Street","employer":"Melbacor","email":"dennisgilbert@melbacor.com","city":"Kersey","state":"ND"} {"index":{"_id":"86"}} {"account_number":86,"balance":15428,"firstname":"Walton","lastname":"Butler","age":36,"gender":"M","address":"999 Schenck Street","employer":"Unisure","email":"waltonbutler@unisure.com","city":"Bentonville","state":"IL"} {"index":{"_id":"93"}} {"account_number":93,"balance":17728,"firstname":"Jeri","lastname":"Booth","age":31,"gender":"M","address":"322 Roosevelt Court","employer":"Geekology","email":"jeribooth@geekology.com","city":"Leming","state":"ND"} {"index":{"_id":"98"}} {"account_number":98,"balance":15085,"firstname":"Cora","lastname":"Barrett","age":24,"gender":"F","address":"555 Neptune Court","employer":"Kiosk","email":"corabarrett@kiosk.com","city":"Independence","state":"MN"} {"index":{"_id":"101"}} {"account_number":101,"balance":43400,"firstname":"Cecelia","lastname":"Grimes","age":31,"gender":"M","address":"972 Lincoln Place","employer":"Ecosys","email":"ceceliagrimes@ecosys.com","city":"Manchester","state":"AR"} {"index":{"_id":"106"}} {"account_number":106,"balance":8212,"firstname":"Josefina","lastname":"Wagner","age":36,"gender":"M","address":"418 Estate Road","employer":"Kyaguru","email":"josefinawagner@kyaguru.com","city":"Darbydale","state":"FL"} {"index":{"_id":"113"}} {"account_number":113,"balance":41652,"firstname":"Burt","lastname":"Moses","age":27,"gender":"M","address":"633 Berry Street","employer":"Uni","email":"burtmoses@uni.com","city":"Russellville","state":"CT"} {"index":{"_id":"118"}} {"account_number":118,"balance":2223,"firstname":"Ballard","lastname":"Vasquez","age":33,"gender":"F","address":"101 Bush Street","employer":"Intergeek","email":"ballardvasquez@intergeek.com","city":"Century","state":"MN"} {"index":{"_id":"120"}} {"account_number":120,"balance":38565,"firstname":"Browning","lastname":"Rodriquez","age":33,"gender":"M","address":"910 Moore Street","employer":"Opportech","email":"browningrodriquez@opportech.com","city":"Cutter","state":"ND"} {"index":{"_id":"125"}} {"account_number":125,"balance":5396,"firstname":"Tanisha","lastname":"Dixon","age":30,"gender":"M","address":"482 Hancock Street","employer":"Junipoor","email":"tanishadixon@junipoor.com","city":"Wauhillau","state":"IA"} {"index":{"_id":"132"}} {"account_number":132,"balance":37707,"firstname":"Horton","lastname":"Romero","age":35,"gender":"M","address":"427 Rutherford Place","employer":"Affluex","email":"hortonromero@affluex.com","city":"Hall","state":"AK"} {"index":{"_id":"137"}} {"account_number":137,"balance":3596,"firstname":"Frost","lastname":"Freeman","age":29,"gender":"F","address":"191 Dennett Place","employer":"Beadzza","email":"frostfreeman@beadzza.com","city":"Sabillasville","state":"HI"} {"index":{"_id":"144"}} {"account_number":144,"balance":43257,"firstname":"Evans","lastname":"Dyer","age":30,"gender":"F","address":"912 Post Court","employer":"Magmina","email":"evansdyer@magmina.com","city":"Gordon","state":"HI"} {"index":{"_id":"149"}} {"account_number":149,"balance":22994,"firstname":"Megan","lastname":"Gonzales","age":21,"gender":"M","address":"836 Tampa Court","employer":"Andershun","email":"megangonzales@andershun.com","city":"Rockhill","state":"AL"} {"index":{"_id":"151"}} {"account_number":151,"balance":34473,"firstname":"Kent","lastname":"Joyner","age":20,"gender":"F","address":"799 Truxton Street","employer":"Kozgene","email":"kentjoyner@kozgene.com","city":"Allamuchy","state":"DC"} {"index":{"_id":"156"}} {"account_number":156,"balance":40185,"firstname":"Sloan","lastname":"Pennington","age":24,"gender":"F","address":"573 Opal Court","employer":"Hopeli","email":"sloanpennington@hopeli.com","city":"Evergreen","state":"CT"} {"index":{"_id":"163"}} {"account_number":163,"balance":43075,"firstname":"Wilda","lastname":"Norman","age":33,"gender":"F","address":"173 Beadel Street","employer":"Kog","email":"wildanorman@kog.com","city":"Bodega","state":"ME"} {"index":{"_id":"168"}} {"account_number":168,"balance":49568,"firstname":"Carissa","lastname":"Simon","age":20,"gender":"M","address":"975 Flatbush Avenue","employer":"Zillacom","email":"carissasimon@zillacom.com","city":"Neibert","state":"IL"} {"index":{"_id":"170"}} {"account_number":170,"balance":6025,"firstname":"Mann","lastname":"Madden","age":36,"gender":"F","address":"161 Radde Place","employer":"Farmex","email":"mannmadden@farmex.com","city":"Thermal","state":"LA"} {"index":{"_id":"175"}} {"account_number":175,"balance":16213,"firstname":"Montoya","lastname":"Donaldson","age":28,"gender":"F","address":"481 Morton Street","employer":"Envire","email":"montoyadonaldson@envire.com","city":"Delco","state":"MA"} {"index":{"_id":"182"}} {"account_number":182,"balance":7803,"firstname":"Manuela","lastname":"Dillon","age":21,"gender":"M","address":"742 Garnet Street","employer":"Moreganic","email":"manueladillon@moreganic.com","city":"Ilchester","state":"TX"} {"index":{"_id":"187"}} {"account_number":187,"balance":26581,"firstname":"Autumn","lastname":"Hodges","age":35,"gender":"M","address":"757 Granite Street","employer":"Ezentia","email":"autumnhodges@ezentia.com","city":"Martinsville","state":"KY"} {"index":{"_id":"194"}} {"account_number":194,"balance":16311,"firstname":"Beck","lastname":"Rosario","age":39,"gender":"M","address":"721 Cambridge Place","employer":"Zoid","email":"beckrosario@zoid.com","city":"Efland","state":"ID"} {"index":{"_id":"199"}} {"account_number":199,"balance":18086,"firstname":"Branch","lastname":"Love","age":26,"gender":"M","address":"458 Commercial Street","employer":"Frolix","email":"branchlove@frolix.com","city":"Caspar","state":"NC"} {"index":{"_id":"202"}} {"account_number":202,"balance":26466,"firstname":"Medina","lastname":"Brown","age":31,"gender":"F","address":"519 Sunnyside Court","employer":"Bleendot","email":"medinabrown@bleendot.com","city":"Winfred","state":"MI"} {"index":{"_id":"207"}} {"account_number":207,"balance":45535,"firstname":"Evelyn","lastname":"Lara","age":35,"gender":"F","address":"636 Chestnut Street","employer":"Ultrasure","email":"evelynlara@ultrasure.com","city":"Logan","state":"MI"} {"index":{"_id":"214"}} {"account_number":214,"balance":24418,"firstname":"Luann","lastname":"Faulkner","age":37,"gender":"F","address":"697 Hazel Court","employer":"Zolar","email":"luannfaulkner@zolar.com","city":"Ticonderoga","state":"TX"} {"index":{"_id":"219"}} {"account_number":219,"balance":17127,"firstname":"Edwards","lastname":"Hurley","age":25,"gender":"M","address":"834 Stockholm Street","employer":"Austech","email":"edwardshurley@austech.com","city":"Bayview","state":"NV"} {"index":{"_id":"221"}} {"account_number":221,"balance":15803,"firstname":"Benjamin","lastname":"Barrera","age":34,"gender":"M","address":"568 Main Street","employer":"Zaphire","email":"benjaminbarrera@zaphire.com","city":"Germanton","state":"WY"} {"index":{"_id":"226"}} {"account_number":226,"balance":37720,"firstname":"Wilkins","lastname":"Brady","age":40,"gender":"F","address":"486 Baltic Street","employer":"Dogtown","email":"wilkinsbrady@dogtown.com","city":"Condon","state":"MT"} {"index":{"_id":"233"}} {"account_number":233,"balance":23020,"firstname":"Washington","lastname":"Walsh","age":27,"gender":"M","address":"366 Church Avenue","employer":"Candecor","email":"washingtonwalsh@candecor.com","city":"Westphalia","state":"MA"} {"index":{"_id":"238"}} {"account_number":238,"balance":21287,"firstname":"Constance","lastname":"Wong","age":28,"gender":"M","address":"496 Brown Street","employer":"Grainspot","email":"constancewong@grainspot.com","city":"Cecilia","state":"IN"} {"index":{"_id":"240"}} {"account_number":240,"balance":49741,"firstname":"Oconnor","lastname":"Clay","age":35,"gender":"F","address":"659 Highland Boulevard","employer":"Franscene","email":"oconnorclay@franscene.com","city":"Kilbourne","state":"NH"} {"index":{"_id":"245"}} {"account_number":245,"balance":22026,"firstname":"Fran","lastname":"Bolton","age":28,"gender":"F","address":"147 Jerome Street","employer":"Solaren","email":"franbolton@solaren.com","city":"Nash","state":"RI"} {"index":{"_id":"252"}} {"account_number":252,"balance":18831,"firstname":"Elvia","lastname":"Poole","age":22,"gender":"F","address":"836 Delevan Street","employer":"Velity","email":"elviapoole@velity.com","city":"Groveville","state":"MI"} {"index":{"_id":"257"}} {"account_number":257,"balance":5318,"firstname":"Olive","lastname":"Oneil","age":35,"gender":"F","address":"457 Decatur Street","employer":"Helixo","email":"oliveoneil@helixo.com","city":"Chicopee","state":"MI"} {"index":{"_id":"264"}} {"account_number":264,"balance":22084,"firstname":"Samantha","lastname":"Ferrell","age":35,"gender":"F","address":"488 Fulton Street","employer":"Flum","email":"samanthaferrell@flum.com","city":"Brandywine","state":"MT"} {"index":{"_id":"269"}} {"account_number":269,"balance":43317,"firstname":"Crosby","lastname":"Figueroa","age":34,"gender":"M","address":"910 Aurelia Court","employer":"Pyramia","email":"crosbyfigueroa@pyramia.com","city":"Leyner","state":"OH"} {"index":{"_id":"271"}} {"account_number":271,"balance":11864,"firstname":"Holt","lastname":"Walter","age":30,"gender":"F","address":"645 Poplar Avenue","employer":"Grupoli","email":"holtwalter@grupoli.com","city":"Mansfield","state":"OR"} {"index":{"_id":"276"}} {"account_number":276,"balance":11606,"firstname":"Pittman","lastname":"Mathis","age":23,"gender":"F","address":"567 Charles Place","employer":"Zuvy","email":"pittmanmathis@zuvy.com","city":"Roeville","state":"KY"} {"index":{"_id":"283"}} {"account_number":283,"balance":24070,"firstname":"Fuentes","lastname":"Foley","age":30,"gender":"M","address":"729 Walker Court","employer":"Knowlysis","email":"fuentesfoley@knowlysis.com","city":"Tryon","state":"TN"} {"index":{"_id":"288"}} {"account_number":288,"balance":27243,"firstname":"Wong","lastname":"Stone","age":39,"gender":"F","address":"440 Willoughby Street","employer":"Zentix","email":"wongstone@zentix.com","city":"Wheatfields","state":"DC"} {"index":{"_id":"290"}} {"account_number":290,"balance":26103,"firstname":"Neva","lastname":"Burgess","age":37,"gender":"F","address":"985 Wyona Street","employer":"Slofast","email":"nevaburgess@slofast.com","city":"Cawood","state":"DC"} {"index":{"_id":"295"}} {"account_number":295,"balance":37358,"firstname":"Howe","lastname":"Nash","age":20,"gender":"M","address":"833 Union Avenue","employer":"Aquacine","email":"howenash@aquacine.com","city":"Indio","state":"MN"} {"index":{"_id":"303"}} {"account_number":303,"balance":21976,"firstname":"Huffman","lastname":"Green","age":24,"gender":"F","address":"455 Colby Court","employer":"Comtest","email":"huffmangreen@comtest.com","city":"Weeksville","state":"UT"} {"index":{"_id":"308"}} {"account_number":308,"balance":33989,"firstname":"Glass","lastname":"Schroeder","age":25,"gender":"F","address":"670 Veterans Avenue","employer":"Realmo","email":"glassschroeder@realmo.com","city":"Gratton","state":"NY"} {"index":{"_id":"310"}} {"account_number":310,"balance":23049,"firstname":"Shannon","lastname":"Morton","age":39,"gender":"F","address":"412 Pleasant Place","employer":"Ovation","email":"shannonmorton@ovation.com","city":"Edgar","state":"AZ"} {"index":{"_id":"315"}} {"account_number":315,"balance":1314,"firstname":"Clare","lastname":"Morrow","age":33,"gender":"F","address":"728 Madeline Court","employer":"Gaptec","email":"claremorrow@gaptec.com","city":"Mapletown","state":"PA"} {"index":{"_id":"322"}} {"account_number":322,"balance":6303,"firstname":"Gilliam","lastname":"Horne","age":27,"gender":"M","address":"414 Florence Avenue","employer":"Shepard","email":"gilliamhorne@shepard.com","city":"Winesburg","state":"WY"} {"index":{"_id":"327"}} {"account_number":327,"balance":29294,"firstname":"Nell","lastname":"Contreras","age":27,"gender":"M","address":"694 Gold Street","employer":"Momentia","email":"nellcontreras@momentia.com","city":"Cumminsville","state":"AL"} {"index":{"_id":"334"}} {"account_number":334,"balance":9178,"firstname":"Cross","lastname":"Floyd","age":21,"gender":"F","address":"815 Herkimer Court","employer":"Maroptic","email":"crossfloyd@maroptic.com","city":"Kraemer","state":"AK"} {"index":{"_id":"339"}} {"account_number":339,"balance":3992,"firstname":"Franco","lastname":"Welch","age":38,"gender":"F","address":"776 Brightwater Court","employer":"Earthplex","email":"francowelch@earthplex.com","city":"Naomi","state":"ME"} {"index":{"_id":"341"}} {"account_number":341,"balance":44367,"firstname":"Alberta","lastname":"Bradford","age":30,"gender":"F","address":"670 Grant Avenue","employer":"Bugsall","email":"albertabradford@bugsall.com","city":"Romeville","state":"MT"} {"index":{"_id":"346"}} {"account_number":346,"balance":26594,"firstname":"Shelby","lastname":"Sanchez","age":36,"gender":"F","address":"257 Fillmore Avenue","employer":"Geekus","email":"shelbysanchez@geekus.com","city":"Seymour","state":"CO"} {"index":{"_id":"353"}} {"account_number":353,"balance":45182,"firstname":"Rivera","lastname":"Sherman","age":37,"gender":"M","address":"603 Garden Place","employer":"Bovis","email":"riverasherman@bovis.com","city":"Otranto","state":"CA"} {"index":{"_id":"358"}} {"account_number":358,"balance":44043,"firstname":"Hale","lastname":"Baldwin","age":40,"gender":"F","address":"845 Menahan Street","employer":"Kidgrease","email":"halebaldwin@kidgrease.com","city":"Day","state":"AK"} {"index":{"_id":"360"}} {"account_number":360,"balance":26651,"firstname":"Ward","lastname":"Hicks","age":34,"gender":"F","address":"592 Brighton Court","employer":"Biotica","email":"wardhicks@biotica.com","city":"Kanauga","state":"VT"} {"index":{"_id":"365"}} {"account_number":365,"balance":3176,"firstname":"Sanders","lastname":"Holder","age":31,"gender":"F","address":"453 Cypress Court","employer":"Geekola","email":"sandersholder@geekola.com","city":"Staples","state":"TN"} {"index":{"_id":"372"}} {"account_number":372,"balance":28566,"firstname":"Alba","lastname":"Forbes","age":24,"gender":"M","address":"814 Meserole Avenue","employer":"Isostream","email":"albaforbes@isostream.com","city":"Clarence","state":"OR"} {"index":{"_id":"377"}} {"account_number":377,"balance":5374,"firstname":"Margo","lastname":"Gay","age":34,"gender":"F","address":"613 Chase Court","employer":"Rotodyne","email":"margogay@rotodyne.com","city":"Waumandee","state":"KS"} {"index":{"_id":"384"}} {"account_number":384,"balance":48758,"firstname":"Sallie","lastname":"Houston","age":31,"gender":"F","address":"836 Polar Street","employer":"Squish","email":"salliehouston@squish.com","city":"Morningside","state":"NC"} {"index":{"_id":"389"}} {"account_number":389,"balance":8839,"firstname":"York","lastname":"Cummings","age":27,"gender":"M","address":"778 Centre Street","employer":"Insurity","email":"yorkcummings@insurity.com","city":"Freeburn","state":"RI"} {"index":{"_id":"391"}} {"account_number":391,"balance":14733,"firstname":"Holman","lastname":"Jordan","age":30,"gender":"M","address":"391 Forrest Street","employer":"Maineland","email":"holmanjordan@maineland.com","city":"Cade","state":"CT"} {"index":{"_id":"396"}} {"account_number":396,"balance":14613,"firstname":"Marsha","lastname":"Elliott","age":38,"gender":"F","address":"297 Liberty Avenue","employer":"Orbiflex","email":"marshaelliott@orbiflex.com","city":"Windsor","state":"TX"} {"index":{"_id":"404"}} {"account_number":404,"balance":34978,"firstname":"Massey","lastname":"Becker","age":26,"gender":"F","address":"930 Pitkin Avenue","employer":"Genekom","email":"masseybecker@genekom.com","city":"Blairstown","state":"OR"} {"index":{"_id":"409"}} {"account_number":409,"balance":36960,"firstname":"Maura","lastname":"Glenn","age":31,"gender":"M","address":"183 Poly Place","employer":"Viagreat","email":"mauraglenn@viagreat.com","city":"Foscoe","state":"DE"} {"index":{"_id":"411"}} {"account_number":411,"balance":1172,"firstname":"Guzman","lastname":"Whitfield","age":22,"gender":"M","address":"181 Perry Terrace","employer":"Springbee","email":"guzmanwhitfield@springbee.com","city":"Balm","state":"IN"} {"index":{"_id":"416"}} {"account_number":416,"balance":27169,"firstname":"Hunt","lastname":"Schwartz","age":28,"gender":"F","address":"461 Havens Place","employer":"Danja","email":"huntschwartz@danja.com","city":"Grenelefe","state":"NV"} {"index":{"_id":"423"}} {"account_number":423,"balance":38852,"firstname":"Hines","lastname":"Underwood","age":21,"gender":"F","address":"284 Louise Terrace","employer":"Namegen","email":"hinesunderwood@namegen.com","city":"Downsville","state":"CO"} {"index":{"_id":"428"}} {"account_number":428,"balance":13925,"firstname":"Stephens","lastname":"Cain","age":20,"gender":"F","address":"189 Summit Street","employer":"Rocklogic","email":"stephenscain@rocklogic.com","city":"Bourg","state":"HI"} {"index":{"_id":"430"}} {"account_number":430,"balance":15251,"firstname":"Alejandra","lastname":"Chavez","age":34,"gender":"M","address":"651 Butler Place","employer":"Gology","email":"alejandrachavez@gology.com","city":"Allensworth","state":"VT"} {"index":{"_id":"435"}} {"account_number":435,"balance":14654,"firstname":"Sue","lastname":"Lopez","age":22,"gender":"F","address":"632 Stone Avenue","employer":"Emergent","email":"suelopez@emergent.com","city":"Waterford","state":"TN"} {"index":{"_id":"442"}} {"account_number":442,"balance":36211,"firstname":"Lawanda","lastname":"Leon","age":27,"gender":"F","address":"126 Canal Avenue","employer":"Xixan","email":"lawandaleon@xixan.com","city":"Berwind","state":"TN"} {"index":{"_id":"447"}} {"account_number":447,"balance":11402,"firstname":"Lucia","lastname":"Livingston","age":35,"gender":"M","address":"773 Lake Avenue","employer":"Soprano","email":"lucialivingston@soprano.com","city":"Edgewater","state":"TN"} {"index":{"_id":"454"}} {"account_number":454,"balance":31687,"firstname":"Alicia","lastname":"Rollins","age":22,"gender":"F","address":"483 Verona Place","employer":"Boilcat","email":"aliciarollins@boilcat.com","city":"Lutsen","state":"MD"} {"index":{"_id":"459"}} {"account_number":459,"balance":18869,"firstname":"Pamela","lastname":"Henry","age":20,"gender":"F","address":"361 Locust Avenue","employer":"Imageflow","email":"pamelahenry@imageflow.com","city":"Greenfields","state":"OH"} {"index":{"_id":"461"}} {"account_number":461,"balance":38807,"firstname":"Mcbride","lastname":"Padilla","age":34,"gender":"F","address":"550 Borinquen Pl","employer":"Zepitope","email":"mcbridepadilla@zepitope.com","city":"Emory","state":"AZ"} {"index":{"_id":"466"}} {"account_number":466,"balance":25109,"firstname":"Marcie","lastname":"Mcmillan","age":30,"gender":"F","address":"947 Gain Court","employer":"Entroflex","email":"marciemcmillan@entroflex.com","city":"Ronco","state":"ND"} {"index":{"_id":"473"}} {"account_number":473,"balance":5391,"firstname":"Susan","lastname":"Luna","age":25,"gender":"F","address":"521 Bogart Street","employer":"Zaya","email":"susanluna@zaya.com","city":"Grazierville","state":"MI"} {"index":{"_id":"478"}} {"account_number":478,"balance":28044,"firstname":"Dana","lastname":"Decker","age":35,"gender":"M","address":"627 Dobbin Street","employer":"Acrodance","email":"danadecker@acrodance.com","city":"Sharon","state":"MN"} {"index":{"_id":"480"}} {"account_number":480,"balance":40807,"firstname":"Anastasia","lastname":"Parker","age":24,"gender":"M","address":"650 Folsom Place","employer":"Zilladyne","email":"anastasiaparker@zilladyne.com","city":"Oberlin","state":"WY"} {"index":{"_id":"485"}} {"account_number":485,"balance":44235,"firstname":"Albert","lastname":"Roberts","age":40,"gender":"M","address":"385 Harman Street","employer":"Stralum","email":"albertroberts@stralum.com","city":"Watrous","state":"NM"} {"index":{"_id":"492"}} {"account_number":492,"balance":31055,"firstname":"Burnett","lastname":"Briggs","age":35,"gender":"M","address":"987 Cass Place","employer":"Pharmex","email":"burnettbriggs@pharmex.com","city":"Cornfields","state":"TX"} {"index":{"_id":"497"}} {"account_number":497,"balance":13493,"firstname":"Doyle","lastname":"Jenkins","age":30,"gender":"M","address":"205 Nevins Street","employer":"Unia","email":"doylejenkins@unia.com","city":"Nicut","state":"DC"} {"index":{"_id":"500"}} {"account_number":500,"balance":39143,"firstname":"Pope","lastname":"Keith","age":28,"gender":"F","address":"537 Fane Court","employer":"Zboo","email":"popekeith@zboo.com","city":"Courtland","state":"AL"} {"index":{"_id":"505"}} {"account_number":505,"balance":45493,"firstname":"Shelley","lastname":"Webb","age":29,"gender":"M","address":"873 Crawford Avenue","employer":"Quadeebo","email":"shelleywebb@quadeebo.com","city":"Topanga","state":"IL"} {"index":{"_id":"512"}} {"account_number":512,"balance":47432,"firstname":"Alisha","lastname":"Morales","age":29,"gender":"M","address":"623 Batchelder Street","employer":"Terragen","email":"alishamorales@terragen.com","city":"Gilmore","state":"VA"} {"index":{"_id":"517"}} {"account_number":517,"balance":3022,"firstname":"Allyson","lastname":"Walls","age":38,"gender":"F","address":"334 Coffey Street","employer":"Gorganic","email":"allysonwalls@gorganic.com","city":"Dahlen","state":"GA"} {"index":{"_id":"524"}} {"account_number":524,"balance":49334,"firstname":"Salas","lastname":"Farley","age":30,"gender":"F","address":"499 Trucklemans Lane","employer":"Xumonk","email":"salasfarley@xumonk.com","city":"Noxen","state":"AL"} {"index":{"_id":"529"}} {"account_number":529,"balance":21788,"firstname":"Deann","lastname":"Fisher","age":23,"gender":"F","address":"511 Buffalo Avenue","employer":"Twiist","email":"deannfisher@twiist.com","city":"Templeton","state":"WA"} {"index":{"_id":"531"}} {"account_number":531,"balance":39770,"firstname":"Janet","lastname":"Pena","age":38,"gender":"M","address":"645 Livonia Avenue","employer":"Corecom","email":"janetpena@corecom.com","city":"Garberville","state":"OK"} {"index":{"_id":"536"}} {"account_number":536,"balance":6255,"firstname":"Emma","lastname":"Adkins","age":33,"gender":"F","address":"971 Calder Place","employer":"Ontagene","email":"emmaadkins@ontagene.com","city":"Ruckersville","state":"GA"} {"index":{"_id":"543"}} {"account_number":543,"balance":48022,"firstname":"Marina","lastname":"Rasmussen","age":31,"gender":"M","address":"446 Love Lane","employer":"Crustatia","email":"marinarasmussen@crustatia.com","city":"Statenville","state":"MD"} {"index":{"_id":"548"}} {"account_number":548,"balance":36930,"firstname":"Sandra","lastname":"Andrews","age":37,"gender":"M","address":"973 Prospect Street","employer":"Datagene","email":"sandraandrews@datagene.com","city":"Inkerman","state":"MO"} {"index":{"_id":"550"}} {"account_number":550,"balance":32238,"firstname":"Walsh","lastname":"Goodwin","age":22,"gender":"M","address":"953 Canda Avenue","employer":"Proflex","email":"walshgoodwin@proflex.com","city":"Ypsilanti","state":"MT"} {"index":{"_id":"555"}} {"account_number":555,"balance":10750,"firstname":"Fannie","lastname":"Slater","age":31,"gender":"M","address":"457 Tech Place","employer":"Kineticut","email":"fannieslater@kineticut.com","city":"Basye","state":"MO"} {"index":{"_id":"562"}} {"account_number":562,"balance":10737,"firstname":"Sarah","lastname":"Strong","age":39,"gender":"F","address":"177 Pioneer Street","employer":"Megall","email":"sarahstrong@megall.com","city":"Ladera","state":"WY"} {"index":{"_id":"567"}} {"account_number":567,"balance":6507,"firstname":"Diana","lastname":"Dominguez","age":40,"gender":"M","address":"419 Albany Avenue","employer":"Ohmnet","email":"dianadominguez@ohmnet.com","city":"Wildwood","state":"TX"} {"index":{"_id":"574"}} {"account_number":574,"balance":32954,"firstname":"Andrea","lastname":"Mosley","age":24,"gender":"M","address":"368 Throop Avenue","employer":"Musix","email":"andreamosley@musix.com","city":"Blende","state":"DC"} {"index":{"_id":"579"}} {"account_number":579,"balance":12044,"firstname":"Banks","lastname":"Sawyer","age":36,"gender":"M","address":"652 Doone Court","employer":"Rooforia","email":"bankssawyer@rooforia.com","city":"Foxworth","state":"ND"} {"index":{"_id":"581"}} {"account_number":581,"balance":16525,"firstname":"Fuller","lastname":"Mcintyre","age":32,"gender":"M","address":"169 Bergen Place","employer":"Applideck","email":"fullermcintyre@applideck.com","city":"Kenvil","state":"NY"} {"index":{"_id":"586"}} {"account_number":586,"balance":13644,"firstname":"Love","lastname":"Velasquez","age":26,"gender":"F","address":"290 Girard Street","employer":"Zomboid","email":"lovevelasquez@zomboid.com","city":"Villarreal","state":"SD"} {"index":{"_id":"593"}} {"account_number":593,"balance":41230,"firstname":"Muriel","lastname":"Vazquez","age":37,"gender":"M","address":"395 Montgomery Street","employer":"Sustenza","email":"murielvazquez@sustenza.com","city":"Strykersville","state":"OK"} {"index":{"_id":"598"}} {"account_number":598,"balance":33251,"firstname":"Morgan","lastname":"Coleman","age":33,"gender":"M","address":"324 McClancy Place","employer":"Aclima","email":"morgancoleman@aclima.com","city":"Bowden","state":"WA"} {"index":{"_id":"601"}} {"account_number":601,"balance":20796,"firstname":"Vickie","lastname":"Valentine","age":34,"gender":"F","address":"432 Bassett Avenue","employer":"Comvene","email":"vickievalentine@comvene.com","city":"Teasdale","state":"UT"} {"index":{"_id":"606"}} {"account_number":606,"balance":28770,"firstname":"Michael","lastname":"Bray","age":31,"gender":"M","address":"935 Lake Place","employer":"Telepark","email":"michaelbray@telepark.com","city":"Lemoyne","state":"CT"} {"index":{"_id":"613"}} {"account_number":613,"balance":39340,"firstname":"Eddie","lastname":"Mccarty","age":34,"gender":"F","address":"971 Richards Street","employer":"Bisba","email":"eddiemccarty@bisba.com","city":"Fruitdale","state":"NY"} {"index":{"_id":"618"}} {"account_number":618,"balance":8976,"firstname":"Cheri","lastname":"Ford","age":30,"gender":"F","address":"803 Ridgewood Avenue","employer":"Zorromop","email":"cheriford@zorromop.com","city":"Gambrills","state":"VT"} {"index":{"_id":"620"}} {"account_number":620,"balance":7224,"firstname":"Coleen","lastname":"Bartlett","age":38,"gender":"M","address":"761 Carroll Street","employer":"Idealis","email":"coleenbartlett@idealis.com","city":"Mathews","state":"DE"} {"index":{"_id":"625"}} {"account_number":625,"balance":46010,"firstname":"Cynthia","lastname":"Johnston","age":23,"gender":"M","address":"142 Box Street","employer":"Zentry","email":"cynthiajohnston@zentry.com","city":"Makena","state":"MA"} {"index":{"_id":"632"}} {"account_number":632,"balance":40470,"firstname":"Kay","lastname":"Warren","age":20,"gender":"F","address":"422 Alabama Avenue","employer":"Realysis","email":"kaywarren@realysis.com","city":"Homestead","state":"HI"} {"index":{"_id":"637"}} {"account_number":637,"balance":3169,"firstname":"Kathy","lastname":"Carter","age":27,"gender":"F","address":"410 Jamison Lane","employer":"Limage","email":"kathycarter@limage.com","city":"Ernstville","state":"WA"} {"index":{"_id":"644"}} {"account_number":644,"balance":44021,"firstname":"Etta","lastname":"Miller","age":21,"gender":"F","address":"376 Lawton Street","employer":"Bluegrain","email":"ettamiller@bluegrain.com","city":"Baker","state":"MD"} {"index":{"_id":"649"}} {"account_number":649,"balance":20275,"firstname":"Jeanine","lastname":"Malone","age":26,"gender":"F","address":"114 Dodworth Street","employer":"Nixelt","email":"jeaninemalone@nixelt.com","city":"Keyport","state":"AK"} {"index":{"_id":"651"}} {"account_number":651,"balance":18360,"firstname":"Young","lastname":"Reeves","age":34,"gender":"M","address":"581 Plaza Street","employer":"Krog","email":"youngreeves@krog.com","city":"Sussex","state":"WY"} {"index":{"_id":"656"}} {"account_number":656,"balance":21632,"firstname":"Olson","lastname":"Hunt","age":36,"gender":"M","address":"342 Jaffray Street","employer":"Volax","email":"olsonhunt@volax.com","city":"Bangor","state":"WA"} {"index":{"_id":"663"}} {"account_number":663,"balance":2456,"firstname":"Rollins","lastname":"Richards","age":37,"gender":"M","address":"129 Sullivan Place","employer":"Geostele","email":"rollinsrichards@geostele.com","city":"Morgandale","state":"FL"} {"index":{"_id":"668"}} {"account_number":668,"balance":45069,"firstname":"Potter","lastname":"Michael","age":27,"gender":"M","address":"803 Glenmore Avenue","employer":"Ontality","email":"pottermichael@ontality.com","city":"Newkirk","state":"KS"} {"index":{"_id":"670"}} {"account_number":670,"balance":10178,"firstname":"Ollie","lastname":"Riley","age":22,"gender":"M","address":"252 Jackson Place","employer":"Adornica","email":"ollieriley@adornica.com","city":"Brethren","state":"WI"} {"index":{"_id":"675"}} {"account_number":675,"balance":36102,"firstname":"Fisher","lastname":"Shepard","age":27,"gender":"F","address":"859 Varick Street","employer":"Qot","email":"fishershepard@qot.com","city":"Diaperville","state":"MD"} {"index":{"_id":"682"}} {"account_number":682,"balance":14168,"firstname":"Anne","lastname":"Hale","age":22,"gender":"F","address":"708 Anthony Street","employer":"Cytrek","email":"annehale@cytrek.com","city":"Beechmont","state":"WV"} {"index":{"_id":"687"}} {"account_number":687,"balance":48630,"firstname":"Caroline","lastname":"Cox","age":31,"gender":"M","address":"626 Hillel Place","employer":"Opticon","email":"carolinecox@opticon.com","city":"Loma","state":"ND"} {"index":{"_id":"694"}} {"account_number":694,"balance":33125,"firstname":"Craig","lastname":"Palmer","age":31,"gender":"F","address":"273 Montrose Avenue","employer":"Comvey","email":"craigpalmer@comvey.com","city":"Cleary","state":"OK"} {"index":{"_id":"699"}} {"account_number":699,"balance":4156,"firstname":"Gallagher","lastname":"Marshall","age":37,"gender":"F","address":"648 Clifford Place","employer":"Exiand","email":"gallaghermarshall@exiand.com","city":"Belfair","state":"KY"} {"index":{"_id":"702"}} {"account_number":702,"balance":46490,"firstname":"Meadows","lastname":"Delgado","age":26,"gender":"M","address":"612 Jardine Place","employer":"Daisu","email":"meadowsdelgado@daisu.com","city":"Venice","state":"AR"} {"index":{"_id":"707"}} {"account_number":707,"balance":30325,"firstname":"Sonya","lastname":"Trevino","age":30,"gender":"F","address":"181 Irving Place","employer":"Atgen","email":"sonyatrevino@atgen.com","city":"Enetai","state":"TN"} {"index":{"_id":"714"}} {"account_number":714,"balance":16602,"firstname":"Socorro","lastname":"Murray","age":34,"gender":"F","address":"810 Manhattan Court","employer":"Isoswitch","email":"socorromurray@isoswitch.com","city":"Jugtown","state":"AZ"} {"index":{"_id":"719"}} {"account_number":719,"balance":33107,"firstname":"Leanna","lastname":"Reed","age":25,"gender":"F","address":"528 Krier Place","employer":"Rodeology","email":"leannareed@rodeology.com","city":"Carrizo","state":"WI"} {"index":{"_id":"721"}} {"account_number":721,"balance":32958,"firstname":"Mara","lastname":"Dickson","age":26,"gender":"M","address":"810 Harrison Avenue","employer":"Comtours","email":"maradickson@comtours.com","city":"Thynedale","state":"DE"} {"index":{"_id":"726"}} {"account_number":726,"balance":44737,"firstname":"Rosemary","lastname":"Salazar","age":21,"gender":"M","address":"290 Croton Loop","employer":"Rockabye","email":"rosemarysalazar@rockabye.com","city":"Helen","state":"IA"} {"index":{"_id":"733"}} {"account_number":733,"balance":15722,"firstname":"Lakeisha","lastname":"Mccarthy","age":37,"gender":"M","address":"782 Turnbull Avenue","employer":"Exosis","email":"lakeishamccarthy@exosis.com","city":"Caberfae","state":"NM"} {"index":{"_id":"738"}} {"account_number":738,"balance":44936,"firstname":"Rosalind","lastname":"Hunter","age":32,"gender":"M","address":"644 Eaton Court","employer":"Zolarity","email":"rosalindhunter@zolarity.com","city":"Cataract","state":"SD"} {"index":{"_id":"740"}} {"account_number":740,"balance":6143,"firstname":"Chambers","lastname":"Hahn","age":22,"gender":"M","address":"937 Windsor Place","employer":"Medalert","email":"chambershahn@medalert.com","city":"Dorneyville","state":"DC"} {"index":{"_id":"745"}} {"account_number":745,"balance":4572,"firstname":"Jacobs","lastname":"Sweeney","age":32,"gender":"M","address":"189 Lott Place","employer":"Comtent","email":"jacobssweeney@comtent.com","city":"Advance","state":"NJ"} {"index":{"_id":"752"}} {"account_number":752,"balance":14039,"firstname":"Jerry","lastname":"Rush","age":31,"gender":"M","address":"632 Dank Court","employer":"Ebidco","email":"jerryrush@ebidco.com","city":"Geyserville","state":"AR"} {"index":{"_id":"757"}} {"account_number":757,"balance":34628,"firstname":"Mccullough","lastname":"Moore","age":30,"gender":"F","address":"304 Hastings Street","employer":"Nikuda","email":"mcculloughmoore@nikuda.com","city":"Charco","state":"DC"} {"index":{"_id":"764"}} {"account_number":764,"balance":3728,"firstname":"Noemi","lastname":"Gill","age":30,"gender":"M","address":"427 Chester Street","employer":"Avit","email":"noemigill@avit.com","city":"Chesterfield","state":"AL"} {"index":{"_id":"769"}} {"account_number":769,"balance":15362,"firstname":"Francis","lastname":"Beck","age":28,"gender":"M","address":"454 Livingston Street","employer":"Furnafix","email":"francisbeck@furnafix.com","city":"Dunnavant","state":"HI"} {"index":{"_id":"771"}} {"account_number":771,"balance":32784,"firstname":"Jocelyn","lastname":"Boone","age":23,"gender":"M","address":"513 Division Avenue","employer":"Collaire","email":"jocelynboone@collaire.com","city":"Lisco","state":"VT"} {"index":{"_id":"776"}} {"account_number":776,"balance":29177,"firstname":"Duke","lastname":"Atkinson","age":24,"gender":"M","address":"520 Doscher Street","employer":"Tripsch","email":"dukeatkinson@tripsch.com","city":"Lafferty","state":"NC"} {"index":{"_id":"783"}} {"account_number":783,"balance":11911,"firstname":"Faith","lastname":"Cooper","age":25,"gender":"F","address":"539 Rapelye Street","employer":"Insuron","email":"faithcooper@insuron.com","city":"Jennings","state":"MN"} {"index":{"_id":"788"}} {"account_number":788,"balance":12473,"firstname":"Marianne","lastname":"Aguilar","age":39,"gender":"F","address":"213 Holly Street","employer":"Marqet","email":"marianneaguilar@marqet.com","city":"Alfarata","state":"HI"} {"index":{"_id":"790"}} {"account_number":790,"balance":29912,"firstname":"Ellis","lastname":"Sullivan","age":39,"gender":"F","address":"877 Coyle Street","employer":"Enersave","email":"ellissullivan@enersave.com","city":"Canby","state":"MS"} {"index":{"_id":"795"}} {"account_number":795,"balance":31450,"firstname":"Bruce","lastname":"Avila","age":34,"gender":"M","address":"865 Newkirk Placez","employer":"Plasmosis","email":"bruceavila@plasmosis.com","city":"Ada","state":"ID"} {"index":{"_id":"803"}} {"account_number":803,"balance":49567,"firstname":"Marissa","lastname":"Spears","age":25,"gender":"M","address":"963 Highland Avenue","employer":"Centregy","email":"marissaspears@centregy.com","city":"Bloomington","state":"MS"} {"index":{"_id":"808"}} {"account_number":808,"balance":11251,"firstname":"Nola","lastname":"Quinn","age":20,"gender":"M","address":"863 Wythe Place","employer":"Iplax","email":"nolaquinn@iplax.com","city":"Cuylerville","state":"NH"} {"index":{"_id":"810"}} {"account_number":810,"balance":10563,"firstname":"Alyssa","lastname":"Ortega","age":40,"gender":"M","address":"977 Clymer Street","employer":"Eventage","email":"alyssaortega@eventage.com","city":"Convent","state":"SC"} {"index":{"_id":"815"}} {"account_number":815,"balance":19336,"firstname":"Guthrie","lastname":"Morse","age":30,"gender":"M","address":"685 Vandalia Avenue","employer":"Gronk","email":"guthriemorse@gronk.com","city":"Fowlerville","state":"OR"} {"index":{"_id":"822"}} {"account_number":822,"balance":13024,"firstname":"Hicks","lastname":"Farrell","age":25,"gender":"M","address":"468 Middleton Street","employer":"Zolarex","email":"hicksfarrell@zolarex.com","city":"Columbus","state":"OR"} {"index":{"_id":"827"}} {"account_number":827,"balance":37536,"firstname":"Naomi","lastname":"Ball","age":29,"gender":"F","address":"319 Stewart Street","employer":"Isotronic","email":"naomiball@isotronic.com","city":"Trona","state":"NM"} {"index":{"_id":"834"}} {"account_number":834,"balance":38049,"firstname":"Sybil","lastname":"Carrillo","age":25,"gender":"M","address":"359 Baughman Place","employer":"Phuel","email":"sybilcarrillo@phuel.com","city":"Kohatk","state":"CT"} {"index":{"_id":"839"}} {"account_number":839,"balance":38292,"firstname":"Langley","lastname":"Neal","age":39,"gender":"F","address":"565 Newton Street","employer":"Liquidoc","email":"langleyneal@liquidoc.com","city":"Osage","state":"AL"} {"index":{"_id":"841"}} {"account_number":841,"balance":28291,"firstname":"Dalton","lastname":"Waters","age":21,"gender":"M","address":"859 Grand Street","employer":"Malathion","email":"daltonwaters@malathion.com","city":"Tonopah","state":"AZ"} {"index":{"_id":"846"}} {"account_number":846,"balance":35099,"firstname":"Maureen","lastname":"Glass","age":22,"gender":"M","address":"140 Amherst Street","employer":"Stelaecor","email":"maureenglass@stelaecor.com","city":"Cucumber","state":"IL"} {"index":{"_id":"853"}} {"account_number":853,"balance":38353,"firstname":"Travis","lastname":"Parks","age":40,"gender":"M","address":"930 Bay Avenue","employer":"Pyramax","email":"travisparks@pyramax.com","city":"Gadsden","state":"ND"} {"index":{"_id":"858"}} {"account_number":858,"balance":23194,"firstname":"Small","lastname":"Hatfield","age":36,"gender":"M","address":"593 Tennis Court","employer":"Letpro","email":"smallhatfield@letpro.com","city":"Haena","state":"KS"} {"index":{"_id":"860"}} {"account_number":860,"balance":23613,"firstname":"Clark","lastname":"Boyd","age":37,"gender":"M","address":"501 Rock Street","employer":"Deepends","email":"clarkboyd@deepends.com","city":"Whitewater","state":"MA"} {"index":{"_id":"865"}} {"account_number":865,"balance":10574,"firstname":"Cook","lastname":"Kelley","age":28,"gender":"F","address":"865 Lincoln Terrace","employer":"Quizmo","email":"cookkelley@quizmo.com","city":"Kansas","state":"KY"} {"index":{"_id":"872"}} {"account_number":872,"balance":26314,"firstname":"Jane","lastname":"Greer","age":36,"gender":"F","address":"717 Hewes Street","employer":"Newcube","email":"janegreer@newcube.com","city":"Delshire","state":"DE"} {"index":{"_id":"877"}} {"account_number":877,"balance":42879,"firstname":"Tracey","lastname":"Ruiz","age":34,"gender":"F","address":"141 Tompkins Avenue","employer":"Waab","email":"traceyruiz@waab.com","city":"Zeba","state":"NM"} {"index":{"_id":"884"}} {"account_number":884,"balance":29316,"firstname":"Reva","lastname":"Rosa","age":40,"gender":"M","address":"784 Greene Avenue","employer":"Urbanshee","email":"revarosa@urbanshee.com","city":"Bakersville","state":"MS"} {"index":{"_id":"889"}} {"account_number":889,"balance":26464,"firstname":"Fischer","lastname":"Klein","age":38,"gender":"F","address":"948 Juliana Place","employer":"Comtext","email":"fischerklein@comtext.com","city":"Jackpot","state":"PA"} {"index":{"_id":"891"}} {"account_number":891,"balance":34829,"firstname":"Jacobson","lastname":"Clemons","age":24,"gender":"F","address":"507 Wilson Street","employer":"Quilm","email":"jacobsonclemons@quilm.com","city":"Muir","state":"TX"} {"index":{"_id":"896"}} {"account_number":896,"balance":31947,"firstname":"Buckley","lastname":"Peterson","age":26,"gender":"M","address":"217 Beayer Place","employer":"Earwax","email":"buckleypeterson@earwax.com","city":"Franklin","state":"DE"} {"index":{"_id":"904"}} {"account_number":904,"balance":27707,"firstname":"Mendez","lastname":"Mcneil","age":26,"gender":"M","address":"431 Halsey Street","employer":"Macronaut","email":"mendezmcneil@macronaut.com","city":"Troy","state":"OK"} {"index":{"_id":"909"}} {"account_number":909,"balance":18421,"firstname":"Stark","lastname":"Lewis","age":36,"gender":"M","address":"409 Tilden Avenue","employer":"Frosnex","email":"starklewis@frosnex.com","city":"Axis","state":"CA"} {"index":{"_id":"911"}} {"account_number":911,"balance":42655,"firstname":"Annie","lastname":"Lyons","age":21,"gender":"M","address":"518 Woods Place","employer":"Enerforce","email":"annielyons@enerforce.com","city":"Stagecoach","state":"MA"} {"index":{"_id":"916"}} {"account_number":916,"balance":47887,"firstname":"Jarvis","lastname":"Alexander","age":40,"gender":"M","address":"406 Bergen Avenue","employer":"Equitax","email":"jarvisalexander@equitax.com","city":"Haring","state":"KY"} {"index":{"_id":"923"}} {"account_number":923,"balance":48466,"firstname":"Mueller","lastname":"Mckee","age":26,"gender":"M","address":"298 Ruby Street","employer":"Luxuria","email":"muellermckee@luxuria.com","city":"Coleville","state":"TN"} {"index":{"_id":"928"}} {"account_number":928,"balance":19611,"firstname":"Hester","lastname":"Copeland","age":22,"gender":"F","address":"425 Cropsey Avenue","employer":"Dymi","email":"hestercopeland@dymi.com","city":"Wolcott","state":"NE"} {"index":{"_id":"930"}} {"account_number":930,"balance":47257,"firstname":"Kinney","lastname":"Lawson","age":39,"gender":"M","address":"501 Raleigh Place","employer":"Neptide","email":"kinneylawson@neptide.com","city":"Deltaville","state":"MD"} {"index":{"_id":"935"}} {"account_number":935,"balance":4959,"firstname":"Flowers","lastname":"Robles","age":30,"gender":"M","address":"201 Hull Street","employer":"Xelegyl","email":"flowersrobles@xelegyl.com","city":"Rehrersburg","state":"AL"} {"index":{"_id":"942"}} {"account_number":942,"balance":21299,"firstname":"Hamilton","lastname":"Clayton","age":26,"gender":"M","address":"413 Debevoise Street","employer":"Architax","email":"hamiltonclayton@architax.com","city":"Terlingua","state":"NM"} {"index":{"_id":"947"}} {"account_number":947,"balance":22039,"firstname":"Virgie","lastname":"Garza","age":30,"gender":"M","address":"903 Matthews Court","employer":"Plasmox","email":"virgiegarza@plasmox.com","city":"Somerset","state":"WY"} {"index":{"_id":"954"}} {"account_number":954,"balance":49404,"firstname":"Jenna","lastname":"Martin","age":22,"gender":"M","address":"688 Hart Street","employer":"Zinca","email":"jennamartin@zinca.com","city":"Oasis","state":"MD"} {"index":{"_id":"959"}} {"account_number":959,"balance":34743,"firstname":"Shaffer","lastname":"Cervantes","age":40,"gender":"M","address":"931 Varick Avenue","employer":"Oceanica","email":"shaffercervantes@oceanica.com","city":"Bowie","state":"AL"} {"index":{"_id":"961"}} {"account_number":961,"balance":43219,"firstname":"Betsy","lastname":"Hyde","age":27,"gender":"F","address":"183 Junius Street","employer":"Tubalum","email":"betsyhyde@tubalum.com","city":"Driftwood","state":"TX"} {"index":{"_id":"966"}} {"account_number":966,"balance":20619,"firstname":"Susanne","lastname":"Rodriguez","age":35,"gender":"F","address":"255 Knickerbocker Avenue","employer":"Comtrek","email":"susannerodriguez@comtrek.com","city":"Trinway","state":"TX"} {"index":{"_id":"973"}} {"account_number":973,"balance":45756,"firstname":"Rice","lastname":"Farmer","age":31,"gender":"M","address":"476 Nassau Avenue","employer":"Photobin","email":"ricefarmer@photobin.com","city":"Suitland","state":"ME"} {"index":{"_id":"978"}} {"account_number":978,"balance":21459,"firstname":"Melanie","lastname":"Rojas","age":33,"gender":"M","address":"991 Java Street","employer":"Kage","email":"melanierojas@kage.com","city":"Greenock","state":"VT"} {"index":{"_id":"980"}} {"account_number":980,"balance":42436,"firstname":"Cash","lastname":"Collier","age":33,"gender":"F","address":"999 Sapphire Street","employer":"Ceprene","email":"cashcollier@ceprene.com","city":"Glidden","state":"AK"} {"index":{"_id":"985"}} {"account_number":985,"balance":20083,"firstname":"Martin","lastname":"Gardner","age":28,"gender":"F","address":"644 Fairview Place","employer":"Golistic","email":"martingardner@golistic.com","city":"Connerton","state":"NJ"} {"index":{"_id":"992"}} {"account_number":992,"balance":11413,"firstname":"Kristie","lastname":"Kennedy","age":33,"gender":"F","address":"750 Hudson Avenue","employer":"Ludak","email":"kristiekennedy@ludak.com","city":"Warsaw","state":"WY"} {"index":{"_id":"997"}} {"account_number":997,"balance":25311,"firstname":"Combs","lastname":"Frederick","age":20,"gender":"M","address":"586 Lloyd Court","employer":"Pathways","email":"combsfrederick@pathways.com","city":"Williamson","state":"CA"} {"index":{"_id":"3"}} {"account_number":3,"balance":44947,"firstname":"Levine","lastname":"Burks","age":26,"gender":"F","address":"328 Wilson Avenue","employer":"Amtap","email":"levineburks@amtap.com","city":"Cochranville","state":"HI"} {"index":{"_id":"8"}} {"account_number":8,"balance":48868,"firstname":"Jan","lastname":"Burns","age":35,"gender":"M","address":"699 Visitation Place","employer":"Glasstep","email":"janburns@glasstep.com","city":"Wakulla","state":"AZ"} {"index":{"_id":"10"}} {"account_number":10,"balance":46170,"firstname":"Dominique","lastname":"Park","age":37,"gender":"F","address":"100 Gatling Place","employer":"Conjurica","email":"dominiquepark@conjurica.com","city":"Omar","state":"NJ"} {"index":{"_id":"15"}} {"account_number":15,"balance":43456,"firstname":"Bobbie","lastname":"Sexton","age":21,"gender":"M","address":"232 Sedgwick Place","employer":"Zytrex","email":"bobbiesexton@zytrex.com","city":"Hendersonville","state":"CA"} {"index":{"_id":"22"}} {"account_number":22,"balance":40283,"firstname":"Barrera","lastname":"Terrell","age":23,"gender":"F","address":"292 Orange Street","employer":"Steelfab","email":"barreraterrell@steelfab.com","city":"Bynum","state":"ME"} {"index":{"_id":"27"}} {"account_number":27,"balance":6176,"firstname":"Meyers","lastname":"Williamson","age":26,"gender":"F","address":"675 Henderson Walk","employer":"Plexia","email":"meyerswilliamson@plexia.com","city":"Richmond","state":"AZ"} {"index":{"_id":"34"}} {"account_number":34,"balance":35379,"firstname":"Ellison","lastname":"Kim","age":30,"gender":"F","address":"986 Revere Place","employer":"Signity","email":"ellisonkim@signity.com","city":"Sehili","state":"IL"} {"index":{"_id":"39"}} {"account_number":39,"balance":38688,"firstname":"Bowers","lastname":"Mendez","age":22,"gender":"F","address":"665 Bennet Court","employer":"Farmage","email":"bowersmendez@farmage.com","city":"Duryea","state":"PA"} {"index":{"_id":"41"}} {"account_number":41,"balance":36060,"firstname":"Hancock","lastname":"Holden","age":20,"gender":"M","address":"625 Gaylord Drive","employer":"Poochies","email":"hancockholden@poochies.com","city":"Alamo","state":"KS"} {"index":{"_id":"46"}} {"account_number":46,"balance":12351,"firstname":"Karla","lastname":"Bowman","age":23,"gender":"M","address":"554 Chapel Street","employer":"Undertap","email":"karlabowman@undertap.com","city":"Sylvanite","state":"DC"} {"index":{"_id":"53"}} {"account_number":53,"balance":28101,"firstname":"Kathryn","lastname":"Payne","age":29,"gender":"F","address":"467 Louis Place","employer":"Katakana","email":"kathrynpayne@katakana.com","city":"Harviell","state":"SD"} {"index":{"_id":"58"}} {"account_number":58,"balance":31697,"firstname":"Marva","lastname":"Cannon","age":40,"gender":"M","address":"993 Highland Place","employer":"Comcubine","email":"marvacannon@comcubine.com","city":"Orviston","state":"MO"} {"index":{"_id":"60"}} {"account_number":60,"balance":45955,"firstname":"Maude","lastname":"Casey","age":31,"gender":"F","address":"566 Strauss Street","employer":"Quilch","email":"maudecasey@quilch.com","city":"Enlow","state":"GA"} {"index":{"_id":"65"}} {"account_number":65,"balance":23282,"firstname":"Leonor","lastname":"Pruitt","age":24,"gender":"M","address":"974 Terrace Place","employer":"Velos","email":"leonorpruitt@velos.com","city":"Devon","state":"WI"} {"index":{"_id":"72"}} {"account_number":72,"balance":9732,"firstname":"Barlow","lastname":"Rhodes","age":25,"gender":"F","address":"891 Clinton Avenue","employer":"Zialactic","email":"barlowrhodes@zialactic.com","city":"Echo","state":"TN"} {"index":{"_id":"77"}} {"account_number":77,"balance":5724,"firstname":"Byrd","lastname":"Conley","age":24,"gender":"F","address":"698 Belmont Avenue","employer":"Zidox","email":"byrdconley@zidox.com","city":"Rockbridge","state":"SC"} {"index":{"_id":"84"}} {"account_number":84,"balance":3001,"firstname":"Hutchinson","lastname":"Newton","age":34,"gender":"F","address":"553 Locust Street","employer":"Zaggles","email":"hutchinsonnewton@zaggles.com","city":"Snyderville","state":"DC"} {"index":{"_id":"89"}} {"account_number":89,"balance":13263,"firstname":"Mcdowell","lastname":"Bradley","age":28,"gender":"M","address":"960 Howard Alley","employer":"Grok","email":"mcdowellbradley@grok.com","city":"Toftrees","state":"TX"} {"index":{"_id":"91"}} {"account_number":91,"balance":29799,"firstname":"Vonda","lastname":"Galloway","age":20,"gender":"M","address":"988 Voorhies Avenue","employer":"Illumity","email":"vondagalloway@illumity.com","city":"Holcombe","state":"HI"} {"index":{"_id":"96"}} {"account_number":96,"balance":15933,"firstname":"Shirley","lastname":"Edwards","age":38,"gender":"M","address":"817 Caton Avenue","employer":"Equitox","email":"shirleyedwards@equitox.com","city":"Nelson","state":"MA"} {"index":{"_id":"104"}} {"account_number":104,"balance":32619,"firstname":"Casey","lastname":"Roth","age":29,"gender":"M","address":"963 Railroad Avenue","employer":"Hotcakes","email":"caseyroth@hotcakes.com","city":"Davenport","state":"OH"} {"index":{"_id":"109"}} {"account_number":109,"balance":25812,"firstname":"Gretchen","lastname":"Dawson","age":31,"gender":"M","address":"610 Bethel Loop","employer":"Tetak","email":"gretchendawson@tetak.com","city":"Hailesboro","state":"CO"} {"index":{"_id":"111"}} {"account_number":111,"balance":1481,"firstname":"Traci","lastname":"Allison","age":35,"gender":"M","address":"922 Bryant Street","employer":"Enjola","email":"traciallison@enjola.com","city":"Robinette","state":"OR"} {"index":{"_id":"116"}} {"account_number":116,"balance":21335,"firstname":"Hobbs","lastname":"Wright","age":24,"gender":"M","address":"965 Temple Court","employer":"Netbook","email":"hobbswright@netbook.com","city":"Strong","state":"CA"} {"index":{"_id":"123"}} {"account_number":123,"balance":3079,"firstname":"Cleo","lastname":"Beach","age":27,"gender":"F","address":"653 Haring Street","employer":"Proxsoft","email":"cleobeach@proxsoft.com","city":"Greensburg","state":"ME"} {"index":{"_id":"128"}} {"account_number":128,"balance":3556,"firstname":"Mack","lastname":"Bullock","age":34,"gender":"F","address":"462 Ingraham Street","employer":"Terascape","email":"mackbullock@terascape.com","city":"Eureka","state":"PA"} {"index":{"_id":"130"}} {"account_number":130,"balance":24171,"firstname":"Roxie","lastname":"Cantu","age":33,"gender":"M","address":"841 Catherine Street","employer":"Skybold","email":"roxiecantu@skybold.com","city":"Deputy","state":"NE"} {"index":{"_id":"135"}} {"account_number":135,"balance":24885,"firstname":"Stevenson","lastname":"Crosby","age":40,"gender":"F","address":"473 Boardwalk ","employer":"Accel","email":"stevensoncrosby@accel.com","city":"Norris","state":"OK"} {"index":{"_id":"142"}} {"account_number":142,"balance":4544,"firstname":"Vang","lastname":"Hughes","age":27,"gender":"M","address":"357 Landis Court","employer":"Bolax","email":"vanghughes@bolax.com","city":"Emerald","state":"WY"} {"index":{"_id":"147"}} {"account_number":147,"balance":35921,"firstname":"Charmaine","lastname":"Whitney","age":28,"gender":"F","address":"484 Seton Place","employer":"Comveyer","email":"charmainewhitney@comveyer.com","city":"Dexter","state":"DC"} {"index":{"_id":"154"}} {"account_number":154,"balance":40945,"firstname":"Burns","lastname":"Solis","age":31,"gender":"M","address":"274 Lorraine Street","employer":"Rodemco","email":"burnssolis@rodemco.com","city":"Ballico","state":"WI"} {"index":{"_id":"159"}} {"account_number":159,"balance":1696,"firstname":"Alvarez","lastname":"Mack","age":22,"gender":"F","address":"897 Manor Court","employer":"Snorus","email":"alvarezmack@snorus.com","city":"Rosedale","state":"CA"} {"index":{"_id":"161"}} {"account_number":161,"balance":4659,"firstname":"Doreen","lastname":"Randall","age":37,"gender":"F","address":"178 Court Street","employer":"Calcula","email":"doreenrandall@calcula.com","city":"Belmont","state":"TX"} {"index":{"_id":"166"}} {"account_number":166,"balance":33847,"firstname":"Rutledge","lastname":"Rivas","age":23,"gender":"M","address":"352 Verona Street","employer":"Virxo","email":"rutledgerivas@virxo.com","city":"Brandermill","state":"NE"} {"index":{"_id":"173"}} {"account_number":173,"balance":5989,"firstname":"Whitley","lastname":"Blevins","age":32,"gender":"M","address":"127 Brooklyn Avenue","employer":"Pawnagra","email":"whitleyblevins@pawnagra.com","city":"Rodanthe","state":"ND"} {"index":{"_id":"178"}} {"account_number":178,"balance":36735,"firstname":"Clements","lastname":"Finley","age":39,"gender":"F","address":"270 Story Court","employer":"Imaginart","email":"clementsfinley@imaginart.com","city":"Lookingglass","state":"MN"} {"index":{"_id":"180"}} {"account_number":180,"balance":34236,"firstname":"Ursula","lastname":"Goodman","age":32,"gender":"F","address":"414 Clinton Street","employer":"Earthmark","email":"ursulagoodman@earthmark.com","city":"Rote","state":"AR"} {"index":{"_id":"185"}} {"account_number":185,"balance":43532,"firstname":"Laurel","lastname":"Cline","age":40,"gender":"M","address":"788 Fenimore Street","employer":"Prismatic","email":"laurelcline@prismatic.com","city":"Frank","state":"UT"} {"index":{"_id":"192"}} {"account_number":192,"balance":23508,"firstname":"Ramsey","lastname":"Carr","age":31,"gender":"F","address":"209 Williamsburg Street","employer":"Strezzo","email":"ramseycarr@strezzo.com","city":"Grapeview","state":"NM"} {"index":{"_id":"197"}} {"account_number":197,"balance":17246,"firstname":"Sweet","lastname":"Sanders","age":33,"gender":"F","address":"712 Homecrest Court","employer":"Isosure","email":"sweetsanders@isosure.com","city":"Sheatown","state":"VT"} {"index":{"_id":"200"}} {"account_number":200,"balance":26210,"firstname":"Teri","lastname":"Hester","age":39,"gender":"M","address":"653 Abbey Court","employer":"Electonic","email":"terihester@electonic.com","city":"Martell","state":"MD"} {"index":{"_id":"205"}} {"account_number":205,"balance":45493,"firstname":"Johnson","lastname":"Chang","age":28,"gender":"F","address":"331 John Street","employer":"Gleamink","email":"johnsonchang@gleamink.com","city":"Sultana","state":"KS"} {"index":{"_id":"212"}} {"account_number":212,"balance":10299,"firstname":"Marisol","lastname":"Fischer","age":39,"gender":"M","address":"362 Prince Street","employer":"Autograte","email":"marisolfischer@autograte.com","city":"Oley","state":"SC"} {"index":{"_id":"217"}} {"account_number":217,"balance":33730,"firstname":"Sally","lastname":"Mccoy","age":38,"gender":"F","address":"854 Corbin Place","employer":"Omnigog","email":"sallymccoy@omnigog.com","city":"Escondida","state":"FL"} {"index":{"_id":"224"}} {"account_number":224,"balance":42708,"firstname":"Billie","lastname":"Nixon","age":28,"gender":"F","address":"241 Kaufman Place","employer":"Xanide","email":"billienixon@xanide.com","city":"Chapin","state":"NY"} {"index":{"_id":"229"}} {"account_number":229,"balance":2740,"firstname":"Jana","lastname":"Hensley","age":30,"gender":"M","address":"176 Erasmus Street","employer":"Isotrack","email":"janahensley@isotrack.com","city":"Caledonia","state":"ME"} {"index":{"_id":"231"}} {"account_number":231,"balance":46180,"firstname":"Essie","lastname":"Clarke","age":34,"gender":"F","address":"308 Harbor Lane","employer":"Pharmacon","email":"essieclarke@pharmacon.com","city":"Fillmore","state":"MS"} {"index":{"_id":"236"}} {"account_number":236,"balance":41200,"firstname":"Suzanne","lastname":"Bird","age":39,"gender":"F","address":"219 Luquer Street","employer":"Imant","email":"suzannebird@imant.com","city":"Bainbridge","state":"NY"} {"index":{"_id":"243"}} {"account_number":243,"balance":29902,"firstname":"Evangelina","lastname":"Perez","age":20,"gender":"M","address":"787 Joval Court","employer":"Keengen","email":"evangelinaperez@keengen.com","city":"Mulberry","state":"SD"} {"index":{"_id":"248"}} {"account_number":248,"balance":49989,"firstname":"West","lastname":"England","age":36,"gender":"M","address":"717 Hendrickson Place","employer":"Obliq","email":"westengland@obliq.com","city":"Maury","state":"WA"} {"index":{"_id":"250"}} {"account_number":250,"balance":27893,"firstname":"Earlene","lastname":"Ellis","age":39,"gender":"F","address":"512 Bay Street","employer":"Codact","email":"earleneellis@codact.com","city":"Sunwest","state":"GA"} {"index":{"_id":"255"}} {"account_number":255,"balance":49339,"firstname":"Iva","lastname":"Rivers","age":38,"gender":"M","address":"470 Rost Place","employer":"Mantrix","email":"ivarivers@mantrix.com","city":"Disautel","state":"MD"} {"index":{"_id":"262"}} {"account_number":262,"balance":30289,"firstname":"Tameka","lastname":"Levine","age":36,"gender":"F","address":"815 Atlantic Avenue","employer":"Acium","email":"tamekalevine@acium.com","city":"Winchester","state":"SD"} {"index":{"_id":"267"}} {"account_number":267,"balance":42753,"firstname":"Weeks","lastname":"Castillo","age":21,"gender":"F","address":"526 Holt Court","employer":"Talendula","email":"weekscastillo@talendula.com","city":"Washington","state":"NV"} {"index":{"_id":"274"}} {"account_number":274,"balance":12104,"firstname":"Frieda","lastname":"House","age":33,"gender":"F","address":"171 Banker Street","employer":"Quonk","email":"friedahouse@quonk.com","city":"Aberdeen","state":"NJ"} {"index":{"_id":"279"}} {"account_number":279,"balance":15904,"firstname":"Chapman","lastname":"Hart","age":32,"gender":"F","address":"902 Bliss Terrace","employer":"Kongene","email":"chapmanhart@kongene.com","city":"Bradenville","state":"NJ"} {"index":{"_id":"281"}} {"account_number":281,"balance":39830,"firstname":"Bean","lastname":"Aguirre","age":20,"gender":"F","address":"133 Pilling Street","employer":"Amril","email":"beanaguirre@amril.com","city":"Waterview","state":"TX"} {"index":{"_id":"286"}} {"account_number":286,"balance":39063,"firstname":"Rosetta","lastname":"Turner","age":35,"gender":"M","address":"169 Jefferson Avenue","employer":"Spacewax","email":"rosettaturner@spacewax.com","city":"Stewart","state":"MO"} {"index":{"_id":"293"}} {"account_number":293,"balance":29867,"firstname":"Cruz","lastname":"Carver","age":28,"gender":"F","address":"465 Boerum Place","employer":"Vitricomp","email":"cruzcarver@vitricomp.com","city":"Crayne","state":"CO"} {"index":{"_id":"298"}} {"account_number":298,"balance":34334,"firstname":"Bullock","lastname":"Marsh","age":20,"gender":"M","address":"589 Virginia Place","employer":"Renovize","email":"bullockmarsh@renovize.com","city":"Coinjock","state":"UT"} {"index":{"_id":"301"}} {"account_number":301,"balance":16782,"firstname":"Minerva","lastname":"Graham","age":35,"gender":"M","address":"532 Harrison Place","employer":"Sureplex","email":"minervagraham@sureplex.com","city":"Belleview","state":"GA"} {"index":{"_id":"306"}} {"account_number":306,"balance":2171,"firstname":"Hensley","lastname":"Hardin","age":40,"gender":"M","address":"196 Maujer Street","employer":"Neocent","email":"hensleyhardin@neocent.com","city":"Reinerton","state":"HI"} {"index":{"_id":"313"}} {"account_number":313,"balance":34108,"firstname":"Alston","lastname":"Henderson","age":36,"gender":"F","address":"132 Prescott Place","employer":"Prosure","email":"alstonhenderson@prosure.com","city":"Worton","state":"IA"} {"index":{"_id":"318"}} {"account_number":318,"balance":8512,"firstname":"Nichole","lastname":"Pearson","age":34,"gender":"F","address":"656 Lacon Court","employer":"Yurture","email":"nicholepearson@yurture.com","city":"Juarez","state":"MO"} {"index":{"_id":"320"}} {"account_number":320,"balance":34521,"firstname":"Patti","lastname":"Brennan","age":37,"gender":"F","address":"870 Degraw Street","employer":"Cognicode","email":"pattibrennan@cognicode.com","city":"Torboy","state":"FL"} {"index":{"_id":"325"}} {"account_number":325,"balance":1956,"firstname":"Magdalena","lastname":"Simmons","age":25,"gender":"F","address":"681 Townsend Street","employer":"Geekosis","email":"magdalenasimmons@geekosis.com","city":"Sterling","state":"CA"} {"index":{"_id":"332"}} {"account_number":332,"balance":37770,"firstname":"Shepherd","lastname":"Davenport","age":28,"gender":"F","address":"586 Montague Terrace","employer":"Ecraze","email":"shepherddavenport@ecraze.com","city":"Accoville","state":"NM"} {"index":{"_id":"337"}} {"account_number":337,"balance":43432,"firstname":"Monroe","lastname":"Stafford","age":37,"gender":"F","address":"183 Seigel Street","employer":"Centuria","email":"monroestafford@centuria.com","city":"Camino","state":"DE"} {"index":{"_id":"344"}} {"account_number":344,"balance":42654,"firstname":"Sasha","lastname":"Baxter","age":35,"gender":"F","address":"700 Bedford Place","employer":"Callflex","email":"sashabaxter@callflex.com","city":"Campo","state":"MI"} {"index":{"_id":"349"}} {"account_number":349,"balance":24180,"firstname":"Allison","lastname":"Fitzpatrick","age":22,"gender":"F","address":"913 Arlington Avenue","employer":"Veraq","email":"allisonfitzpatrick@veraq.com","city":"Marbury","state":"TX"} {"index":{"_id":"351"}} {"account_number":351,"balance":47089,"firstname":"Hendrix","lastname":"Stephens","age":29,"gender":"M","address":"181 Beaver Street","employer":"Recrisys","email":"hendrixstephens@recrisys.com","city":"Denio","state":"OR"} {"index":{"_id":"356"}} {"account_number":356,"balance":34540,"firstname":"Lourdes","lastname":"Valdez","age":20,"gender":"F","address":"700 Anchorage Place","employer":"Interloo","email":"lourdesvaldez@interloo.com","city":"Goldfield","state":"OK"} {"index":{"_id":"363"}} {"account_number":363,"balance":34007,"firstname":"Peggy","lastname":"Bright","age":21,"gender":"M","address":"613 Engert Avenue","employer":"Inventure","email":"peggybright@inventure.com","city":"Chautauqua","state":"ME"} {"index":{"_id":"368"}} {"account_number":368,"balance":23535,"firstname":"Hooper","lastname":"Tyson","age":39,"gender":"M","address":"892 Taaffe Place","employer":"Zaggle","email":"hoopertyson@zaggle.com","city":"Nutrioso","state":"ME"} {"index":{"_id":"370"}} {"account_number":370,"balance":28499,"firstname":"Oneill","lastname":"Carney","age":25,"gender":"F","address":"773 Adelphi Street","employer":"Bedder","email":"oneillcarney@bedder.com","city":"Yorklyn","state":"FL"} {"index":{"_id":"375"}} {"account_number":375,"balance":23860,"firstname":"Phoebe","lastname":"Patton","age":25,"gender":"M","address":"564 Hale Avenue","employer":"Xoggle","email":"phoebepatton@xoggle.com","city":"Brule","state":"NM"} {"index":{"_id":"382"}} {"account_number":382,"balance":42061,"firstname":"Finley","lastname":"Singleton","age":37,"gender":"F","address":"407 Clay Street","employer":"Quarex","email":"finleysingleton@quarex.com","city":"Bedias","state":"LA"} {"index":{"_id":"387"}} {"account_number":387,"balance":35916,"firstname":"April","lastname":"Hill","age":29,"gender":"M","address":"818 Bayard Street","employer":"Kengen","email":"aprilhill@kengen.com","city":"Chloride","state":"NC"} {"index":{"_id":"394"}} {"account_number":394,"balance":6121,"firstname":"Lorrie","lastname":"Nunez","age":38,"gender":"M","address":"221 Ralph Avenue","employer":"Bullzone","email":"lorrienunez@bullzone.com","city":"Longoria","state":"ID"} {"index":{"_id":"399"}} {"account_number":399,"balance":32587,"firstname":"Carmela","lastname":"Franks","age":23,"gender":"M","address":"617 Dewey Place","employer":"Zensure","email":"carmelafranks@zensure.com","city":"Sanders","state":"DC"} {"index":{"_id":"402"}} {"account_number":402,"balance":1282,"firstname":"Pacheco","lastname":"Rosales","age":32,"gender":"M","address":"538 Pershing Loop","employer":"Circum","email":"pachecorosales@circum.com","city":"Elbert","state":"ID"} {"index":{"_id":"407"}} {"account_number":407,"balance":36417,"firstname":"Gilda","lastname":"Jacobson","age":29,"gender":"F","address":"883 Loring Avenue","employer":"Comveyor","email":"gildajacobson@comveyor.com","city":"Topaz","state":"NH"} {"index":{"_id":"414"}} {"account_number":414,"balance":17506,"firstname":"Conway","lastname":"Daugherty","age":37,"gender":"F","address":"643 Kermit Place","employer":"Lyria","email":"conwaydaugherty@lyria.com","city":"Vaughn","state":"NV"} {"index":{"_id":"419"}} {"account_number":419,"balance":34847,"firstname":"Helen","lastname":"Montoya","age":29,"gender":"F","address":"736 Kingsland Avenue","employer":"Hairport","email":"helenmontoya@hairport.com","city":"Edinburg","state":"NE"} {"index":{"_id":"421"}} {"account_number":421,"balance":46868,"firstname":"Tamika","lastname":"Mccall","age":27,"gender":"F","address":"764 Bragg Court","employer":"Eventix","email":"tamikamccall@eventix.com","city":"Tivoli","state":"RI"} {"index":{"_id":"426"}} {"account_number":426,"balance":4499,"firstname":"Julie","lastname":"Parsons","age":31,"gender":"M","address":"768 Keap Street","employer":"Goko","email":"julieparsons@goko.com","city":"Coldiron","state":"VA"} {"index":{"_id":"433"}} {"account_number":433,"balance":19266,"firstname":"Wilkinson","lastname":"Flowers","age":39,"gender":"M","address":"154 Douglass Street","employer":"Xsports","email":"wilkinsonflowers@xsports.com","city":"Coultervillle","state":"MN"} {"index":{"_id":"438"}} {"account_number":438,"balance":16367,"firstname":"Walter","lastname":"Velez","age":27,"gender":"F","address":"931 Farragut Road","employer":"Virva","email":"waltervelez@virva.com","city":"Tyro","state":"WV"} {"index":{"_id":"440"}} {"account_number":440,"balance":41590,"firstname":"Ray","lastname":"Wiley","age":31,"gender":"F","address":"102 Barwell Terrace","employer":"Polaria","email":"raywiley@polaria.com","city":"Hardyville","state":"IA"} {"index":{"_id":"445"}} {"account_number":445,"balance":41178,"firstname":"Rodriguez","lastname":"Macias","age":34,"gender":"M","address":"164 Boerum Street","employer":"Xylar","email":"rodriguezmacias@xylar.com","city":"Riner","state":"AL"} {"index":{"_id":"452"}} {"account_number":452,"balance":3589,"firstname":"Blackwell","lastname":"Delaney","age":39,"gender":"F","address":"443 Sackett Street","employer":"Imkan","email":"blackwelldelaney@imkan.com","city":"Gasquet","state":"DC"} {"index":{"_id":"457"}} {"account_number":457,"balance":14057,"firstname":"Bush","lastname":"Gordon","age":34,"gender":"M","address":"975 Dakota Place","employer":"Softmicro","email":"bushgordon@softmicro.com","city":"Chemung","state":"PA"} {"index":{"_id":"464"}} {"account_number":464,"balance":20504,"firstname":"Cobb","lastname":"Humphrey","age":21,"gender":"M","address":"823 Sunnyside Avenue","employer":"Apexia","email":"cobbhumphrey@apexia.com","city":"Wintersburg","state":"NY"} {"index":{"_id":"469"}} {"account_number":469,"balance":26509,"firstname":"Marci","lastname":"Shepherd","age":26,"gender":"M","address":"565 Hall Street","employer":"Shadease","email":"marcishepherd@shadease.com","city":"Springhill","state":"IL"} {"index":{"_id":"471"}} {"account_number":471,"balance":7629,"firstname":"Juana","lastname":"Silva","age":36,"gender":"M","address":"249 Amity Street","employer":"Artworlds","email":"juanasilva@artworlds.com","city":"Norfolk","state":"TX"} {"index":{"_id":"476"}} {"account_number":476,"balance":33386,"firstname":"Silva","lastname":"Marks","age":31,"gender":"F","address":"183 Eldert Street","employer":"Medifax","email":"silvamarks@medifax.com","city":"Hachita","state":"RI"} {"index":{"_id":"483"}} {"account_number":483,"balance":6344,"firstname":"Kelley","lastname":"Harper","age":29,"gender":"M","address":"758 Preston Court","employer":"Xyqag","email":"kelleyharper@xyqag.com","city":"Healy","state":"IA"} {"index":{"_id":"488"}} {"account_number":488,"balance":6289,"firstname":"Wilma","lastname":"Hopkins","age":38,"gender":"M","address":"428 Lee Avenue","employer":"Entality","email":"wilmahopkins@entality.com","city":"Englevale","state":"WI"} {"index":{"_id":"490"}} {"account_number":490,"balance":1447,"firstname":"Strong","lastname":"Hendrix","age":26,"gender":"F","address":"134 Beach Place","employer":"Duoflex","email":"stronghendrix@duoflex.com","city":"Allentown","state":"ND"} {"index":{"_id":"495"}} {"account_number":495,"balance":13478,"firstname":"Abigail","lastname":"Nichols","age":40,"gender":"F","address":"887 President Street","employer":"Enquility","email":"abigailnichols@enquility.com","city":"Bagtown","state":"NM"} {"index":{"_id":"503"}} {"account_number":503,"balance":42649,"firstname":"Leta","lastname":"Stout","age":39,"gender":"F","address":"518 Bowery Street","employer":"Pivitol","email":"letastout@pivitol.com","city":"Boonville","state":"ND"} {"index":{"_id":"508"}} {"account_number":508,"balance":41300,"firstname":"Lawrence","lastname":"Mathews","age":27,"gender":"F","address":"987 Rose Street","employer":"Deviltoe","email":"lawrencemathews@deviltoe.com","city":"Woodburn","state":"FL"} {"index":{"_id":"510"}} {"account_number":510,"balance":48504,"firstname":"Petty","lastname":"Sykes","age":28,"gender":"M","address":"566 Village Road","employer":"Nebulean","email":"pettysykes@nebulean.com","city":"Wedgewood","state":"MO"} {"index":{"_id":"515"}} {"account_number":515,"balance":18531,"firstname":"Lott","lastname":"Keller","age":27,"gender":"M","address":"827 Miami Court","employer":"Translink","email":"lottkeller@translink.com","city":"Gila","state":"TX"} {"index":{"_id":"522"}} {"account_number":522,"balance":19879,"firstname":"Faulkner","lastname":"Garrett","age":29,"gender":"F","address":"396 Grove Place","employer":"Pigzart","email":"faulknergarrett@pigzart.com","city":"Felt","state":"AR"} {"index":{"_id":"527"}} {"account_number":527,"balance":2028,"firstname":"Carver","lastname":"Peters","age":35,"gender":"M","address":"816 Victor Road","employer":"Housedown","email":"carverpeters@housedown.com","city":"Nadine","state":"MD"} {"index":{"_id":"534"}} {"account_number":534,"balance":20470,"firstname":"Cristina","lastname":"Russo","age":25,"gender":"F","address":"500 Highlawn Avenue","employer":"Cyclonica","email":"cristinarusso@cyclonica.com","city":"Gorst","state":"KS"} {"index":{"_id":"539"}} {"account_number":539,"balance":24560,"firstname":"Tami","lastname":"Maddox","age":23,"gender":"F","address":"741 Pineapple Street","employer":"Accidency","email":"tamimaddox@accidency.com","city":"Kennedyville","state":"OH"} {"index":{"_id":"541"}} {"account_number":541,"balance":42915,"firstname":"Logan","lastname":"Burke","age":32,"gender":"M","address":"904 Clarendon Road","employer":"Overplex","email":"loganburke@overplex.com","city":"Johnsonburg","state":"OH"} {"index":{"_id":"546"}} {"account_number":546,"balance":43242,"firstname":"Bernice","lastname":"Sims","age":33,"gender":"M","address":"382 Columbia Street","employer":"Verbus","email":"bernicesims@verbus.com","city":"Sena","state":"KY"} {"index":{"_id":"553"}} {"account_number":553,"balance":28390,"firstname":"Aimee","lastname":"Cohen","age":28,"gender":"M","address":"396 Lafayette Avenue","employer":"Eplode","email":"aimeecohen@eplode.com","city":"Thatcher","state":"NJ"} {"index":{"_id":"558"}} {"account_number":558,"balance":8922,"firstname":"Horne","lastname":"Valenzuela","age":20,"gender":"F","address":"979 Kensington Street","employer":"Isoternia","email":"hornevalenzuela@isoternia.com","city":"Greenbush","state":"NC"} {"index":{"_id":"560"}} {"account_number":560,"balance":24514,"firstname":"Felecia","lastname":"Oneill","age":26,"gender":"M","address":"995 Autumn Avenue","employer":"Mediot","email":"feleciaoneill@mediot.com","city":"Joppa","state":"IN"} {"index":{"_id":"565"}} {"account_number":565,"balance":15197,"firstname":"Taylor","lastname":"Ingram","age":37,"gender":"F","address":"113 Will Place","employer":"Lyrichord","email":"tayloringram@lyrichord.com","city":"Collins","state":"ME"} {"index":{"_id":"572"}} {"account_number":572,"balance":49355,"firstname":"Therese","lastname":"Espinoza","age":20,"gender":"M","address":"994 Chester Court","employer":"Gonkle","email":"thereseespinoza@gonkle.com","city":"Hayes","state":"UT"} {"index":{"_id":"577"}} {"account_number":577,"balance":21398,"firstname":"Gilbert","lastname":"Serrano","age":38,"gender":"F","address":"294 Troutman Street","employer":"Senmao","email":"gilbertserrano@senmao.com","city":"Greer","state":"MT"} {"index":{"_id":"584"}} {"account_number":584,"balance":5346,"firstname":"Pearson","lastname":"Bryant","age":40,"gender":"F","address":"971 Heyward Street","employer":"Anacho","email":"pearsonbryant@anacho.com","city":"Bluffview","state":"MN"} {"index":{"_id":"589"}} {"account_number":589,"balance":33260,"firstname":"Ericka","lastname":"Cote","age":39,"gender":"F","address":"425 Bath Avenue","employer":"Venoflex","email":"erickacote@venoflex.com","city":"Blue","state":"CT"} {"index":{"_id":"591"}} {"account_number":591,"balance":48997,"firstname":"Rivers","lastname":"Macdonald","age":34,"gender":"F","address":"919 Johnson Street","employer":"Ziore","email":"riversmacdonald@ziore.com","city":"Townsend","state":"IL"} {"index":{"_id":"596"}} {"account_number":596,"balance":4063,"firstname":"Letitia","lastname":"Walker","age":26,"gender":"F","address":"963 Vanderveer Place","employer":"Zizzle","email":"letitiawalker@zizzle.com","city":"Rossmore","state":"ID"} {"index":{"_id":"604"}} {"account_number":604,"balance":10675,"firstname":"Isabel","lastname":"Gilliam","age":23,"gender":"M","address":"854 Broadway ","employer":"Zenthall","email":"isabelgilliam@zenthall.com","city":"Ventress","state":"WI"} {"index":{"_id":"609"}} {"account_number":609,"balance":28586,"firstname":"Montgomery","lastname":"Washington","age":30,"gender":"M","address":"169 Schroeders Avenue","employer":"Kongle","email":"montgomerywashington@kongle.com","city":"Croom","state":"AZ"} {"index":{"_id":"611"}} {"account_number":611,"balance":17528,"firstname":"Katherine","lastname":"Prince","age":33,"gender":"F","address":"705 Elm Avenue","employer":"Zillacon","email":"katherineprince@zillacon.com","city":"Rew","state":"MI"} {"index":{"_id":"616"}} {"account_number":616,"balance":25276,"firstname":"Jessie","lastname":"Mayer","age":35,"gender":"F","address":"683 Chester Avenue","employer":"Emtrak","email":"jessiemayer@emtrak.com","city":"Marysville","state":"HI"} {"index":{"_id":"623"}} {"account_number":623,"balance":20514,"firstname":"Rose","lastname":"Combs","age":32,"gender":"F","address":"312 Grimes Road","employer":"Aquamate","email":"rosecombs@aquamate.com","city":"Fostoria","state":"OH"} {"index":{"_id":"628"}} {"account_number":628,"balance":42736,"firstname":"Buckner","lastname":"Chen","age":37,"gender":"M","address":"863 Rugby Road","employer":"Jamnation","email":"bucknerchen@jamnation.com","city":"Camas","state":"TX"} {"index":{"_id":"630"}} {"account_number":630,"balance":46060,"firstname":"Leanne","lastname":"Jones","age":31,"gender":"M","address":"451 Bayview Avenue","employer":"Wazzu","email":"leannejones@wazzu.com","city":"Kylertown","state":"OK"} {"index":{"_id":"635"}} {"account_number":635,"balance":44705,"firstname":"Norman","lastname":"Gilmore","age":33,"gender":"M","address":"330 Gates Avenue","employer":"Comfirm","email":"normangilmore@comfirm.com","city":"Riceville","state":"TN"} {"index":{"_id":"642"}} {"account_number":642,"balance":32852,"firstname":"Reyna","lastname":"Harris","age":35,"gender":"M","address":"305 Powell Street","employer":"Bedlam","email":"reynaharris@bedlam.com","city":"Florence","state":"KS"} {"index":{"_id":"647"}} {"account_number":647,"balance":10147,"firstname":"Annabelle","lastname":"Velazquez","age":30,"gender":"M","address":"299 Kensington Walk","employer":"Sealoud","email":"annabellevelazquez@sealoud.com","city":"Soudan","state":"ME"} {"index":{"_id":"654"}} {"account_number":654,"balance":38695,"firstname":"Armstrong","lastname":"Frazier","age":25,"gender":"M","address":"899 Seeley Street","employer":"Zensor","email":"armstrongfrazier@zensor.com","city":"Cherokee","state":"UT"} {"index":{"_id":"659"}} {"account_number":659,"balance":29648,"firstname":"Dorsey","lastname":"Sosa","age":40,"gender":"M","address":"270 Aberdeen Street","employer":"Daycore","email":"dorseysosa@daycore.com","city":"Chamberino","state":"SC"} {"index":{"_id":"661"}} {"account_number":661,"balance":3679,"firstname":"Joanne","lastname":"Spencer","age":39,"gender":"F","address":"910 Montauk Avenue","employer":"Visalia","email":"joannespencer@visalia.com","city":"Valmy","state":"NH"} {"index":{"_id":"666"}} {"account_number":666,"balance":13880,"firstname":"Mcguire","lastname":"Lloyd","age":40,"gender":"F","address":"658 Just Court","employer":"Centrexin","email":"mcguirelloyd@centrexin.com","city":"Warren","state":"MT"} {"index":{"_id":"673"}} {"account_number":673,"balance":11303,"firstname":"Mcdaniel","lastname":"Harrell","age":33,"gender":"M","address":"565 Montgomery Place","employer":"Eyeris","email":"mcdanielharrell@eyeris.com","city":"Garnet","state":"NV"} {"index":{"_id":"678"}} {"account_number":678,"balance":43663,"firstname":"Ruby","lastname":"Shaffer","age":28,"gender":"M","address":"350 Clark Street","employer":"Comtrail","email":"rubyshaffer@comtrail.com","city":"Aurora","state":"MA"} {"index":{"_id":"680"}} {"account_number":680,"balance":31561,"firstname":"Melton","lastname":"Camacho","age":32,"gender":"F","address":"771 Montana Place","employer":"Insuresys","email":"meltoncamacho@insuresys.com","city":"Sparkill","state":"IN"} {"index":{"_id":"685"}} {"account_number":685,"balance":22249,"firstname":"Yesenia","lastname":"Rowland","age":24,"gender":"F","address":"193 Dekalb Avenue","employer":"Coriander","email":"yeseniarowland@coriander.com","city":"Lupton","state":"NC"} {"index":{"_id":"692"}} {"account_number":692,"balance":10435,"firstname":"Haney","lastname":"Barlow","age":21,"gender":"F","address":"267 Lenox Road","employer":"Egypto","email":"haneybarlow@egypto.com","city":"Detroit","state":"IN"} {"index":{"_id":"697"}} {"account_number":697,"balance":48745,"firstname":"Mallory","lastname":"Emerson","age":24,"gender":"F","address":"318 Dunne Court","employer":"Exoplode","email":"malloryemerson@exoplode.com","city":"Montura","state":"LA"} {"index":{"_id":"700"}} {"account_number":700,"balance":19164,"firstname":"Patel","lastname":"Durham","age":21,"gender":"F","address":"440 King Street","employer":"Icology","email":"pateldurham@icology.com","city":"Mammoth","state":"IL"} {"index":{"_id":"705"}} {"account_number":705,"balance":28415,"firstname":"Krystal","lastname":"Cross","age":22,"gender":"M","address":"604 Drew Street","employer":"Tubesys","email":"krystalcross@tubesys.com","city":"Dalton","state":"MO"} {"index":{"_id":"712"}} {"account_number":712,"balance":12459,"firstname":"Butler","lastname":"Alston","age":37,"gender":"M","address":"486 Hemlock Street","employer":"Quordate","email":"butleralston@quordate.com","city":"Verdi","state":"MS"} {"index":{"_id":"717"}} {"account_number":717,"balance":29270,"firstname":"Erickson","lastname":"Mcdonald","age":31,"gender":"M","address":"873 Franklin Street","employer":"Exotechno","email":"ericksonmcdonald@exotechno.com","city":"Jessie","state":"MS"} {"index":{"_id":"724"}} {"account_number":724,"balance":12548,"firstname":"Hopper","lastname":"Peck","age":31,"gender":"M","address":"849 Hendrickson Street","employer":"Uxmox","email":"hopperpeck@uxmox.com","city":"Faxon","state":"UT"} {"index":{"_id":"729"}} {"account_number":729,"balance":41812,"firstname":"Katy","lastname":"Rivera","age":36,"gender":"F","address":"791 Olive Street","employer":"Blurrybus","email":"katyrivera@blurrybus.com","city":"Innsbrook","state":"MI"} {"index":{"_id":"731"}} {"account_number":731,"balance":4994,"firstname":"Lorene","lastname":"Weiss","age":35,"gender":"M","address":"990 Ocean Court","employer":"Comvoy","email":"loreneweiss@comvoy.com","city":"Lavalette","state":"WI"} {"index":{"_id":"736"}} {"account_number":736,"balance":28677,"firstname":"Rogers","lastname":"Mcmahon","age":21,"gender":"F","address":"423 Cameron Court","employer":"Brainclip","email":"rogersmcmahon@brainclip.com","city":"Saddlebrooke","state":"FL"} {"index":{"_id":"743"}} {"account_number":743,"balance":14077,"firstname":"Susana","lastname":"Moody","age":23,"gender":"M","address":"842 Fountain Avenue","employer":"Bitrex","email":"susanamoody@bitrex.com","city":"Temperanceville","state":"TN"} {"index":{"_id":"748"}} {"account_number":748,"balance":38060,"firstname":"Ford","lastname":"Branch","age":25,"gender":"M","address":"926 Cypress Avenue","employer":"Buzzness","email":"fordbranch@buzzness.com","city":"Beason","state":"DC"} {"index":{"_id":"750"}} {"account_number":750,"balance":40481,"firstname":"Cherie","lastname":"Brooks","age":20,"gender":"F","address":"601 Woodhull Street","employer":"Kaggle","email":"cheriebrooks@kaggle.com","city":"Groton","state":"MA"} {"index":{"_id":"755"}} {"account_number":755,"balance":43878,"firstname":"Bartlett","lastname":"Conway","age":22,"gender":"M","address":"453 Times Placez","employer":"Konnect","email":"bartlettconway@konnect.com","city":"Belva","state":"VT"} {"index":{"_id":"762"}} {"account_number":762,"balance":10291,"firstname":"Amanda","lastname":"Head","age":20,"gender":"F","address":"990 Ocean Parkway","employer":"Zentury","email":"amandahead@zentury.com","city":"Hegins","state":"AR"} {"index":{"_id":"767"}} {"account_number":767,"balance":26220,"firstname":"Anthony","lastname":"Sutton","age":27,"gender":"F","address":"179 Fayette Street","employer":"Xiix","email":"anthonysutton@xiix.com","city":"Iberia","state":"TN"} {"index":{"_id":"774"}} {"account_number":774,"balance":35287,"firstname":"Lynnette","lastname":"Alvarez","age":38,"gender":"F","address":"991 Brightwater Avenue","employer":"Gink","email":"lynnettealvarez@gink.com","city":"Leola","state":"NC"} {"index":{"_id":"779"}} {"account_number":779,"balance":40983,"firstname":"Maggie","lastname":"Pace","age":32,"gender":"F","address":"104 Harbor Court","employer":"Bulljuice","email":"maggiepace@bulljuice.com","city":"Floris","state":"MA"} {"index":{"_id":"781"}} {"account_number":781,"balance":29961,"firstname":"Sanford","lastname":"Mullen","age":26,"gender":"F","address":"879 Dover Street","employer":"Zanity","email":"sanfordmullen@zanity.com","city":"Martinez","state":"TX"} {"index":{"_id":"786"}} {"account_number":786,"balance":3024,"firstname":"Rene","lastname":"Vang","age":33,"gender":"M","address":"506 Randolph Street","employer":"Isopop","email":"renevang@isopop.com","city":"Vienna","state":"NJ"} {"index":{"_id":"793"}} {"account_number":793,"balance":16911,"firstname":"Alford","lastname":"Compton","age":36,"gender":"M","address":"186 Veronica Place","employer":"Zyple","email":"alfordcompton@zyple.com","city":"Sugartown","state":"AK"} {"index":{"_id":"798"}} {"account_number":798,"balance":3165,"firstname":"Catherine","lastname":"Ward","age":30,"gender":"F","address":"325 Burnett Street","employer":"Dreamia","email":"catherineward@dreamia.com","city":"Glenbrook","state":"SD"} {"index":{"_id":"801"}} {"account_number":801,"balance":14954,"firstname":"Molly","lastname":"Maldonado","age":37,"gender":"M","address":"518 Maple Avenue","employer":"Straloy","email":"mollymaldonado@straloy.com","city":"Hebron","state":"WI"} {"index":{"_id":"806"}} {"account_number":806,"balance":36492,"firstname":"Carson","lastname":"Riddle","age":31,"gender":"M","address":"984 Lois Avenue","employer":"Terrago","email":"carsonriddle@terrago.com","city":"Leland","state":"MN"} {"index":{"_id":"813"}} {"account_number":813,"balance":30833,"firstname":"Ebony","lastname":"Bishop","age":20,"gender":"M","address":"487 Ridge Court","employer":"Optique","email":"ebonybishop@optique.com","city":"Fairmount","state":"WA"} {"index":{"_id":"818"}} {"account_number":818,"balance":24433,"firstname":"Espinoza","lastname":"Petersen","age":26,"gender":"M","address":"641 Glenwood Road","employer":"Futurity","email":"espinozapetersen@futurity.com","city":"Floriston","state":"MD"} {"index":{"_id":"820"}} {"account_number":820,"balance":1011,"firstname":"Shepard","lastname":"Ramsey","age":24,"gender":"F","address":"806 Village Court","employer":"Mantro","email":"shepardramsey@mantro.com","city":"Tibbie","state":"NV"} {"index":{"_id":"825"}} {"account_number":825,"balance":49000,"firstname":"Terra","lastname":"Witt","age":21,"gender":"F","address":"590 Conway Street","employer":"Insectus","email":"terrawitt@insectus.com","city":"Forbestown","state":"AR"} {"index":{"_id":"832"}} {"account_number":832,"balance":8582,"firstname":"Laura","lastname":"Gibbs","age":39,"gender":"F","address":"511 Osborn Street","employer":"Corepan","email":"lauragibbs@corepan.com","city":"Worcester","state":"KS"} {"index":{"_id":"837"}} {"account_number":837,"balance":14485,"firstname":"Amy","lastname":"Villarreal","age":35,"gender":"M","address":"381 Stillwell Place","employer":"Fleetmix","email":"amyvillarreal@fleetmix.com","city":"Sanford","state":"IA"} {"index":{"_id":"844"}} {"account_number":844,"balance":26840,"firstname":"Jill","lastname":"David","age":31,"gender":"M","address":"346 Legion Street","employer":"Zytrax","email":"jilldavid@zytrax.com","city":"Saticoy","state":"SC"} {"index":{"_id":"849"}} {"account_number":849,"balance":16200,"firstname":"Barry","lastname":"Chapman","age":26,"gender":"M","address":"931 Dekoven Court","employer":"Darwinium","email":"barrychapman@darwinium.com","city":"Whitestone","state":"WY"} {"index":{"_id":"851"}} {"account_number":851,"balance":22026,"firstname":"Henderson","lastname":"Price","age":33,"gender":"F","address":"530 Hausman Street","employer":"Plutorque","email":"hendersonprice@plutorque.com","city":"Brutus","state":"RI"} {"index":{"_id":"856"}} {"account_number":856,"balance":27583,"firstname":"Alissa","lastname":"Knox","age":25,"gender":"M","address":"258 Empire Boulevard","employer":"Geologix","email":"alissaknox@geologix.com","city":"Hartsville/Hartley","state":"MN"} {"index":{"_id":"863"}} {"account_number":863,"balance":23165,"firstname":"Melendez","lastname":"Fernandez","age":40,"gender":"M","address":"661 Johnson Avenue","employer":"Vixo","email":"melendezfernandez@vixo.com","city":"Farmers","state":"IL"} {"index":{"_id":"868"}} {"account_number":868,"balance":27624,"firstname":"Polly","lastname":"Barron","age":22,"gender":"M","address":"129 Frank Court","employer":"Geofarm","email":"pollybarron@geofarm.com","city":"Loyalhanna","state":"ND"} {"index":{"_id":"870"}} {"account_number":870,"balance":43882,"firstname":"Goff","lastname":"Phelps","age":21,"gender":"M","address":"164 Montague Street","employer":"Digigen","email":"goffphelps@digigen.com","city":"Weedville","state":"IL"} {"index":{"_id":"875"}} {"account_number":875,"balance":19655,"firstname":"Mercer","lastname":"Pratt","age":24,"gender":"M","address":"608 Perry Place","employer":"Twiggery","email":"mercerpratt@twiggery.com","city":"Eggertsville","state":"MO"} {"index":{"_id":"882"}} {"account_number":882,"balance":10895,"firstname":"Mari","lastname":"Landry","age":39,"gender":"M","address":"963 Gerald Court","employer":"Kenegy","email":"marilandry@kenegy.com","city":"Lithium","state":"NC"} {"index":{"_id":"887"}} {"account_number":887,"balance":31772,"firstname":"Eunice","lastname":"Watts","age":36,"gender":"F","address":"707 Stuyvesant Avenue","employer":"Memora","email":"eunicewatts@memora.com","city":"Westwood","state":"TN"} {"index":{"_id":"894"}} {"account_number":894,"balance":1031,"firstname":"Tyler","lastname":"Fitzgerald","age":32,"gender":"M","address":"787 Meserole Street","employer":"Jetsilk","email":"tylerfitzgerald@jetsilk.com","city":"Woodlands","state":"WV"} {"index":{"_id":"899"}} {"account_number":899,"balance":32953,"firstname":"Carney","lastname":"Callahan","age":23,"gender":"M","address":"724 Kimball Street","employer":"Mangelica","email":"carneycallahan@mangelica.com","city":"Tecolotito","state":"MT"} {"index":{"_id":"902"}} {"account_number":902,"balance":13345,"firstname":"Hallie","lastname":"Jarvis","age":23,"gender":"F","address":"237 Duryea Court","employer":"Anixang","email":"halliejarvis@anixang.com","city":"Boykin","state":"IN"} {"index":{"_id":"907"}} {"account_number":907,"balance":12961,"firstname":"Ingram","lastname":"William","age":36,"gender":"M","address":"826 Overbaugh Place","employer":"Genmex","email":"ingramwilliam@genmex.com","city":"Kimmell","state":"AK"} {"index":{"_id":"914"}} {"account_number":914,"balance":7120,"firstname":"Esther","lastname":"Bean","age":32,"gender":"F","address":"583 Macon Street","employer":"Applica","email":"estherbean@applica.com","city":"Homeworth","state":"MN"} {"index":{"_id":"919"}} {"account_number":919,"balance":39655,"firstname":"Shauna","lastname":"Hanson","age":27,"gender":"M","address":"557 Hart Place","employer":"Exospace","email":"shaunahanson@exospace.com","city":"Outlook","state":"LA"} {"index":{"_id":"921"}} {"account_number":921,"balance":49119,"firstname":"Barbara","lastname":"Wade","age":29,"gender":"M","address":"687 Hoyts Lane","employer":"Roughies","email":"barbarawade@roughies.com","city":"Sattley","state":"CO"} {"index":{"_id":"926"}} {"account_number":926,"balance":49433,"firstname":"Welch","lastname":"Mcgowan","age":21,"gender":"M","address":"833 Quincy Street","employer":"Atomica","email":"welchmcgowan@atomica.com","city":"Hampstead","state":"VT"} {"index":{"_id":"933"}} {"account_number":933,"balance":18071,"firstname":"Tabitha","lastname":"Cole","age":21,"gender":"F","address":"916 Rogers Avenue","employer":"Eclipto","email":"tabithacole@eclipto.com","city":"Lawrence","state":"TX"} {"index":{"_id":"938"}} {"account_number":938,"balance":9597,"firstname":"Sharron","lastname":"Santos","age":40,"gender":"F","address":"215 Matthews Place","employer":"Zenco","email":"sharronsantos@zenco.com","city":"Wattsville","state":"VT"} {"index":{"_id":"940"}} {"account_number":940,"balance":23285,"firstname":"Melinda","lastname":"Mendoza","age":38,"gender":"M","address":"806 Kossuth Place","employer":"Kneedles","email":"melindamendoza@kneedles.com","city":"Coaldale","state":"OK"} {"index":{"_id":"945"}} {"account_number":945,"balance":23085,"firstname":"Hansen","lastname":"Hebert","age":33,"gender":"F","address":"287 Conduit Boulevard","employer":"Capscreen","email":"hansenhebert@capscreen.com","city":"Taycheedah","state":"AK"} {"index":{"_id":"952"}} {"account_number":952,"balance":21430,"firstname":"Angelique","lastname":"Weeks","age":33,"gender":"M","address":"659 Reeve Place","employer":"Exodoc","email":"angeliqueweeks@exodoc.com","city":"Turpin","state":"MD"} {"index":{"_id":"957"}} {"account_number":957,"balance":11373,"firstname":"Michael","lastname":"Giles","age":31,"gender":"M","address":"668 Court Square","employer":"Yogasm","email":"michaelgiles@yogasm.com","city":"Rosburg","state":"WV"} {"index":{"_id":"964"}} {"account_number":964,"balance":26154,"firstname":"Elena","lastname":"Waller","age":34,"gender":"F","address":"618 Crystal Street","employer":"Insurety","email":"elenawaller@insurety.com","city":"Gallina","state":"NY"} {"index":{"_id":"969"}} {"account_number":969,"balance":22214,"firstname":"Briggs","lastname":"Lynn","age":30,"gender":"M","address":"952 Lester Court","employer":"Quinex","email":"briggslynn@quinex.com","city":"Roland","state":"ID"} {"index":{"_id":"971"}} {"account_number":971,"balance":22772,"firstname":"Gabrielle","lastname":"Reilly","age":32,"gender":"F","address":"964 Tudor Terrace","employer":"Blanet","email":"gabriellereilly@blanet.com","city":"Falmouth","state":"AL"} {"index":{"_id":"976"}} {"account_number":976,"balance":31707,"firstname":"Mullen","lastname":"Tanner","age":26,"gender":"M","address":"711 Whitney Avenue","employer":"Pulze","email":"mullentanner@pulze.com","city":"Mooresburg","state":"MA"} {"index":{"_id":"983"}} {"account_number":983,"balance":47205,"firstname":"Mattie","lastname":"Eaton","age":24,"gender":"F","address":"418 Allen Avenue","employer":"Trasola","email":"mattieeaton@trasola.com","city":"Dupuyer","state":"NJ"} {"index":{"_id":"988"}} {"account_number":988,"balance":17803,"firstname":"Lucy","lastname":"Castro","age":34,"gender":"F","address":"425 Fleet Walk","employer":"Geekfarm","email":"lucycastro@geekfarm.com","city":"Mulino","state":"VA"} {"index":{"_id":"990"}} {"account_number":990,"balance":44456,"firstname":"Kelly","lastname":"Steele","age":35,"gender":"M","address":"809 Hoyt Street","employer":"Eschoir","email":"kellysteele@eschoir.com","city":"Stewartville","state":"ID"} {"index":{"_id":"995"}} {"account_number":995,"balance":21153,"firstname":"Phelps","lastname":"Parrish","age":25,"gender":"M","address":"666 Miller Place","employer":"Pearlessa","email":"phelpsparrish@pearlessa.com","city":"Brecon","state":"ME"} ================================================ FILE: document/navicat/mall数据库模型.ndm2 ================================================ { "paper": { "name": "A4", "leftMargin": 0.5, "rightMargin": 0.5, "topMargin": 0.5, "bottomMargin": 0.5, "isPortriat": true }, "modelVersion": 2.01, "defaultSchema": "Default", "server": { "objectType": "Server_MYSQL", "name": "Default", "serverVersion": 50719, "edition": "Default", "lowerCaseTableNames": 0, "schemas": [ { "objectType": "Schema_MYSQL", "name": "Default", "tables": [], "views": [] }, { "objectType": "Schema_MYSQL", "name": "mall-ref", "tables": [ { "objectType": "Table_MYSQL", "name": "cms_help", "comment": "帮助表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "cms_help", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `cms_help` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `category_id` bigint(20) DEFAULT NULL,\n `icon` varchar(500) DEFAULT NULL,\n `title` varchar(100) DEFAULT NULL,\n `show_status` int(1) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `read_count` int(1) DEFAULT NULL,\n `content` text,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_32` (`category_id`),\n CONSTRAINT `FK_Reference_32` FOREIGN KEY (`category_id`) REFERENCES `cms_help_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='帮助表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "category_id" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "title", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "title" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "read_count", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "read_count" }, { "objectType": "TableField_MYSQL", "name": "content", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "content" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_32", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_32", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "category_id", "keyLength": 0, "order": "", "oldName": "category_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_32", "fields": [ "category_id" ], "referenceSchema": "mall-ref", "referenceTable": "cms_help_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_32" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "cms_help_category", "comment": "帮助分类表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "cms_help_category", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `cms_help_category` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL,\n `icon` varchar(500) DEFAULT NULL COMMENT '分类图标',\n `help_count` int(11) DEFAULT NULL COMMENT '专题数量',\n `show_status` int(2) DEFAULT NULL,\n `sort` int(11) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='帮助分类表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "分类图标", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "help_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "专题数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "help_count" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 2, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "cms_member_report", "comment": "用户举报表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 0, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "cms_member_report", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `cms_member_report` (\n `id` bigint(20) DEFAULT NULL,\n `report_type` int(1) DEFAULT NULL COMMENT '举报类型:0->商品评价;1->话题内容;2->用户评论',\n `report_member_name` varchar(100) DEFAULT NULL COMMENT '举报人',\n `create_time` datetime DEFAULT NULL,\n `report_object` varchar(100) DEFAULT NULL,\n `report_status` int(1) DEFAULT NULL COMMENT '举报状态:0->未处理;1->已处理',\n `handle_status` int(1) DEFAULT NULL COMMENT '处理结果:0->无效;1->有效;2->恶意',\n `note` varchar(200) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户举报表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "report_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "举报类型:0->商品评价;1->话题内容;2->用户评论", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "report_type" }, { "objectType": "TableField_MYSQL", "name": "report_member_name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "举报人", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "report_member_name" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "report_object", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "report_object" }, { "objectType": "TableField_MYSQL", "name": "report_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "举报状态:0->未处理;1->已处理", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "report_status" }, { "objectType": "TableField_MYSQL", "name": "handle_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "处理结果:0->无效;1->有效;2->恶意", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "handle_status" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" } ], "indexes": [], "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "cms_prefrence_area", "comment": "优选专区", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "cms_prefrence_area", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `cms_prefrence_area` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(255) DEFAULT NULL,\n `sub_title` varchar(255) DEFAULT NULL,\n `pic` varbinary(500) DEFAULT NULL COMMENT '展示图片',\n `sort` int(11) DEFAULT NULL,\n `show_status` int(1) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='优选专区'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "sub_title", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sub_title" }, { "objectType": "TableField_MYSQL", "name": "pic", "type": "varbinary", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "展示图片", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pic" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "cms_prefrence_area_product_relation", "comment": "优选专区和产品关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "cms_prefrence_area_product_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `cms_prefrence_area_product_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `prefrence_area_id` bigint(20) DEFAULT NULL,\n `product_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_18` (`prefrence_area_id`),\n KEY `FK_Reference_19` (`product_id`),\n CONSTRAINT `FK_Reference_18` FOREIGN KEY (`prefrence_area_id`) REFERENCES `cms_prefrence_area` (`id`),\n CONSTRAINT `FK_Reference_19` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='优选专区和产品关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "prefrence_area_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "prefrence_area_id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_18", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_18", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "prefrence_area_id", "keyLength": 0, "order": "", "oldName": "prefrence_area_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_19", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_19", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_18", "fields": [ "prefrence_area_id" ], "referenceSchema": "mall-ref", "referenceTable": "cms_prefrence_area", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_18" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_19", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_19" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "cms_subject", "comment": "专题表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "cms_subject", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `cms_subject` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `category_id` bigint(20) DEFAULT NULL,\n `title` varchar(100) DEFAULT NULL,\n `pic` varchar(500) DEFAULT NULL COMMENT '专题主图',\n `product_count` int(11) DEFAULT NULL COMMENT '关联产品数量',\n `recommend_status` int(1) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `collect_count` int(11) DEFAULT NULL,\n `read_count` int(11) DEFAULT NULL,\n `comment_count` int(11) DEFAULT NULL,\n `album_pics` varchar(1000) DEFAULT NULL COMMENT '画册图片用逗号分割',\n `description` varchar(1000) DEFAULT NULL,\n `show_status` int(1) DEFAULT NULL COMMENT '显示状态:0->不显示;1->显示',\n `content` text,\n `forward_count` int(11) DEFAULT NULL COMMENT '转发数',\n `category_name` varchar(200) DEFAULT NULL COMMENT '专题分类名称',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_28` (`category_id`),\n CONSTRAINT `FK_Reference_28` FOREIGN KEY (`category_id`) REFERENCES `cms_subject_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='专题表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "category_id" }, { "objectType": "TableField_MYSQL", "name": "title", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "title" }, { "objectType": "TableField_MYSQL", "name": "pic", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "专题主图", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pic" }, { "objectType": "TableField_MYSQL", "name": "product_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "关联产品数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_count" }, { "objectType": "TableField_MYSQL", "name": "recommend_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recommend_status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "collect_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "collect_count" }, { "objectType": "TableField_MYSQL", "name": "read_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "read_count" }, { "objectType": "TableField_MYSQL", "name": "comment_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "comment_count" }, { "objectType": "TableField_MYSQL", "name": "album_pics", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "画册图片用逗号分割", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "album_pics" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "显示状态:0->不显示;1->显示", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" }, { "objectType": "TableField_MYSQL", "name": "content", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "content" }, { "objectType": "TableField_MYSQL", "name": "forward_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "转发数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "forward_count" }, { "objectType": "TableField_MYSQL", "name": "category_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "专题分类名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "category_name" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_28", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_28", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "category_id", "keyLength": 0, "order": "", "oldName": "category_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_28", "fields": [ "category_id" ], "referenceSchema": "mall-ref", "referenceTable": "cms_subject_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_28" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "cms_subject_category", "comment": "专题分类表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "cms_subject_category", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `cms_subject_category` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL,\n `icon` varchar(500) DEFAULT NULL COMMENT '分类图标',\n `subject_count` int(11) DEFAULT NULL COMMENT '专题数量',\n `show_status` int(2) DEFAULT NULL,\n `sort` int(11) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='专题分类表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "分类图标", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "subject_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "专题数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "subject_count" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 2, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "cms_subject_comment", "comment": "专题评论表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "cms_subject_comment", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `cms_subject_comment` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `subject_id` bigint(20) DEFAULT NULL,\n `member_nick_name` varchar(255) DEFAULT NULL,\n `member_icon` varchar(255) DEFAULT NULL,\n `content` varchar(1000) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `show_status` int(1) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_29` (`subject_id`),\n CONSTRAINT `FK_Reference_29` FOREIGN KEY (`subject_id`) REFERENCES `cms_subject` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='专题评论表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "subject_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "subject_id" }, { "objectType": "TableField_MYSQL", "name": "member_nick_name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_nick_name" }, { "objectType": "TableField_MYSQL", "name": "member_icon", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_icon" }, { "objectType": "TableField_MYSQL", "name": "content", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "content" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_29", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_29", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "subject_id", "keyLength": 0, "order": "", "oldName": "subject_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_29", "fields": [ "subject_id" ], "referenceSchema": "mall-ref", "referenceTable": "cms_subject", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_29" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "cms_subject_product_relation", "comment": "专题商品关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "cms_subject_product_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `cms_subject_product_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `subject_id` bigint(20) DEFAULT NULL,\n `product_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_26` (`subject_id`),\n KEY `FK_Reference_27` (`product_id`),\n CONSTRAINT `FK_Reference_26` FOREIGN KEY (`subject_id`) REFERENCES `cms_subject` (`id`),\n CONSTRAINT `FK_Reference_27` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='专题商品关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "subject_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "subject_id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_26", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_26", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "subject_id", "keyLength": 0, "order": "", "oldName": "subject_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_27", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_27", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_26", "fields": [ "subject_id" ], "referenceSchema": "mall-ref", "referenceTable": "cms_subject", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_26" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_27", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_27" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "cms_topic", "comment": "话题表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "cms_topic", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `cms_topic` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `category_id` bigint(20) DEFAULT NULL,\n `name` varchar(255) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `start_time` datetime DEFAULT NULL,\n `end_time` datetime DEFAULT NULL,\n `attend_count` int(11) DEFAULT NULL COMMENT '参与人数',\n `attention_count` int(11) DEFAULT NULL COMMENT '关注人数',\n `read_count` int(11) DEFAULT NULL,\n `award_name` varchar(100) DEFAULT NULL COMMENT '奖品名称',\n `attend_type` varchar(100) DEFAULT NULL COMMENT '参与方式',\n `content` text COMMENT '话题内容',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_31` (`category_id`),\n CONSTRAINT `FK_Reference_31` FOREIGN KEY (`category_id`) REFERENCES `cms_topic_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='话题表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "category_id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "start_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "start_time" }, { "objectType": "TableField_MYSQL", "name": "end_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "end_time" }, { "objectType": "TableField_MYSQL", "name": "attend_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "参与人数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "attend_count" }, { "objectType": "TableField_MYSQL", "name": "attention_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "关注人数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "attention_count" }, { "objectType": "TableField_MYSQL", "name": "read_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "read_count" }, { "objectType": "TableField_MYSQL", "name": "award_name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "奖品名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "award_name" }, { "objectType": "TableField_MYSQL", "name": "attend_type", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "参与方式", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "attend_type" }, { "objectType": "TableField_MYSQL", "name": "content", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "话题内容", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "content" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_31", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_31", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "category_id", "keyLength": 0, "order": "", "oldName": "category_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_31", "fields": [ "category_id" ], "referenceSchema": "mall-ref", "referenceTable": "cms_topic_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_31" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "cms_topic_category", "comment": "话题分类表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "cms_topic_category", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `cms_topic_category` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL,\n `icon` varchar(500) DEFAULT NULL COMMENT '分类图标',\n `subject_count` int(11) DEFAULT NULL COMMENT '专题数量',\n `show_status` int(2) DEFAULT NULL,\n `sort` int(11) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='话题分类表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "分类图标", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "subject_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "专题数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "subject_count" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 2, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "cms_topic_comment", "comment": "专题评论表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "cms_topic_comment", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `cms_topic_comment` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_nick_name` varchar(255) DEFAULT NULL,\n `topic_id` bigint(20) DEFAULT NULL,\n `member_icon` varchar(255) DEFAULT NULL,\n `content` varchar(1000) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `show_status` int(1) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_30` (`topic_id`),\n CONSTRAINT `FK_Reference_30` FOREIGN KEY (`topic_id`) REFERENCES `cms_topic` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='专题评论表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_nick_name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_nick_name" }, { "objectType": "TableField_MYSQL", "name": "topic_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "topic_id" }, { "objectType": "TableField_MYSQL", "name": "member_icon", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_icon" }, { "objectType": "TableField_MYSQL", "name": "content", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "content" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_30", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_30", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "topic_id", "keyLength": 0, "order": "", "oldName": "topic_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_30", "fields": [ "topic_id" ], "referenceSchema": "mall-ref", "referenceTable": "cms_topic", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_30" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_cart_item", "comment": "购物车表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_cart_item", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 49152, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_cart_item` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `product_sku_id` bigint(20) DEFAULT NULL,\n `member_id` bigint(20) DEFAULT NULL,\n `quantity` int(11) DEFAULT NULL COMMENT '购买数量',\n `price` decimal(10,2) DEFAULT NULL COMMENT '添加到购物车的价格',\n `product_pic` varchar(1000) DEFAULT NULL COMMENT '商品主图',\n `product_name` varchar(500) DEFAULT NULL COMMENT '商品名称',\n `product_brand` varchar(200) DEFAULT NULL,\n `product_sn` varchar(200) DEFAULT NULL,\n `product_sub_title` varchar(500) DEFAULT NULL COMMENT '商品副标题(卖点)',\n `product_sku_code` varchar(200) DEFAULT NULL COMMENT '商品sku条码',\n `member_nickname` varchar(500) DEFAULT NULL COMMENT '会员昵称',\n `create_date` datetime DEFAULT NULL COMMENT '创建时间',\n `modify_date` datetime DEFAULT NULL COMMENT '修改时间',\n `delete_status` int(1) DEFAULT '0' COMMENT '是否删除',\n `product_category_id` bigint(20) DEFAULT NULL COMMENT '商品的分类',\n `product_attr` varchar(500) DEFAULT NULL COMMENT '商品销售属性:[{\"key\":\"颜色\",\"value\":\"银色\"},{\"key\":\"容量\",\"value\":\"4G\"}]',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_65` (`product_id`),\n KEY `FK_Reference_66` (`product_sku_id`),\n KEY `FK_Reference_67` (`member_id`),\n CONSTRAINT `FK_Reference_65` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`),\n CONSTRAINT `FK_Reference_66` FOREIGN KEY (`product_sku_id`) REFERENCES `pms_sku_stock` (`id`),\n CONSTRAINT `FK_Reference_67` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='购物车表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "product_sku_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sku_id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "quantity", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "购买数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "quantity" }, { "objectType": "TableField_MYSQL", "name": "price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "添加到购物车的价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "price" }, { "objectType": "TableField_MYSQL", "name": "product_pic", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品主图", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_pic" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "product_brand", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_brand" }, { "objectType": "TableField_MYSQL", "name": "product_sn", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sn" }, { "objectType": "TableField_MYSQL", "name": "product_sub_title", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品副标题(卖点)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sub_title" }, { "objectType": "TableField_MYSQL", "name": "product_sku_code", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品sku条码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sku_code" }, { "objectType": "TableField_MYSQL", "name": "member_nickname", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "会员昵称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_nickname" }, { "objectType": "TableField_MYSQL", "name": "create_date", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_date" }, { "objectType": "TableField_MYSQL", "name": "modify_date", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "修改时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "modify_date" }, { "objectType": "TableField_MYSQL", "name": "delete_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否删除", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "delete_status" }, { "objectType": "TableField_MYSQL", "name": "product_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品的分类", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_id" }, { "objectType": "TableField_MYSQL", "name": "product_attr", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品销售属性:[{\"key\":\"颜色\",\"value\":\"银色\"},{\"key\":\"容量\",\"value\":\"4G\"}]", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attr" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_65", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_65", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_66", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_66", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_sku_id", "keyLength": 0, "order": "", "oldName": "product_sku_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_67", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_67", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_65", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_65" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_66", "fields": [ "product_sku_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_sku_stock", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_66" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_67", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_67" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_company_address", "comment": "公司收发货地址表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_company_address", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_company_address` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `address_name` varchar(200) DEFAULT NULL COMMENT '地址名称',\n `send_status` int(1) DEFAULT NULL COMMENT '默认发货地址:0->否;1->是',\n `receive_status` int(1) DEFAULT NULL COMMENT '是否默认收货地址:0->否;1->是',\n `name` varchar(64) DEFAULT NULL COMMENT '收发货人姓名',\n `phone` varchar(64) DEFAULT NULL COMMENT '收货人电话',\n `province` varchar(64) DEFAULT NULL COMMENT '省/直辖市',\n `city` varchar(64) DEFAULT NULL COMMENT '市',\n `region` varchar(64) DEFAULT NULL COMMENT '区',\n `detail_address` varchar(200) DEFAULT NULL COMMENT '详细地址',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='公司收发货地址表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "address_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "地址名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "address_name" }, { "objectType": "TableField_MYSQL", "name": "send_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "默认发货地址:0->否;1->是", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "send_status" }, { "objectType": "TableField_MYSQL", "name": "receive_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否默认收货地址:0->否;1->是", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receive_status" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收发货人姓名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "phone", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货人电话", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "phone" }, { "objectType": "TableField_MYSQL", "name": "province", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "省/直辖市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "province" }, { "objectType": "TableField_MYSQL", "name": "city", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "city" }, { "objectType": "TableField_MYSQL", "name": "region", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "区", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "region" }, { "objectType": "TableField_MYSQL", "name": "detail_address", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "详细地址", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail_address" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_order", "comment": "订单表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_order", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_order` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单id',\n `member_id` bigint(20) NOT NULL,\n `coupon_id` bigint(20) DEFAULT NULL,\n `order_sn` varchar(64) DEFAULT NULL COMMENT '订单编号',\n `create_time` datetime DEFAULT NULL COMMENT '提交时间',\n `member_username` varchar(64) DEFAULT NULL COMMENT '用户帐号',\n `total_amount` decimal(10,2) DEFAULT NULL COMMENT '订单总金额',\n `pay_amount` decimal(10,2) DEFAULT NULL COMMENT '应付金额(实际支付金额)',\n `freight_amount` decimal(10,2) DEFAULT NULL COMMENT '运费金额',\n `promotion_amount` decimal(10,2) DEFAULT NULL COMMENT '促销优化金额(促销价、满减、阶梯价)',\n `integration_amount` decimal(10,2) DEFAULT NULL COMMENT '积分抵扣金额',\n `coupon_amount` decimal(10,2) DEFAULT NULL COMMENT '优惠券抵扣金额',\n `discount_amount` decimal(10,2) DEFAULT NULL COMMENT '管理员后台调整订单使用的折扣金额',\n `pay_type` int(1) DEFAULT NULL COMMENT '支付方式:0->未支付;1->支付宝;2->微信',\n `source_type` int(1) DEFAULT NULL COMMENT '订单来源:0->PC订单;1->app订单',\n `status` int(1) DEFAULT NULL COMMENT '订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单',\n `order_type` int(1) DEFAULT NULL COMMENT '订单类型:0->正常订单;1->秒杀订单',\n `delivery_company` varchar(64) DEFAULT NULL COMMENT '物流公司(配送方式)',\n `delivery_sn` varchar(64) DEFAULT NULL COMMENT '物流单号',\n `auto_confirm_day` int(11) DEFAULT NULL COMMENT '自动确认时间(天)',\n `integration` int(11) DEFAULT NULL COMMENT '可以获得的积分',\n `growth` int(11) DEFAULT NULL COMMENT '可以活动的成长值',\n `promotion_info` varchar(100) DEFAULT NULL COMMENT '活动信息',\n `bill_type` int(1) DEFAULT NULL COMMENT '发票类型:0->不开发票;1->电子发票;2->纸质发票',\n `bill_header` varchar(200) DEFAULT NULL COMMENT '发票抬头',\n `bill_content` varchar(200) DEFAULT NULL COMMENT '发票内容',\n `bill_receiver_phone` varchar(32) DEFAULT NULL COMMENT '收票人电话',\n `bill_receiver_email` varchar(64) DEFAULT NULL COMMENT '收票人邮箱',\n `receiver_name` varchar(100) NOT NULL COMMENT '收货人姓名',\n `receiver_phone` varchar(32) NOT NULL COMMENT '收货人电话',\n `receiver_post_code` varchar(32) DEFAULT NULL COMMENT '收货人邮编',\n `receiver_province` varchar(32) DEFAULT NULL COMMENT '省份/直辖市',\n `receiver_city` varchar(32) DEFAULT NULL COMMENT '城市',\n `receiver_region` varchar(32) DEFAULT NULL COMMENT '区',\n `receiver_detail_address` varchar(200) DEFAULT NULL COMMENT '详细地址',\n `note` varchar(500) DEFAULT NULL COMMENT '订单备注',\n `confirm_status` int(1) DEFAULT NULL COMMENT '确认收货状态:0->未确认;1->已确认',\n `delete_status` int(1) NOT NULL DEFAULT '0' COMMENT '删除状态:0->未删除;1->已删除',\n `use_integration` int(11) DEFAULT NULL COMMENT '下单时使用的积分',\n `payment_time` datetime DEFAULT NULL COMMENT '支付时间',\n `delivery_time` datetime DEFAULT NULL COMMENT '发货时间',\n `receive_time` datetime DEFAULT NULL COMMENT '确认收货时间',\n `comment_time` datetime DEFAULT NULL COMMENT '评价时间',\n `modify_time` datetime DEFAULT NULL COMMENT '修改时间',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_57` (`member_id`),\n KEY `FK_Reference_61` (`coupon_id`),\n CONSTRAINT `FK_Reference_57` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`),\n CONSTRAINT `FK_Reference_61` FOREIGN KEY (`coupon_id`) REFERENCES `sms_coupon` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "订单id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "coupon_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_id" }, { "objectType": "TableField_MYSQL", "name": "order_sn", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_sn" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "提交时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "member_username", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "用户帐号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_username" }, { "objectType": "TableField_MYSQL", "name": "total_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单总金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "total_amount" }, { "objectType": "TableField_MYSQL", "name": "pay_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "应付金额(实际支付金额)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pay_amount" }, { "objectType": "TableField_MYSQL", "name": "freight_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "运费金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "freight_amount" }, { "objectType": "TableField_MYSQL", "name": "promotion_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "促销优化金额(促销价、满减、阶梯价)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_amount" }, { "objectType": "TableField_MYSQL", "name": "integration_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分抵扣金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "integration_amount" }, { "objectType": "TableField_MYSQL", "name": "coupon_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "优惠券抵扣金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_amount" }, { "objectType": "TableField_MYSQL", "name": "discount_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "管理员后台调整订单使用的折扣金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "discount_amount" }, { "objectType": "TableField_MYSQL", "name": "pay_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "支付方式:0->未支付;1->支付宝;2->微信", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pay_type" }, { "objectType": "TableField_MYSQL", "name": "source_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单来源:0->PC订单;1->app订单", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "source_type" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "order_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单类型:0->正常订单;1->秒杀订单", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_type" }, { "objectType": "TableField_MYSQL", "name": "delivery_company", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "物流公司(配送方式)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "delivery_company" }, { "objectType": "TableField_MYSQL", "name": "delivery_sn", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "物流单号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "delivery_sn" }, { "objectType": "TableField_MYSQL", "name": "auto_confirm_day", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "自动确认时间(天)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "auto_confirm_day" }, { "objectType": "TableField_MYSQL", "name": "integration", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "可以获得的积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "integration" }, { "objectType": "TableField_MYSQL", "name": "growth", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "可以活动的成长值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "growth" }, { "objectType": "TableField_MYSQL", "name": "promotion_info", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "活动信息", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_info" }, { "objectType": "TableField_MYSQL", "name": "bill_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "发票类型:0->不开发票;1->电子发票;2->纸质发票", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "bill_type" }, { "objectType": "TableField_MYSQL", "name": "bill_header", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "发票抬头", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "bill_header" }, { "objectType": "TableField_MYSQL", "name": "bill_content", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "发票内容", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "bill_content" }, { "objectType": "TableField_MYSQL", "name": "bill_receiver_phone", "type": "varchar", "length": 32, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收票人电话", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "bill_receiver_phone" }, { "objectType": "TableField_MYSQL", "name": "bill_receiver_email", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收票人邮箱", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "bill_receiver_email" }, { "objectType": "TableField_MYSQL", "name": "receiver_name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货人姓名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_name" }, { "objectType": "TableField_MYSQL", "name": "receiver_phone", "type": "varchar", "length": 32, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货人电话", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_phone" }, { "objectType": "TableField_MYSQL", "name": "receiver_post_code", "type": "varchar", "length": 32, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货人邮编", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_post_code" }, { "objectType": "TableField_MYSQL", "name": "receiver_province", "type": "varchar", "length": 32, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "省份/直辖市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_province" }, { "objectType": "TableField_MYSQL", "name": "receiver_city", "type": "varchar", "length": 32, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "城市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_city" }, { "objectType": "TableField_MYSQL", "name": "receiver_region", "type": "varchar", "length": 32, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "区", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_region" }, { "objectType": "TableField_MYSQL", "name": "receiver_detail_address", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "详细地址", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_detail_address" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" }, { "objectType": "TableField_MYSQL", "name": "confirm_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "确认收货状态:0->未确认;1->已确认", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "confirm_status" }, { "objectType": "TableField_MYSQL", "name": "delete_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "删除状态:0->未删除;1->已删除", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "delete_status" }, { "objectType": "TableField_MYSQL", "name": "use_integration", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "下单时使用的积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_integration" }, { "objectType": "TableField_MYSQL", "name": "payment_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "支付时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "payment_time" }, { "objectType": "TableField_MYSQL", "name": "delivery_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "发货时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "delivery_time" }, { "objectType": "TableField_MYSQL", "name": "receive_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "确认收货时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receive_time" }, { "objectType": "TableField_MYSQL", "name": "comment_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "评价时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "comment_time" }, { "objectType": "TableField_MYSQL", "name": "modify_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "修改时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "modify_time" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_57", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_57", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_61", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_61", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "coupon_id", "keyLength": 0, "order": "", "oldName": "coupon_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_57", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "OneAndOnlyOneRelationship", "oldName": "FK_Reference_57" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_61", "fields": [ "coupon_id" ], "referenceSchema": "mall-ref", "referenceTable": "sms_coupon", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_61" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_order_item", "comment": "订单中所包含的商品", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_order_item", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_order_item` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `order_id` bigint(20) DEFAULT NULL COMMENT '订单id',\n `order_sn` varchar(64) DEFAULT NULL COMMENT '订单编号',\n `product_id` bigint(20) DEFAULT NULL,\n `product_pic` varchar(500) DEFAULT NULL,\n `product_name` varchar(200) DEFAULT NULL,\n `product_brand` varchar(200) DEFAULT NULL,\n `product_sn` varchar(64) DEFAULT NULL,\n `product_price` decimal(10,2) DEFAULT NULL COMMENT '销售价格',\n `product_quantity` int(11) DEFAULT NULL COMMENT '购买数量',\n `product_sku_id` bigint(20) DEFAULT NULL COMMENT '商品sku编号',\n `product_sku_code` varchar(50) DEFAULT NULL COMMENT '商品sku条码',\n `product_category_id` bigint(20) DEFAULT NULL COMMENT '商品分类id',\n `promotion_name` varchar(200) DEFAULT NULL COMMENT '商品促销名称',\n `promotion_amount` decimal(10,2) DEFAULT NULL COMMENT '商品促销分解金额',\n `coupon_amount` decimal(10,2) DEFAULT NULL COMMENT '优惠券优惠分解金额',\n `integration_amount` decimal(10,2) DEFAULT NULL COMMENT '积分优惠分解金额',\n `real_amount` decimal(10,2) DEFAULT NULL COMMENT '该商品经过优惠后的分解金额',\n `gift_integration` int(11) NOT NULL DEFAULT '0' COMMENT '商品赠送积分',\n `gift_growth` int(11) NOT NULL DEFAULT '0' COMMENT '商品赠送成长值',\n `product_attr` varchar(500) DEFAULT NULL COMMENT '商品销售属性:[{\"key\":\"颜色\",\"value\":\"颜色\"},{\"key\":\"容量\",\"value\":\"4G\"}]',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_58` (`order_id`),\n KEY `FK_Reference_59` (`product_id`),\n CONSTRAINT `FK_Reference_58` FOREIGN KEY (`order_id`) REFERENCES `oms_order` (`id`),\n CONSTRAINT `FK_Reference_59` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单中所包含的商品'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "order_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_id" }, { "objectType": "TableField_MYSQL", "name": "order_sn", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_sn" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "product_pic", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_pic" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "product_brand", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_brand" }, { "objectType": "TableField_MYSQL", "name": "product_sn", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sn" }, { "objectType": "TableField_MYSQL", "name": "product_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "销售价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_price" }, { "objectType": "TableField_MYSQL", "name": "product_quantity", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "购买数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_quantity" }, { "objectType": "TableField_MYSQL", "name": "product_sku_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品sku编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sku_id" }, { "objectType": "TableField_MYSQL", "name": "product_sku_code", "type": "varchar", "length": 50, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品sku条码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sku_code" }, { "objectType": "TableField_MYSQL", "name": "product_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品分类id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_id" }, { "objectType": "TableField_MYSQL", "name": "promotion_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品促销名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_name" }, { "objectType": "TableField_MYSQL", "name": "promotion_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品促销分解金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_amount" }, { "objectType": "TableField_MYSQL", "name": "coupon_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "优惠券优惠分解金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_amount" }, { "objectType": "TableField_MYSQL", "name": "integration_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分优惠分解金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "integration_amount" }, { "objectType": "TableField_MYSQL", "name": "real_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "该商品经过优惠后的分解金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "real_amount" }, { "objectType": "TableField_MYSQL", "name": "gift_integration", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品赠送积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gift_integration" }, { "objectType": "TableField_MYSQL", "name": "gift_growth", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品赠送成长值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gift_growth" }, { "objectType": "TableField_MYSQL", "name": "product_attr", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品销售属性:[{\"key\":\"颜色\",\"value\":\"颜色\"},{\"key\":\"容量\",\"value\":\"4G\"}]", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attr" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_58", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_58", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "order_id", "keyLength": 0, "order": "", "oldName": "order_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_59", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_59", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_58", "fields": [ "order_id" ], "referenceSchema": "mall-ref", "referenceTable": "oms_order", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_58" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_59", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_59" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_order_operate_history", "comment": "订单操作历史记录", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_order_operate_history", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_order_operate_history` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `order_id` bigint(20) DEFAULT NULL COMMENT '订单id',\n `operate_man` varchar(100) DEFAULT NULL COMMENT '操作人:用户;系统;后台管理员',\n `create_time` datetime DEFAULT NULL COMMENT '操作时间',\n `order_status` int(1) DEFAULT NULL COMMENT '订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单',\n `note` varchar(500) DEFAULT NULL COMMENT '备注',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_62` (`order_id`),\n CONSTRAINT `FK_Reference_62` FOREIGN KEY (`order_id`) REFERENCES `oms_order` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单操作历史记录'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "order_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_id" }, { "objectType": "TableField_MYSQL", "name": "operate_man", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作人:用户;系统;后台管理员", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_man" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "order_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_status" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_62", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_62", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "order_id", "keyLength": 0, "order": "", "oldName": "order_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_62", "fields": [ "order_id" ], "referenceSchema": "mall-ref", "referenceTable": "oms_order", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_62" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_order_return_apply", "comment": "订单退货申请", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_order_return_apply", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 49152, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_order_return_apply` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `order_id` bigint(20) DEFAULT NULL COMMENT '订单id',\n `company_address_id` bigint(20) DEFAULT NULL COMMENT '收货地址表id',\n `product_id` bigint(20) DEFAULT NULL COMMENT '退货商品id',\n `order_sn` varchar(64) DEFAULT NULL COMMENT '订单编号',\n `create_time` datetime DEFAULT NULL COMMENT '申请时间',\n `member_username` varchar(64) DEFAULT NULL COMMENT '会员用户名',\n `return_amount` decimal(10,2) DEFAULT NULL COMMENT '退款金额',\n `return_name` varchar(100) DEFAULT NULL COMMENT '退货人姓名',\n `return_phone` varchar(100) DEFAULT NULL COMMENT '退货人电话',\n `status` int(1) DEFAULT NULL COMMENT '申请状态:0->待处理;1->退货中;2->已完成;3->已拒绝',\n `handle_time` datetime DEFAULT NULL COMMENT '处理时间',\n `product_pic` varchar(500) DEFAULT NULL COMMENT '商品图片',\n `product_name` varchar(200) DEFAULT NULL COMMENT '商品名称',\n `product_brand` varchar(200) DEFAULT NULL COMMENT '商品品牌',\n `product_attr` varchar(500) DEFAULT NULL COMMENT '商品销售属性:颜色:红色;尺码:xl;',\n `product_count` int(11) DEFAULT NULL COMMENT '退货数量',\n `product_price` decimal(10,2) DEFAULT NULL COMMENT '商品单价',\n `product_real_price` decimal(10,2) DEFAULT NULL COMMENT '商品实际支付单价',\n `reason` varchar(200) DEFAULT NULL COMMENT '原因',\n `description` varchar(500) DEFAULT NULL COMMENT '描述',\n `proof_pics` varchar(1000) DEFAULT NULL COMMENT '凭证图片,以逗号隔开',\n `handle_note` varchar(500) DEFAULT NULL COMMENT '处理备注',\n `handle_man` varchar(100) DEFAULT NULL COMMENT '处理人员',\n `receive_man` varchar(100) DEFAULT NULL COMMENT '收货人',\n `receive_time` datetime DEFAULT NULL COMMENT '收货时间',\n `receive_note` varchar(500) DEFAULT NULL COMMENT '收货备注',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_63` (`order_id`),\n KEY `FK_Reference_64` (`company_address_id`),\n KEY `FK_Reference_75` (`product_id`),\n CONSTRAINT `FK_Reference_63` FOREIGN KEY (`order_id`) REFERENCES `oms_order` (`id`),\n CONSTRAINT `FK_Reference_64` FOREIGN KEY (`company_address_id`) REFERENCES `oms_company_address` (`id`),\n CONSTRAINT `FK_Reference_75` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单退货申请'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "order_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_id" }, { "objectType": "TableField_MYSQL", "name": "company_address_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货地址表id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "company_address_id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退货商品id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "order_sn", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_sn" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "申请时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "member_username", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "会员用户名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_username" }, { "objectType": "TableField_MYSQL", "name": "return_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退款金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "return_amount" }, { "objectType": "TableField_MYSQL", "name": "return_name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退货人姓名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "return_name" }, { "objectType": "TableField_MYSQL", "name": "return_phone", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退货人电话", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "return_phone" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "申请状态:0->待处理;1->退货中;2->已完成;3->已拒绝", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "handle_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "处理时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "handle_time" }, { "objectType": "TableField_MYSQL", "name": "product_pic", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品图片", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_pic" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "product_brand", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品品牌", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_brand" }, { "objectType": "TableField_MYSQL", "name": "product_attr", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品销售属性:颜色:红色;尺码:xl;", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attr" }, { "objectType": "TableField_MYSQL", "name": "product_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退货数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_count" }, { "objectType": "TableField_MYSQL", "name": "product_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品单价", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_price" }, { "objectType": "TableField_MYSQL", "name": "product_real_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品实际支付单价", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_real_price" }, { "objectType": "TableField_MYSQL", "name": "reason", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "原因", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "reason" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "描述", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" }, { "objectType": "TableField_MYSQL", "name": "proof_pics", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "凭证图片,以逗号隔开", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "proof_pics" }, { "objectType": "TableField_MYSQL", "name": "handle_note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "处理备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "handle_note" }, { "objectType": "TableField_MYSQL", "name": "handle_man", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "处理人员", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "handle_man" }, { "objectType": "TableField_MYSQL", "name": "receive_man", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货人", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receive_man" }, { "objectType": "TableField_MYSQL", "name": "receive_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receive_time" }, { "objectType": "TableField_MYSQL", "name": "receive_note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receive_note" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_63", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_63", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "order_id", "keyLength": 0, "order": "", "oldName": "order_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_64", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_64", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "company_address_id", "keyLength": 0, "order": "", "oldName": "company_address_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_75", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_75", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_63", "fields": [ "order_id" ], "referenceSchema": "mall-ref", "referenceTable": "oms_order", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_63" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_64", "fields": [ "company_address_id" ], "referenceSchema": "mall-ref", "referenceTable": "oms_company_address", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_64" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_75", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_75" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_order_return_reason", "comment": "退货原因表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_order_return_reason", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_order_return_reason` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL COMMENT '退货类型',\n `sort` int(11) DEFAULT NULL,\n `status` int(1) DEFAULT NULL COMMENT '状态:0->不启用;1->启用',\n `create_time` datetime DEFAULT NULL COMMENT '添加时间',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='退货原因表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退货类型", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "状态:0->不启用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "添加时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_order_setting", "comment": "订单设置表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_order_setting", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_order_setting` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `flash_order_overtime` int(11) DEFAULT NULL COMMENT '秒杀订单超时关闭时间(分)',\n `normal_order_overtime` int(11) DEFAULT NULL COMMENT '正常订单超时时间(分)',\n `confirm_overtime` int(11) DEFAULT NULL COMMENT '发货后自动确认收货时间(天)',\n `finish_overtime` int(11) DEFAULT NULL COMMENT '自动完成交易时间,不能申请售后(天)',\n `comment_overtime` int(11) DEFAULT NULL COMMENT '订单完成后自动好评时间(天)',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单设置表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "flash_order_overtime", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "秒杀订单超时关闭时间(分)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "flash_order_overtime" }, { "objectType": "TableField_MYSQL", "name": "normal_order_overtime", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "正常订单超时时间(分)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "normal_order_overtime" }, { "objectType": "TableField_MYSQL", "name": "confirm_overtime", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "发货后自动确认收货时间(天)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "confirm_overtime" }, { "objectType": "TableField_MYSQL", "name": "finish_overtime", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "自动完成交易时间,不能申请售后(天)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "finish_overtime" }, { "objectType": "TableField_MYSQL", "name": "comment_overtime", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单完成后自动好评时间(天)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "comment_overtime" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_album", "comment": "相册表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_album", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_album` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(64) DEFAULT NULL,\n `cover_pic` varchar(1000) DEFAULT NULL,\n `pic_count` int(11) DEFAULT NULL,\n `sort` int(11) DEFAULT NULL,\n `description` varchar(1000) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='相册表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "cover_pic", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "cover_pic" }, { "objectType": "TableField_MYSQL", "name": "pic_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pic_count" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_album_pic", "comment": "画册图片表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_album_pic", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_album_pic` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `album_id` bigint(20) DEFAULT NULL,\n `pic` varchar(1000) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_25` (`album_id`),\n CONSTRAINT `FK_Reference_25` FOREIGN KEY (`album_id`) REFERENCES `pms_album` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='画册图片表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "album_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "album_id" }, { "objectType": "TableField_MYSQL", "name": "pic", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pic" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_25", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_25", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "album_id", "keyLength": 0, "order": "", "oldName": "album_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_25", "fields": [ "album_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_album", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_25" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_brand", "comment": "品牌表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_brand", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_brand` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(64) DEFAULT NULL,\n `first_letter` varchar(8) DEFAULT NULL COMMENT '首字母',\n `sort` int(11) DEFAULT NULL,\n `factory_status` int(1) DEFAULT NULL COMMENT '是否为品牌制造商:0->不是;1->是',\n `show_status` int(1) DEFAULT NULL,\n `product_count` int(11) DEFAULT NULL COMMENT '产品数量',\n `product_comment_count` int(11) DEFAULT NULL COMMENT '产品评论数量',\n `logo` varchar(255) DEFAULT NULL COMMENT '品牌logo',\n `big_pic` varchar(255) DEFAULT NULL COMMENT '专区大图',\n `brand_story` text COMMENT '品牌故事',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='品牌表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "first_letter", "type": "varchar", "length": 8, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "首字母", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "first_letter" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "factory_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否为品牌制造商:0->不是;1->是", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "factory_status" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" }, { "objectType": "TableField_MYSQL", "name": "product_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "产品数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_count" }, { "objectType": "TableField_MYSQL", "name": "product_comment_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "产品评论数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_comment_count" }, { "objectType": "TableField_MYSQL", "name": "logo", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "品牌logo", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "logo" }, { "objectType": "TableField_MYSQL", "name": "big_pic", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "专区大图", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "big_pic" }, { "objectType": "TableField_MYSQL", "name": "brand_story", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "品牌故事", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "brand_story" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_comment", "comment": "商品评价表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_comment", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_comment` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `member_nick_name` varchar(255) DEFAULT NULL,\n `product_name` varchar(255) DEFAULT NULL,\n `star` int(3) DEFAULT NULL COMMENT '评价星数:0->5',\n `member_ip` varchar(64) DEFAULT NULL COMMENT '评价的ip',\n `create_time` datetime DEFAULT NULL,\n `show_status` int(1) DEFAULT NULL,\n `product_attribute` varchar(255) DEFAULT NULL COMMENT '购买时的商品属性',\n `collect_couont` int(11) DEFAULT NULL,\n `read_count` int(11) DEFAULT NULL,\n `content` text,\n `pics` varchar(1000) DEFAULT NULL COMMENT '上传图片地址,以逗号隔开',\n `member_icon` varchar(255) DEFAULT NULL COMMENT '评论用户头像',\n `replay_count` int(11) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_23` (`product_id`),\n CONSTRAINT `FK_Reference_23` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品评价表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "member_nick_name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_nick_name" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "star", "type": "int", "length": 3, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "评价星数:0->5", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "star" }, { "objectType": "TableField_MYSQL", "name": "member_ip", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "评价的ip", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_ip" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" }, { "objectType": "TableField_MYSQL", "name": "product_attribute", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "购买时的商品属性", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attribute" }, { "objectType": "TableField_MYSQL", "name": "collect_couont", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "collect_couont" }, { "objectType": "TableField_MYSQL", "name": "read_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "read_count" }, { "objectType": "TableField_MYSQL", "name": "content", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "content" }, { "objectType": "TableField_MYSQL", "name": "pics", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "上传图片地址,以逗号隔开", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pics" }, { "objectType": "TableField_MYSQL", "name": "member_icon", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "评论用户头像", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_icon" }, { "objectType": "TableField_MYSQL", "name": "replay_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "replay_count" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_23", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_23", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_23", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_23" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_comment_replay", "comment": "产品评价回复表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_comment_replay", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_comment_replay` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `comment_id` bigint(20) DEFAULT NULL,\n `member_nick_name` varchar(255) DEFAULT NULL,\n `member_icon` varchar(255) DEFAULT NULL,\n `content` varchar(1000) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `type` int(1) DEFAULT NULL COMMENT '评论人员类型;0->会员;1->管理员',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_24` (`comment_id`),\n CONSTRAINT `FK_Reference_24` FOREIGN KEY (`comment_id`) REFERENCES `pms_comment` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品评价回复表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "comment_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "comment_id" }, { "objectType": "TableField_MYSQL", "name": "member_nick_name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_nick_name" }, { "objectType": "TableField_MYSQL", "name": "member_icon", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_icon" }, { "objectType": "TableField_MYSQL", "name": "content", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "content" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "评论人员类型;0->会员;1->管理员", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_24", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_24", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "comment_id", "keyLength": 0, "order": "", "oldName": "comment_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_24", "fields": [ "comment_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_comment", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_24" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_feight_template", "comment": "运费模版", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_feight_template", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_feight_template` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(64) DEFAULT NULL,\n `charge_type` int(1) DEFAULT NULL COMMENT '计费类型:0->按重量;1->按件数',\n `first_weight` decimal(10,2) DEFAULT NULL COMMENT '首重kg',\n `first_fee` decimal(10,2) DEFAULT NULL COMMENT '首费(元)',\n `continue_weight` decimal(10,2) DEFAULT NULL,\n `continme_fee` decimal(10,2) DEFAULT NULL,\n `dest` varchar(255) DEFAULT NULL COMMENT '目的地(省、市)',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='运费模版'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "charge_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "计费类型:0->按重量;1->按件数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "charge_type" }, { "objectType": "TableField_MYSQL", "name": "first_weight", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "首重kg", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "first_weight" }, { "objectType": "TableField_MYSQL", "name": "first_fee", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "首费(元)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "first_fee" }, { "objectType": "TableField_MYSQL", "name": "continue_weight", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "continue_weight" }, { "objectType": "TableField_MYSQL", "name": "continme_fee", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "continme_fee" }, { "objectType": "TableField_MYSQL", "name": "dest", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "目的地(省、市)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "dest" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_member_price", "comment": "商品会员价格表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_member_price", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_member_price` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `member_level_id` bigint(20) DEFAULT NULL,\n `member_price` decimal(10,2) DEFAULT NULL COMMENT '会员价格',\n `member_level_name` varchar(100) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_60` (`member_level_id`),\n KEY `FK_Reference_9` (`product_id`),\n CONSTRAINT `FK_Reference_60` FOREIGN KEY (`member_level_id`) REFERENCES `ums_member_level` (`id`),\n CONSTRAINT `FK_Reference_9` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品会员价格表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "member_level_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_level_id" }, { "objectType": "TableField_MYSQL", "name": "member_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "会员价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_price" }, { "objectType": "TableField_MYSQL", "name": "member_level_name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_level_name" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_60", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_60", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_level_id", "keyLength": 0, "order": "", "oldName": "member_level_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_9", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_9", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_60", "fields": [ "member_level_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member_level", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_60" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_9", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_9" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product", "comment": "商品信息", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 65536, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `brand_id` bigint(20) DEFAULT NULL,\n `product_category_id` bigint(20) DEFAULT NULL,\n `feight_template_id` bigint(20) DEFAULT NULL,\n `product_attribute_category_id` bigint(20) DEFAULT NULL,\n `name` varchar(64) NOT NULL,\n `pic` varchar(255) DEFAULT NULL,\n `product_sn` varchar(64) NOT NULL COMMENT '货号',\n `delete_status` int(1) DEFAULT NULL COMMENT '删除状态:0->未删除;1->已删除',\n `publish_status` int(1) DEFAULT NULL COMMENT '上架状态:0->下架;1->上架',\n `new_status` int(1) DEFAULT NULL COMMENT '新品状态:0->不是新品;1->新品',\n `recommand_status` int(1) DEFAULT NULL COMMENT '推荐状态;0->不推荐;1->推荐',\n `verify_status` int(1) DEFAULT NULL COMMENT '审核状态:0->未审核;1->审核通过',\n `sort` int(11) DEFAULT NULL COMMENT '排序',\n `sale` int(11) DEFAULT NULL COMMENT '销量',\n `price` decimal(10,2) DEFAULT NULL,\n `promotion_price` decimal(10,2) DEFAULT NULL COMMENT '促销价格',\n `gift_growth` int(11) DEFAULT '0' COMMENT '赠送的成长值',\n `gift_point` int(11) DEFAULT '0' COMMENT '赠送的积分',\n `use_point_limit` int(11) DEFAULT NULL COMMENT '限制使用的积分数',\n `sub_title` varchar(255) DEFAULT NULL COMMENT '副标题',\n `description` text COMMENT '商品描述',\n `original_price` decimal(10,2) DEFAULT NULL COMMENT '市场价',\n `stock` int(11) DEFAULT NULL COMMENT '库存',\n `low_stock` int(11) DEFAULT NULL COMMENT '库存预警值',\n `unit` varchar(16) DEFAULT NULL COMMENT '单位',\n `weight` decimal(10,2) DEFAULT NULL COMMENT '商品重量,默认为克',\n `preview_status` int(1) DEFAULT NULL COMMENT '是否为预告商品:0->不是;1->是',\n `service_ids` varchar(64) DEFAULT NULL COMMENT '以逗号分割的产品服务:1->无忧退货;2->快速退款;3->免费包邮',\n `keywords` varchar(255) DEFAULT NULL,\n `note` varchar(255) DEFAULT NULL,\n `album_pics` varchar(255) DEFAULT NULL COMMENT '画册图片,连产品图片限制为5张,以逗号分割',\n `detail_title` varchar(255) DEFAULT NULL,\n `detail_desc` text,\n `detail_html` text COMMENT '产品详情网页内容',\n `detail_mobile_html` text COMMENT '移动端网页详情',\n `promotion_start_time` datetime DEFAULT NULL COMMENT '促销开始时间',\n `promotion_end_time` datetime DEFAULT NULL COMMENT '促销结束时间',\n `promotion_per_limit` int(11) DEFAULT NULL COMMENT '活动限购数量',\n `promotion_type` int(1) DEFAULT NULL COMMENT '促销类型:0->没有促销使用原价;1->使用促销价;2->使用会员价;3->使用阶梯价格;4->使用满减价格;5->限时购',\n `product_category_name` varchar(255) DEFAULT NULL COMMENT '产品分类名称',\n `brand_name` varchar(255) DEFAULT NULL COMMENT '品牌名称',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_1` (`brand_id`),\n KEY `FK_Reference_13` (`product_attribute_category_id`),\n KEY `FK_Reference_5` (`product_category_id`),\n KEY `FK_Reference_6` (`feight_template_id`),\n CONSTRAINT `FK_Reference_1` FOREIGN KEY (`brand_id`) REFERENCES `pms_brand` (`id`),\n CONSTRAINT `FK_Reference_13` FOREIGN KEY (`product_attribute_category_id`) REFERENCES `pms_product_attribute_category` (`id`),\n CONSTRAINT `FK_Reference_5` FOREIGN KEY (`product_category_id`) REFERENCES `pms_product_category` (`id`),\n CONSTRAINT `FK_Reference_6` FOREIGN KEY (`feight_template_id`) REFERENCES `pms_feight_template` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品信息'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "brand_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "brand_id" }, { "objectType": "TableField_MYSQL", "name": "product_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_id" }, { "objectType": "TableField_MYSQL", "name": "feight_template_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "feight_template_id" }, { "objectType": "TableField_MYSQL", "name": "product_attribute_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attribute_category_id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "pic", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pic" }, { "objectType": "TableField_MYSQL", "name": "product_sn", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "货号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sn" }, { "objectType": "TableField_MYSQL", "name": "delete_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "删除状态:0->未删除;1->已删除", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "delete_status" }, { "objectType": "TableField_MYSQL", "name": "publish_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "上架状态:0->下架;1->上架", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "publish_status" }, { "objectType": "TableField_MYSQL", "name": "new_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "新品状态:0->不是新品;1->新品", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "new_status" }, { "objectType": "TableField_MYSQL", "name": "recommand_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "推荐状态;0->不推荐;1->推荐", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recommand_status" }, { "objectType": "TableField_MYSQL", "name": "verify_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "审核状态:0->未审核;1->审核通过", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "verify_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "sale", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "销量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sale" }, { "objectType": "TableField_MYSQL", "name": "price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "price" }, { "objectType": "TableField_MYSQL", "name": "promotion_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "促销价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_price" }, { "objectType": "TableField_MYSQL", "name": "gift_growth", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "赠送的成长值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gift_growth" }, { "objectType": "TableField_MYSQL", "name": "gift_point", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "赠送的积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gift_point" }, { "objectType": "TableField_MYSQL", "name": "use_point_limit", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "限制使用的积分数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_point_limit" }, { "objectType": "TableField_MYSQL", "name": "sub_title", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "副标题", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sub_title" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品描述", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" }, { "objectType": "TableField_MYSQL", "name": "original_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "市场价", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "original_price" }, { "objectType": "TableField_MYSQL", "name": "stock", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "库存", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "stock" }, { "objectType": "TableField_MYSQL", "name": "low_stock", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "库存预警值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "low_stock" }, { "objectType": "TableField_MYSQL", "name": "unit", "type": "varchar", "length": 16, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "单位", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "unit" }, { "objectType": "TableField_MYSQL", "name": "weight", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品重量,默认为克", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "weight" }, { "objectType": "TableField_MYSQL", "name": "preview_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否为预告商品:0->不是;1->是", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "preview_status" }, { "objectType": "TableField_MYSQL", "name": "service_ids", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "以逗号分割的产品服务:1->无忧退货;2->快速退款;3->免费包邮", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "service_ids" }, { "objectType": "TableField_MYSQL", "name": "keywords", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "keywords" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" }, { "objectType": "TableField_MYSQL", "name": "album_pics", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "画册图片,连产品图片限制为5张,以逗号分割", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "album_pics" }, { "objectType": "TableField_MYSQL", "name": "detail_title", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail_title" }, { "objectType": "TableField_MYSQL", "name": "detail_desc", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail_desc" }, { "objectType": "TableField_MYSQL", "name": "detail_html", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "产品详情网页内容", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail_html" }, { "objectType": "TableField_MYSQL", "name": "detail_mobile_html", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "移动端网页详情", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail_mobile_html" }, { "objectType": "TableField_MYSQL", "name": "promotion_start_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "促销开始时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_start_time" }, { "objectType": "TableField_MYSQL", "name": "promotion_end_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "促销结束时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_end_time" }, { "objectType": "TableField_MYSQL", "name": "promotion_per_limit", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "活动限购数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_per_limit" }, { "objectType": "TableField_MYSQL", "name": "promotion_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "促销类型:0->没有促销使用原价;1->使用促销价;2->使用会员价;3->使用阶梯价格;4->使用满减价格;5->限时购", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_type" }, { "objectType": "TableField_MYSQL", "name": "product_category_name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "产品分类名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_name" }, { "objectType": "TableField_MYSQL", "name": "brand_name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "品牌名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "brand_name" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_1", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_1", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "brand_id", "keyLength": 0, "order": "", "oldName": "brand_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_13", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_13", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_attribute_category_id", "keyLength": 0, "order": "", "oldName": "product_attribute_category_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_5", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_5", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_category_id", "keyLength": 0, "order": "", "oldName": "product_category_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_6", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_6", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "feight_template_id", "keyLength": 0, "order": "", "oldName": "feight_template_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_1", "fields": [ "brand_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_brand", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_1" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_13", "fields": [ "product_attribute_category_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_attribute_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_13" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_5", "fields": [ "product_category_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_5" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_6", "fields": [ "feight_template_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_feight_template", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_6" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_attribute", "comment": "商品属性参数表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_attribute", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_attribute` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_attribute_category_id` bigint(20) DEFAULT NULL,\n `name` varchar(64) DEFAULT NULL,\n `select_type` int(1) DEFAULT NULL COMMENT '属性选择类型:0->唯一;1->单选;2->多选;对应属性和参数意义不同;',\n `input_type` int(1) DEFAULT NULL COMMENT '属性录入方式:0->手工录入;1->从列表中选取',\n `input_list` varchar(255) DEFAULT NULL COMMENT '可选值列表,以逗号隔开',\n `sort` int(11) DEFAULT NULL COMMENT '排序字段:最高的可以单独上传图片',\n `filter_type` int(1) DEFAULT NULL COMMENT '分类筛选样式:1->普通;1->颜色',\n `search_type` int(1) DEFAULT NULL COMMENT '检索类型;0->不需要进行检索;1->关键字检索;2->范围检索',\n `related_status` int(1) DEFAULT NULL COMMENT '相同属性产品是否关联;0->不关联;1->关联',\n `hand_add_status` int(1) DEFAULT NULL COMMENT '是否支持手动新增;0->不支持;1->支持',\n `type` int(1) DEFAULT NULL COMMENT '属性的类型;0->规格;1->参数',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_12` (`product_attribute_category_id`),\n CONSTRAINT `FK_Reference_12` FOREIGN KEY (`product_attribute_category_id`) REFERENCES `pms_product_attribute_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品属性参数表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_attribute_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attribute_category_id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "select_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "属性选择类型:0->唯一;1->单选;2->多选;对应属性和参数意义不同;", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "select_type" }, { "objectType": "TableField_MYSQL", "name": "input_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "属性录入方式:0->手工录入;1->从列表中选取", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "input_type" }, { "objectType": "TableField_MYSQL", "name": "input_list", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "可选值列表,以逗号隔开", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "input_list" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序字段:最高的可以单独上传图片", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "filter_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "分类筛选样式:1->普通;1->颜色", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "filter_type" }, { "objectType": "TableField_MYSQL", "name": "search_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "检索类型;0->不需要进行检索;1->关键字检索;2->范围检索", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "search_type" }, { "objectType": "TableField_MYSQL", "name": "related_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "相同属性产品是否关联;0->不关联;1->关联", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "related_status" }, { "objectType": "TableField_MYSQL", "name": "hand_add_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否支持手动新增;0->不支持;1->支持", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "hand_add_status" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "属性的类型;0->规格;1->参数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_12", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_12", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_attribute_category_id", "keyLength": 0, "order": "", "oldName": "product_attribute_category_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_12", "fields": [ "product_attribute_category_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_attribute_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_12" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_attribute_category", "comment": "产品属性分类表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_attribute_category", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_attribute_category` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(64) DEFAULT NULL,\n `attribute_count` int(11) DEFAULT NULL COMMENT '属性数量',\n `param_count` int(11) DEFAULT NULL COMMENT '参数数量',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品属性分类表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "attribute_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "属性数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "attribute_count" }, { "objectType": "TableField_MYSQL", "name": "param_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "参数数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "param_count" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_attribute_value", "comment": "存储产品参数信息的表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_attribute_value", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_attribute_value` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `product_attribute_id` bigint(20) DEFAULT NULL,\n `value` varchar(64) DEFAULT NULL COMMENT '手动添加规格或参数的值,参数单值,规格有多个时以逗号隔开',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_14` (`product_id`),\n KEY `FK_Reference_15` (`product_attribute_id`),\n CONSTRAINT `FK_Reference_14` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`),\n CONSTRAINT `FK_Reference_15` FOREIGN KEY (`product_attribute_id`) REFERENCES `pms_product_attribute` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储产品参数信息的表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "product_attribute_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attribute_id" }, { "objectType": "TableField_MYSQL", "name": "value", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "手动添加规格或参数的值,参数单值,规格有多个时以逗号隔开", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "value" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_14", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_14", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_15", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_15", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_attribute_id", "keyLength": 0, "order": "", "oldName": "product_attribute_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_14", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_14" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_15", "fields": [ "product_attribute_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_attribute", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_15" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_category", "comment": "产品分类", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_category", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_category` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `parent_id` bigint(20) DEFAULT NULL COMMENT '上机分类的编号:0表示一级分类',\n `name` varchar(64) DEFAULT NULL,\n `level` int(1) DEFAULT NULL COMMENT '分类级别:0->1级;1->2级',\n `product_count` int(11) DEFAULT NULL,\n `product_unit` varchar(64) DEFAULT NULL,\n `nav_status` int(1) DEFAULT NULL COMMENT '是否显示在导航栏:0->不显示;1->显示',\n `show_status` int(1) DEFAULT NULL COMMENT '显示状态:0->不显示;1->显示',\n `sort` int(11) DEFAULT NULL,\n `icon` varchar(255) DEFAULT NULL COMMENT '图标',\n `keywords` varchar(255) DEFAULT NULL,\n `description` text COMMENT '描述',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_20` (`parent_id`),\n CONSTRAINT `FK_Reference_20` FOREIGN KEY (`parent_id`) REFERENCES `pms_product_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品分类'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "parent_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "上机分类的编号:0表示一级分类", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "parent_id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "level", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "分类级别:0->1级;1->2级", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "level" }, { "objectType": "TableField_MYSQL", "name": "product_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_count" }, { "objectType": "TableField_MYSQL", "name": "product_unit", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_unit" }, { "objectType": "TableField_MYSQL", "name": "nav_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否显示在导航栏:0->不显示;1->显示", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "nav_status" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "显示状态:0->不显示;1->显示", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "图标", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "keywords", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "keywords" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "描述", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_20", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_20", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "parent_id", "keyLength": 0, "order": "", "oldName": "parent_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_20", "fields": [ "parent_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_20" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_category_attribute_relation", "comment": "产品的分类和属性的关系表,用于设置分类筛选条件(只支持一级分类)", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_category_attribute_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_category_attribute_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_category_id` bigint(20) DEFAULT NULL,\n `product_attribute_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_21` (`product_category_id`),\n KEY `FK_Reference_22` (`product_attribute_id`),\n CONSTRAINT `FK_Reference_21` FOREIGN KEY (`product_category_id`) REFERENCES `pms_product_category` (`id`),\n CONSTRAINT `FK_Reference_22` FOREIGN KEY (`product_attribute_id`) REFERENCES `pms_product_attribute` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品的分类和属性的关系表,用于设置分类筛选条件(只支持一级分类)'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_id" }, { "objectType": "TableField_MYSQL", "name": "product_attribute_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attribute_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_21", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_21", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_category_id", "keyLength": 0, "order": "", "oldName": "product_category_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_22", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_22", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_attribute_id", "keyLength": 0, "order": "", "oldName": "product_attribute_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_21", "fields": [ "product_category_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_21" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_22", "fields": [ "product_attribute_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_attribute", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_22" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_full_reduction", "comment": "产品满减表(只针对同商品)", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_full_reduction", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_full_reduction` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `full_price` decimal(10,2) DEFAULT NULL,\n `reduce_price` decimal(10,2) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_11` (`product_id`),\n CONSTRAINT `FK_Reference_11` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品满减表(只针对同商品)'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "full_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "full_price" }, { "objectType": "TableField_MYSQL", "name": "reduce_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "reduce_price" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_11", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_11", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_11", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_11" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_ladder", "comment": "产品阶梯价格表(只针对同商品)", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_ladder", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_ladder` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `count` int(11) DEFAULT NULL COMMENT '满足的商品数量',\n `discount` decimal(10,2) DEFAULT NULL COMMENT '折扣',\n `price` decimal(10,2) DEFAULT NULL COMMENT '折后价格',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_10` (`product_id`),\n CONSTRAINT `FK_Reference_10` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品阶梯价格表(只针对同商品)'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "满足的商品数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "count" }, { "objectType": "TableField_MYSQL", "name": "discount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "折扣", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "discount" }, { "objectType": "TableField_MYSQL", "name": "price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "折后价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "price" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_10", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_10", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_10", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_10" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_operate_log", "comment": "", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_operate_log", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:55", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_operate_log` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `price_old` decimal(10,2) DEFAULT NULL,\n `price_new` decimal(10,2) DEFAULT NULL,\n `sale_price_old` decimal(10,2) DEFAULT NULL,\n `sale_price_new` decimal(10,2) DEFAULT NULL,\n `gift_point_old` int(11) DEFAULT NULL COMMENT '赠送的积分',\n `gift_point_new` int(11) DEFAULT NULL,\n `use_point_limit_old` int(11) DEFAULT NULL,\n `use_point_limit_new` int(11) DEFAULT NULL,\n `operate_man` varchar(64) DEFAULT NULL COMMENT '操作人',\n `create_time` datetime DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_4` (`product_id`),\n CONSTRAINT `FK_Reference_4` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "price_old", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "price_old" }, { "objectType": "TableField_MYSQL", "name": "price_new", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "price_new" }, { "objectType": "TableField_MYSQL", "name": "sale_price_old", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sale_price_old" }, { "objectType": "TableField_MYSQL", "name": "sale_price_new", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sale_price_new" }, { "objectType": "TableField_MYSQL", "name": "gift_point_old", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "赠送的积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gift_point_old" }, { "objectType": "TableField_MYSQL", "name": "gift_point_new", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gift_point_new" }, { "objectType": "TableField_MYSQL", "name": "use_point_limit_old", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_point_limit_old" }, { "objectType": "TableField_MYSQL", "name": "use_point_limit_new", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_point_limit_new" }, { "objectType": "TableField_MYSQL", "name": "operate_man", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作人", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_man" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_4", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_4", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_4", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_4" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_vertify_record", "comment": "商品审核记录", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_vertify_record", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_vertify_record` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `vertify_man` varchar(64) DEFAULT NULL COMMENT '审核人',\n `status` int(1) DEFAULT NULL COMMENT '审核后的状态:0->未通过;2->已通过',\n `detail` varchar(255) DEFAULT NULL COMMENT '反馈详情',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_3` (`product_id`),\n CONSTRAINT `FK_Reference_3` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品审核记录'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "vertify_man", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "审核人", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "vertify_man" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "审核后的状态:0->未通过;2->已通过", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "detail", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "反馈详情", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_3", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_3", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_3", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_3" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_sku_stock", "comment": "sku的库存", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_sku_stock", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_sku_stock` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `sku_code` varchar(64) NOT NULL COMMENT 'sku编码',\n `price` decimal(10,2) DEFAULT NULL,\n `stock` int(11) DEFAULT '0' COMMENT '库存',\n `low_stock` int(11) DEFAULT NULL COMMENT '预警库存',\n `pic` varchar(255) DEFAULT NULL COMMENT '展示图片',\n `sale` int(11) DEFAULT NULL COMMENT '销量',\n `promotion_price` decimal(10,2) DEFAULT NULL COMMENT '单品促销价格',\n `lock_stock` int(11) DEFAULT '0' COMMENT '锁定库存',\n `sp_data` varchar(500) DEFAULT NULL COMMENT '商品销售属性,json格式',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_2` (`product_id`),\n CONSTRAINT `FK_Reference_2` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='sku的库存'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "sku_code", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "sku编码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sku_code" }, { "objectType": "TableField_MYSQL", "name": "price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "price" }, { "objectType": "TableField_MYSQL", "name": "stock", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "库存", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "stock" }, { "objectType": "TableField_MYSQL", "name": "low_stock", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "预警库存", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "low_stock" }, { "objectType": "TableField_MYSQL", "name": "pic", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "展示图片", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pic" }, { "objectType": "TableField_MYSQL", "name": "sale", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "销量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sale" }, { "objectType": "TableField_MYSQL", "name": "promotion_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "单品促销价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_price" }, { "objectType": "TableField_MYSQL", "name": "lock_stock", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "锁定库存", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "lock_stock" }, { "objectType": "TableField_MYSQL", "name": "sp_data", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品销售属性,json格式", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sp_data" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_2", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_2", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_2", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_2" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_coupon", "comment": "优惠卷表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_coupon", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_coupon` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `type` int(1) DEFAULT NULL COMMENT '优惠卷类型;0->全场赠券;1->会员赠券;2->购物赠券;3->注册赠券',\n `name` varchar(100) DEFAULT NULL,\n `platform` int(1) DEFAULT NULL COMMENT '使用平台:0->全部;1->移动;2->PC',\n `count` int(11) DEFAULT NULL COMMENT '数量',\n `amount` decimal(10,2) DEFAULT NULL COMMENT '金额',\n `per_limit` int(11) DEFAULT NULL COMMENT '每人限领张数',\n `min_point` decimal(10,2) DEFAULT NULL COMMENT '使用门槛;0表示无门槛',\n `start_time` datetime DEFAULT NULL,\n `end_time` datetime DEFAULT NULL,\n `use_type` int(1) DEFAULT NULL COMMENT '使用类型:0->全场通用;1->指定分类;2->指定商品',\n `note` varchar(200) DEFAULT NULL COMMENT '备注',\n `publish_count` int(11) DEFAULT NULL COMMENT '发行数量',\n `use_count` int(11) DEFAULT NULL COMMENT '已使用数量',\n `receive_count` int(11) DEFAULT NULL COMMENT '领取数量',\n `enable_time` datetime DEFAULT NULL COMMENT '可以领取的日期',\n `code` varchar(64) DEFAULT NULL COMMENT '优惠码',\n `member_level` int(1) DEFAULT NULL COMMENT '可领取的会员类型:0->无限时',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='优惠卷表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "优惠卷类型;0->全场赠券;1->会员赠券;2->购物赠券;3->注册赠券", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "platform", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "使用平台:0->全部;1->移动;2->PC", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "platform" }, { "objectType": "TableField_MYSQL", "name": "count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "count" }, { "objectType": "TableField_MYSQL", "name": "amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "amount" }, { "objectType": "TableField_MYSQL", "name": "per_limit", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每人限领张数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "per_limit" }, { "objectType": "TableField_MYSQL", "name": "min_point", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "使用门槛;0表示无门槛", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "min_point" }, { "objectType": "TableField_MYSQL", "name": "start_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "start_time" }, { "objectType": "TableField_MYSQL", "name": "end_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "end_time" }, { "objectType": "TableField_MYSQL", "name": "use_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "使用类型:0->全场通用;1->指定分类;2->指定商品", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_type" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" }, { "objectType": "TableField_MYSQL", "name": "publish_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "发行数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "publish_count" }, { "objectType": "TableField_MYSQL", "name": "use_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "已使用数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_count" }, { "objectType": "TableField_MYSQL", "name": "receive_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "领取数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receive_count" }, { "objectType": "TableField_MYSQL", "name": "enable_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "可以领取的日期", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "enable_time" }, { "objectType": "TableField_MYSQL", "name": "code", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "优惠码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "code" }, { "objectType": "TableField_MYSQL", "name": "member_level", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "可领取的会员类型:0->无限时", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_level" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_coupon_history", "comment": "优惠券使用、领取历史表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_coupon_history", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 49152, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_coupon_history` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `coupon_id` bigint(20) DEFAULT NULL,\n `member_id` bigint(20) DEFAULT NULL,\n `order_id` bigint(20) DEFAULT NULL COMMENT '订单id',\n `coupon_code` varchar(64) DEFAULT NULL,\n `member_nickname` varchar(64) DEFAULT NULL COMMENT '领取人昵称',\n `get_type` int(1) DEFAULT NULL COMMENT '获取类型:0->后台赠送;1->主动获取',\n `create_time` datetime DEFAULT NULL,\n `use_status` int(1) DEFAULT NULL COMMENT '使用状态:0->未使用;1->已使用;2->已过期',\n `use_time` datetime DEFAULT NULL COMMENT '使用时间',\n `order_sn` varchar(100) DEFAULT NULL COMMENT '订单号码',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_37` (`coupon_id`),\n KEY `FK_Reference_38` (`member_id`),\n KEY `FK_Reference_76` (`order_id`),\n CONSTRAINT `FK_Reference_37` FOREIGN KEY (`coupon_id`) REFERENCES `sms_coupon` (`id`),\n CONSTRAINT `FK_Reference_38` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`),\n CONSTRAINT `FK_Reference_76` FOREIGN KEY (`order_id`) REFERENCES `oms_order` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='优惠券使用、领取历史表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "coupon_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "order_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_id" }, { "objectType": "TableField_MYSQL", "name": "coupon_code", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_code" }, { "objectType": "TableField_MYSQL", "name": "member_nickname", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "领取人昵称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_nickname" }, { "objectType": "TableField_MYSQL", "name": "get_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "获取类型:0->后台赠送;1->主动获取", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "get_type" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "use_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "使用状态:0->未使用;1->已使用;2->已过期", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_status" }, { "objectType": "TableField_MYSQL", "name": "use_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "使用时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_time" }, { "objectType": "TableField_MYSQL", "name": "order_sn", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单号码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_sn" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_37", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_37", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "coupon_id", "keyLength": 0, "order": "", "oldName": "coupon_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_38", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_38", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_76", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_76", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "order_id", "keyLength": 0, "order": "", "oldName": "order_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_37", "fields": [ "coupon_id" ], "referenceSchema": "mall-ref", "referenceTable": "sms_coupon", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_37" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_38", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_38" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_76", "fields": [ "order_id" ], "referenceSchema": "mall-ref", "referenceTable": "oms_order", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_76" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_coupon_product_category_relation", "comment": "优惠券和产品分类关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_coupon_product_category_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_coupon_product_category_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `coupon_id` bigint(20) DEFAULT NULL,\n `product_category_id` bigint(20) DEFAULT NULL,\n `product_category_name` varchar(200) DEFAULT NULL COMMENT '产品分类名称',\n `parent_category_name` varchar(200) DEFAULT NULL COMMENT '父分类名称',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_35` (`coupon_id`),\n KEY `FK_Reference_36` (`product_category_id`),\n CONSTRAINT `FK_Reference_35` FOREIGN KEY (`coupon_id`) REFERENCES `sms_coupon` (`id`),\n CONSTRAINT `FK_Reference_36` FOREIGN KEY (`product_category_id`) REFERENCES `pms_product_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='优惠券和产品分类关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "coupon_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_id" }, { "objectType": "TableField_MYSQL", "name": "product_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_id" }, { "objectType": "TableField_MYSQL", "name": "product_category_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "产品分类名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_name" }, { "objectType": "TableField_MYSQL", "name": "parent_category_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "父分类名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "parent_category_name" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_35", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_35", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "coupon_id", "keyLength": 0, "order": "", "oldName": "coupon_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_36", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_36", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_category_id", "keyLength": 0, "order": "", "oldName": "product_category_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_35", "fields": [ "coupon_id" ], "referenceSchema": "mall-ref", "referenceTable": "sms_coupon", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_35" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_36", "fields": [ "product_category_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_36" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_coupon_product_relation", "comment": "优惠券和产品的关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_coupon_product_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_coupon_product_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `coupon_id` bigint(20) DEFAULT NULL,\n `product_id` bigint(20) DEFAULT NULL,\n `product_name` varchar(500) DEFAULT NULL COMMENT '商品名称',\n `product_sn` varchar(200) DEFAULT NULL COMMENT '商品编码',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_33` (`coupon_id`),\n KEY `FK_Reference_34` (`product_id`),\n CONSTRAINT `FK_Reference_33` FOREIGN KEY (`coupon_id`) REFERENCES `sms_coupon` (`id`),\n CONSTRAINT `FK_Reference_34` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='优惠券和产品的关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "coupon_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "product_sn", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品编码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sn" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_33", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_33", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "coupon_id", "keyLength": 0, "order": "", "oldName": "coupon_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_34", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_34", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_33", "fields": [ "coupon_id" ], "referenceSchema": "mall-ref", "referenceTable": "sms_coupon", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_33" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_34", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_34" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_flash_promotion", "comment": "限时购表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_flash_promotion", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_flash_promotion` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `title` varchar(200) DEFAULT NULL,\n `start_date` date DEFAULT NULL COMMENT '开始日期',\n `end_date` date DEFAULT NULL COMMENT '结束日期',\n `status` int(1) DEFAULT NULL COMMENT '上下线状态',\n `create_time` datetime DEFAULT NULL COMMENT '秒杀时间段名称',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='限时购表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "title", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "title" }, { "objectType": "TableField_MYSQL", "name": "start_date", "type": "date", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "开始日期", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "start_date" }, { "objectType": "TableField_MYSQL", "name": "end_date", "type": "date", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "结束日期", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "end_date" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "上下线状态", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "秒杀时间段名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_flash_promotion_log", "comment": "限时购通知记录", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_flash_promotion_log", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_flash_promotion_log` (\n `id` int(11) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `product_id` bigint(20) DEFAULT NULL,\n `member_phone` varchar(64) DEFAULT NULL,\n `product_name` varchar(100) DEFAULT NULL,\n `subscribe_time` datetime DEFAULT NULL COMMENT '会员订阅时间',\n `send_time` datetime DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_44` (`member_id`),\n KEY `FK_Reference_45` (`product_id`),\n CONSTRAINT `FK_Reference_44` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`),\n CONSTRAINT `FK_Reference_45` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='限时购通知记录'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "member_phone", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_phone" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "subscribe_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "会员订阅时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "subscribe_time" }, { "objectType": "TableField_MYSQL", "name": "send_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "send_time" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_44", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_44", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_45", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_45", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_44", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_44" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_45", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_45" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_flash_promotion_product_relation", "comment": "商品限时购与商品关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_flash_promotion_product_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 49152, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_flash_promotion_product_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',\n `flash_promotion_id` bigint(20) DEFAULT NULL,\n `flash_promotion_session_id` bigint(20) DEFAULT NULL COMMENT '编号',\n `product_id` bigint(20) DEFAULT NULL,\n `flash_promotion_price` decimal(10,2) DEFAULT NULL COMMENT '限时购价格',\n `flash_promotion_count` int(11) DEFAULT NULL COMMENT '限时购数量',\n `flash_promotion_limit` int(11) DEFAULT NULL COMMENT '每人限购数量',\n `sort` int(11) DEFAULT NULL COMMENT '排序',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_77` (`flash_promotion_id`),\n KEY `FK_Reference_78` (`flash_promotion_session_id`),\n KEY `FK_Reference_79` (`product_id`),\n CONSTRAINT `FK_Reference_77` FOREIGN KEY (`flash_promotion_id`) REFERENCES `sms_flash_promotion` (`id`),\n CONSTRAINT `FK_Reference_78` FOREIGN KEY (`flash_promotion_session_id`) REFERENCES `sms_flash_promotion_session` (`id`),\n CONSTRAINT `FK_Reference_79` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品限时购与商品关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "flash_promotion_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "flash_promotion_id" }, { "objectType": "TableField_MYSQL", "name": "flash_promotion_session_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "flash_promotion_session_id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "flash_promotion_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "限时购价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "flash_promotion_price" }, { "objectType": "TableField_MYSQL", "name": "flash_promotion_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "限时购数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "flash_promotion_count" }, { "objectType": "TableField_MYSQL", "name": "flash_promotion_limit", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每人限购数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "flash_promotion_limit" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_77", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_77", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "flash_promotion_id", "keyLength": 0, "order": "", "oldName": "flash_promotion_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_78", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_78", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "flash_promotion_session_id", "keyLength": 0, "order": "", "oldName": "flash_promotion_session_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_79", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_79", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_77", "fields": [ "flash_promotion_id" ], "referenceSchema": "mall-ref", "referenceTable": "sms_flash_promotion", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_77" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_78", "fields": [ "flash_promotion_session_id" ], "referenceSchema": "mall-ref", "referenceTable": "sms_flash_promotion_session", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_78" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_79", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_79" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_flash_promotion_session", "comment": "限时购场次表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_flash_promotion_session", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_flash_promotion_session` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',\n `name` varchar(200) DEFAULT NULL COMMENT '场次名称',\n `start_time` time DEFAULT NULL COMMENT '每日开始时间',\n `end_time` time DEFAULT NULL COMMENT '每日结束时间',\n `status` int(1) DEFAULT NULL COMMENT '启用状态:0->不启用;1->启用',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='限时购场次表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "场次名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "start_time", "type": "time", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每日开始时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "start_time" }, { "objectType": "TableField_MYSQL", "name": "end_time", "type": "time", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每日结束时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "end_time" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "启用状态:0->不启用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_home_advertise", "comment": "首页轮播广告表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_home_advertise", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_home_advertise` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL,\n `type` int(1) DEFAULT NULL COMMENT '轮播位置:0->PC首页轮播;1->app首页轮播',\n `pic` varchar(500) DEFAULT NULL,\n `start_time` datetime DEFAULT NULL,\n `end_time` datetime DEFAULT NULL,\n `status` int(1) DEFAULT NULL COMMENT '上下线状态:0->下线;1->上线',\n `click_count` int(11) DEFAULT NULL COMMENT '点击数',\n `order_count` int(11) DEFAULT NULL COMMENT '下单数',\n `url` varchar(500) DEFAULT NULL COMMENT '链接地址',\n `note` varchar(500) DEFAULT NULL COMMENT '备注',\n `sort` int(11) DEFAULT '0' COMMENT '排序',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='首页轮播广告表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "轮播位置:0->PC首页轮播;1->app首页轮播", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" }, { "objectType": "TableField_MYSQL", "name": "pic", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pic" }, { "objectType": "TableField_MYSQL", "name": "start_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "start_time" }, { "objectType": "TableField_MYSQL", "name": "end_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "end_time" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "上下线状态:0->下线;1->上线", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "click_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "点击数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "click_count" }, { "objectType": "TableField_MYSQL", "name": "order_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "下单数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_count" }, { "objectType": "TableField_MYSQL", "name": "url", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "链接地址", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "url" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_home_brand", "comment": "首页推荐品牌表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_home_brand", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_home_brand` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `brand_id` bigint(20) DEFAULT NULL,\n `brand_name` varchar(64) DEFAULT NULL,\n `recommend_status` int(1) DEFAULT NULL,\n `sort` int(11) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_39` (`brand_id`),\n CONSTRAINT `FK_Reference_39` FOREIGN KEY (`brand_id`) REFERENCES `pms_brand` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='首页推荐品牌表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "brand_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "brand_id" }, { "objectType": "TableField_MYSQL", "name": "brand_name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "brand_name" }, { "objectType": "TableField_MYSQL", "name": "recommend_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recommend_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_39", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_39", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "brand_id", "keyLength": 0, "order": "", "oldName": "brand_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_39", "fields": [ "brand_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_brand", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_39" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_home_new_product", "comment": "新鲜好物表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_home_new_product", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_home_new_product` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `product_name` varchar(64) DEFAULT NULL,\n `recommend_status` int(1) DEFAULT NULL,\n `sort` int(1) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_40` (`product_id`),\n CONSTRAINT `FK_Reference_40` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='新鲜好物表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "recommend_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recommend_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_40", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_40", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_40", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_40" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_home_recommend_product", "comment": "人气推荐商品表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_home_recommend_product", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_home_recommend_product` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `product_name` varchar(64) DEFAULT NULL,\n `recommend_status` int(1) DEFAULT NULL,\n `sort` int(1) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_41` (`product_id`),\n CONSTRAINT `FK_Reference_41` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='人气推荐商品表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "recommend_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recommend_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_41", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_41", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_41", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_41" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_home_recommend_subject", "comment": "首页推荐专题表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_home_recommend_subject", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_home_recommend_subject` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `subject_id` bigint(20) DEFAULT NULL,\n `subject_name` varchar(64) DEFAULT NULL,\n `recommend_status` int(1) DEFAULT NULL,\n `sort` int(11) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_42` (`subject_id`),\n CONSTRAINT `FK_Reference_42` FOREIGN KEY (`subject_id`) REFERENCES `cms_subject` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='首页推荐专题表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "subject_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "subject_id" }, { "objectType": "TableField_MYSQL", "name": "subject_name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "subject_name" }, { "objectType": "TableField_MYSQL", "name": "recommend_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recommend_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_42", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_42", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "subject_id", "keyLength": 0, "order": "", "oldName": "subject_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_42", "fields": [ "subject_id" ], "referenceSchema": "mall-ref", "referenceTable": "cms_subject", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_42" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_admin", "comment": "后台用户表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_admin", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_admin` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `username` varchar(64) DEFAULT NULL COMMENT '用户名',\n `password` varchar(64) DEFAULT NULL COMMENT '密码',\n `icon` varchar(500) DEFAULT NULL COMMENT '头像',\n `email` varchar(100) DEFAULT NULL COMMENT '邮箱',\n `nick_name` varchar(200) DEFAULT NULL COMMENT '昵称',\n `note` varchar(500) DEFAULT NULL COMMENT '备注信息',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `login_time` datetime DEFAULT NULL COMMENT '最后登录时间',\n `status` int(1) DEFAULT '1' COMMENT '帐号启用状态:0->禁用;1->启用',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "username", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "用户名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "username" }, { "objectType": "TableField_MYSQL", "name": "password", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "密码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "password" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "头像", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "email", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "邮箱", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "email" }, { "objectType": "TableField_MYSQL", "name": "nick_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "昵称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "nick_name" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "备注信息", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "login_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "最后登录时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "login_time" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "1", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "帐号启用状态:0->禁用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_admin_login_log", "comment": "后台用户登录日志表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_admin_login_log", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_admin_login_log` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `admin_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `ip` varchar(64) DEFAULT NULL,\n `address` varchar(100) DEFAULT NULL,\n `user_agent` varchar(100) DEFAULT NULL COMMENT '浏览器登录类型',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_46` (`admin_id`),\n CONSTRAINT `FK_Reference_46` FOREIGN KEY (`admin_id`) REFERENCES `ums_admin` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户登录日志表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "admin_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "admin_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "ip", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "ip" }, { "objectType": "TableField_MYSQL", "name": "address", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "address" }, { "objectType": "TableField_MYSQL", "name": "user_agent", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "浏览器登录类型", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "user_agent" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_46", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_46", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "admin_id", "keyLength": 0, "order": "", "oldName": "admin_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_46", "fields": [ "admin_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_admin", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_46" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_admin_permission_relation", "comment": "后台用户和权限关系表(除角色中定义的权限以外的加减权限)", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_admin_permission_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_admin_permission_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `admin_id` bigint(20) DEFAULT NULL,\n `permission_id` bigint(20) DEFAULT NULL,\n `type` int(1) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_73` (`admin_id`),\n KEY `FK_Reference_74` (`permission_id`),\n CONSTRAINT `FK_Reference_73` FOREIGN KEY (`admin_id`) REFERENCES `ums_admin` (`id`),\n CONSTRAINT `FK_Reference_74` FOREIGN KEY (`permission_id`) REFERENCES `ums_permission` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户和权限关系表(除角色中定义的权限以外的加减权限)'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "admin_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "admin_id" }, { "objectType": "TableField_MYSQL", "name": "permission_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "permission_id" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_73", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_73", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "admin_id", "keyLength": 0, "order": "", "oldName": "admin_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_74", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_74", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "permission_id", "keyLength": 0, "order": "", "oldName": "permission_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_73", "fields": [ "admin_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_admin", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_73" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_74", "fields": [ "permission_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_permission", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_74" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_admin_role_relation", "comment": "后台用户和角色关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_admin_role_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_admin_role_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `admin_id` bigint(20) DEFAULT NULL,\n `role_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_69` (`admin_id`),\n KEY `FK_Reference_70` (`role_id`),\n CONSTRAINT `FK_Reference_69` FOREIGN KEY (`admin_id`) REFERENCES `ums_admin` (`id`),\n CONSTRAINT `FK_Reference_70` FOREIGN KEY (`role_id`) REFERENCES `ums_role` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户和角色关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "admin_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "admin_id" }, { "objectType": "TableField_MYSQL", "name": "role_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "role_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_69", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_69", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "admin_id", "keyLength": 0, "order": "", "oldName": "admin_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_70", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_70", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "role_id", "keyLength": 0, "order": "", "oldName": "role_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_69", "fields": [ "admin_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_admin", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_69" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_70", "fields": [ "role_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_role", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_70" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_growth_change_history", "comment": "成长值变化历史记录表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_growth_change_history", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_growth_change_history` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `change_type` int(1) DEFAULT NULL COMMENT '改变类型:0->增加;1->减少',\n `change_count` int(11) DEFAULT NULL COMMENT '积分改变数量',\n `operate_man` varchar(100) DEFAULT NULL COMMENT '操作人员',\n `operate_note` varchar(200) DEFAULT NULL COMMENT '操作备注',\n `source_type` int(1) DEFAULT NULL COMMENT '积分来源:0->购物;1->管理员修改',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_56` (`member_id`),\n CONSTRAINT `FK_Reference_56` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='成长值变化历史记录表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "change_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "改变类型:0->增加;1->减少", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "change_type" }, { "objectType": "TableField_MYSQL", "name": "change_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分改变数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "change_count" }, { "objectType": "TableField_MYSQL", "name": "operate_man", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作人员", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_man" }, { "objectType": "TableField_MYSQL", "name": "operate_note", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_note" }, { "objectType": "TableField_MYSQL", "name": "source_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分来源:0->购物;1->管理员修改", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "source_type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_56", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_56", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_56", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_56" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_integration_change_history", "comment": "积分变化历史记录表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_integration_change_history", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_integration_change_history` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `change_type` int(1) DEFAULT NULL COMMENT '改变类型:0->增加;1->减少',\n `change_count` int(11) DEFAULT NULL COMMENT '积分改变数量',\n `operate_man` varchar(100) DEFAULT NULL COMMENT '操作人员',\n `operate_note` varchar(200) DEFAULT NULL COMMENT '操作备注',\n `source_type` int(1) DEFAULT NULL COMMENT '积分来源:0->购物;1->管理员修改',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_55` (`member_id`),\n CONSTRAINT `FK_Reference_55` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='积分变化历史记录表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "change_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "改变类型:0->增加;1->减少", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "change_type" }, { "objectType": "TableField_MYSQL", "name": "change_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分改变数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "change_count" }, { "objectType": "TableField_MYSQL", "name": "operate_man", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作人员", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_man" }, { "objectType": "TableField_MYSQL", "name": "operate_note", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_note" }, { "objectType": "TableField_MYSQL", "name": "source_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分来源:0->购物;1->管理员修改", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "source_type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_55", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_55", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_55", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_55" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_integration_consume_setting", "comment": "积分消费设置", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_integration_consume_setting", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_integration_consume_setting` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `deduction_per_amount` int(11) DEFAULT NULL COMMENT '每一元需要抵扣的积分数量',\n `max_percent_per_order` int(11) DEFAULT NULL COMMENT '每笔订单最高抵用百分比',\n `use_unit` int(11) DEFAULT NULL COMMENT '每次使用积分最小单位100',\n `coupon_status` int(1) DEFAULT NULL COMMENT '是否可以和优惠券同用;0->不可以;1->可以',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='积分消费设置'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "deduction_per_amount", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每一元需要抵扣的积分数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "deduction_per_amount" }, { "objectType": "TableField_MYSQL", "name": "max_percent_per_order", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每笔订单最高抵用百分比", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "max_percent_per_order" }, { "objectType": "TableField_MYSQL", "name": "use_unit", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每次使用积分最小单位100", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_unit" }, { "objectType": "TableField_MYSQL", "name": "coupon_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否可以和优惠券同用;0->不可以;1->可以", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_status" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member", "comment": "会员表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_level_id` bigint(20) DEFAULT NULL,\n `username` varchar(64) DEFAULT NULL COMMENT '用户名',\n `password` varchar(64) DEFAULT NULL COMMENT '密码',\n `nickname` varchar(64) DEFAULT NULL COMMENT '昵称',\n `phone` varchar(64) DEFAULT NULL COMMENT '手机号码',\n `status` int(1) DEFAULT NULL COMMENT '帐号启用状态:0->禁用;1->启用',\n `create_time` datetime DEFAULT NULL COMMENT '注册时间',\n `icon` varchar(500) DEFAULT NULL COMMENT '头像',\n `gender` int(1) DEFAULT NULL COMMENT '性别:0->未知;1->男;2->女',\n `birthday` date DEFAULT NULL COMMENT '生日',\n `city` varchar(64) DEFAULT NULL COMMENT '所做城市',\n `job` varchar(100) DEFAULT NULL COMMENT '职业',\n `personalized_signature` varchar(200) DEFAULT NULL COMMENT '个性签名',\n `source_type` int(1) DEFAULT NULL COMMENT '用户来源',\n `integration` int(11) DEFAULT NULL COMMENT '积分',\n `growth` int(11) DEFAULT NULL COMMENT '成长值',\n `luckey_count` int(11) DEFAULT NULL COMMENT '剩余抽奖次数',\n `history_integration` int(11) DEFAULT NULL COMMENT '历史积分数量',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_47` (`member_level_id`),\n CONSTRAINT `FK_Reference_47` FOREIGN KEY (`member_level_id`) REFERENCES `ums_member_level` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_level_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_level_id" }, { "objectType": "TableField_MYSQL", "name": "username", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "用户名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "username" }, { "objectType": "TableField_MYSQL", "name": "password", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "密码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "password" }, { "objectType": "TableField_MYSQL", "name": "nickname", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "昵称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "nickname" }, { "objectType": "TableField_MYSQL", "name": "phone", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "手机号码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "phone" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "帐号启用状态:0->禁用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "注册时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "头像", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "gender", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "性别:0->未知;1->男;2->女", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gender" }, { "objectType": "TableField_MYSQL", "name": "birthday", "type": "date", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "生日", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "birthday" }, { "objectType": "TableField_MYSQL", "name": "city", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "所做城市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "city" }, { "objectType": "TableField_MYSQL", "name": "job", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "职业", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "job" }, { "objectType": "TableField_MYSQL", "name": "personalized_signature", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "个性签名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "personalized_signature" }, { "objectType": "TableField_MYSQL", "name": "source_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "用户来源", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "source_type" }, { "objectType": "TableField_MYSQL", "name": "integration", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "integration" }, { "objectType": "TableField_MYSQL", "name": "growth", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "成长值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "growth" }, { "objectType": "TableField_MYSQL", "name": "luckey_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "剩余抽奖次数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "luckey_count" }, { "objectType": "TableField_MYSQL", "name": "history_integration", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "历史积分数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "history_integration" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_47", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_47", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_level_id", "keyLength": 0, "order": "", "oldName": "member_level_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_47", "fields": [ "member_level_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member_level", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_47" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_level", "comment": "会员等级表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_level", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_level` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL,\n `growth_point` int(11) DEFAULT NULL,\n `default_status` int(1) DEFAULT NULL COMMENT '是否为默认等级:0->不是;1->是',\n `free_freight_point` decimal(10,2) DEFAULT NULL COMMENT '免运费标准',\n `comment_growth_point` int(11) DEFAULT NULL COMMENT '每次评价获取的成长值',\n `priviledge_free_freight` int(1) DEFAULT NULL COMMENT '是否有免邮特权',\n `priviledge_sign_in` int(1) DEFAULT NULL COMMENT '是否有签到特权',\n `priviledge_comment` int(1) DEFAULT NULL COMMENT '是否有评论获奖励特权',\n `priviledge_promotion` int(1) DEFAULT NULL COMMENT '是否有专享活动特权',\n `priviledge_member_price` int(1) DEFAULT NULL COMMENT '是否有会员价格特权',\n `priviledge_birthday` int(1) DEFAULT NULL COMMENT '是否有生日特权',\n `note` varchar(200) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员等级表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "growth_point", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "growth_point" }, { "objectType": "TableField_MYSQL", "name": "default_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否为默认等级:0->不是;1->是", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "default_status" }, { "objectType": "TableField_MYSQL", "name": "free_freight_point", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "免运费标准", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "free_freight_point" }, { "objectType": "TableField_MYSQL", "name": "comment_growth_point", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每次评价获取的成长值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "comment_growth_point" }, { "objectType": "TableField_MYSQL", "name": "priviledge_free_freight", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否有免邮特权", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "priviledge_free_freight" }, { "objectType": "TableField_MYSQL", "name": "priviledge_sign_in", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否有签到特权", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "priviledge_sign_in" }, { "objectType": "TableField_MYSQL", "name": "priviledge_comment", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否有评论获奖励特权", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "priviledge_comment" }, { "objectType": "TableField_MYSQL", "name": "priviledge_promotion", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否有专享活动特权", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "priviledge_promotion" }, { "objectType": "TableField_MYSQL", "name": "priviledge_member_price", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否有会员价格特权", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "priviledge_member_price" }, { "objectType": "TableField_MYSQL", "name": "priviledge_birthday", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否有生日特权", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "priviledge_birthday" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_login_log", "comment": "会员登录记录", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_login_log", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_login_log` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `ip` varchar(64) DEFAULT NULL,\n `city` varchar(64) DEFAULT NULL,\n `login_type` int(1) DEFAULT NULL COMMENT '登录类型:0->PC;1->android;2->ios;3->小程序',\n `province` varchar(64) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_52` (`member_id`),\n CONSTRAINT `FK_Reference_52` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员登录记录'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "ip", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "ip" }, { "objectType": "TableField_MYSQL", "name": "city", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "city" }, { "objectType": "TableField_MYSQL", "name": "login_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "登录类型:0->PC;1->android;2->ios;3->小程序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "login_type" }, { "objectType": "TableField_MYSQL", "name": "province", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "province" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_52", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_52", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_52", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_52" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_member_tag_relation", "comment": "用户和标签关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_member_tag_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_member_tag_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `tag_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_53` (`member_id`),\n KEY `FK_Reference_54` (`tag_id`),\n CONSTRAINT `FK_Reference_53` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`),\n CONSTRAINT `FK_Reference_54` FOREIGN KEY (`tag_id`) REFERENCES `ums_member_tag` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户和标签关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "tag_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "tag_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_53", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_53", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_54", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_54", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "tag_id", "keyLength": 0, "order": "", "oldName": "tag_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_53", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_53" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_54", "fields": [ "tag_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member_tag", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_54" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_product_category_relation", "comment": "会员与产品分类关系表(用户喜欢的分类)", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_product_category_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_product_category_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `product_category_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_48` (`member_id`),\n KEY `FK_Reference_49` (`product_category_id`),\n CONSTRAINT `FK_Reference_48` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`),\n CONSTRAINT `FK_Reference_49` FOREIGN KEY (`product_category_id`) REFERENCES `pms_product_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员与产品分类关系表(用户喜欢的分类)'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "product_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_48", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_48", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_49", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_49", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_category_id", "keyLength": 0, "order": "", "oldName": "product_category_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_48", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_48" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_49", "fields": [ "product_category_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_49" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_receive_address", "comment": "会员收货地址表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_receive_address", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_receive_address` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `name` varchar(100) DEFAULT NULL COMMENT '收货人名称',\n `phone_number` varchar(64) DEFAULT NULL,\n `default_status` int(1) DEFAULT NULL COMMENT '是否为默认',\n `post_code` varchar(100) DEFAULT NULL COMMENT '邮政编码',\n `province` varchar(100) DEFAULT NULL COMMENT '省份/直辖市',\n `city` varchar(100) DEFAULT NULL COMMENT '城市',\n `region` varchar(100) DEFAULT NULL COMMENT '区',\n `detail_address` varchar(128) DEFAULT NULL COMMENT '详细地址(街道)',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_51` (`member_id`),\n CONSTRAINT `FK_Reference_51` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员收货地址表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货人名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "phone_number", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "phone_number" }, { "objectType": "TableField_MYSQL", "name": "default_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否为默认", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "default_status" }, { "objectType": "TableField_MYSQL", "name": "post_code", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "邮政编码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "post_code" }, { "objectType": "TableField_MYSQL", "name": "province", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "省份/直辖市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "province" }, { "objectType": "TableField_MYSQL", "name": "city", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "城市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "city" }, { "objectType": "TableField_MYSQL", "name": "region", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "区", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "region" }, { "objectType": "TableField_MYSQL", "name": "detail_address", "type": "varchar", "length": 128, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "详细地址(街道)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail_address" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_51", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_51", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_51", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_51" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_rule_setting", "comment": "会员积分成长规则表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_rule_setting", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_rule_setting` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `continue_sign_day` int(11) DEFAULT NULL COMMENT '连续签到天数',\n `continue_sign_point` int(11) DEFAULT NULL COMMENT '连续签到赠送数量',\n `consume_per_point` decimal(10,2) DEFAULT NULL COMMENT '每消费多少元获取1个点',\n `low_order_amount` decimal(10,2) DEFAULT NULL COMMENT '最低获取点数的订单金额',\n `max_point_per_order` int(11) DEFAULT NULL COMMENT '每笔订单最高获取点数',\n `type` int(1) DEFAULT NULL COMMENT '类型:0->积分规则;1->成长值规则',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员积分成长规则表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "continue_sign_day", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "连续签到天数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "continue_sign_day" }, { "objectType": "TableField_MYSQL", "name": "continue_sign_point", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "连续签到赠送数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "continue_sign_point" }, { "objectType": "TableField_MYSQL", "name": "consume_per_point", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每消费多少元获取1个点", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "consume_per_point" }, { "objectType": "TableField_MYSQL", "name": "low_order_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "最低获取点数的订单金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "low_order_amount" }, { "objectType": "TableField_MYSQL", "name": "max_point_per_order", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每笔订单最高获取点数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "max_point_per_order" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "类型:0->积分规则;1->成长值规则", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_statistics_info", "comment": "会员统计信息", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_statistics_info", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_statistics_info` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `consume_amount` decimal(10,2) DEFAULT NULL COMMENT '累计消费金额',\n `order_count` int(11) DEFAULT NULL COMMENT '订单数量',\n `coupon_count` int(11) DEFAULT NULL COMMENT '优惠券数量',\n `comment_count` int(11) DEFAULT NULL COMMENT '评价数',\n `return_order_count` int(11) DEFAULT NULL COMMENT '退货数量',\n `login_count` int(11) DEFAULT NULL COMMENT '登录次数',\n `attend_count` int(11) DEFAULT NULL COMMENT '关注数量',\n `fans_count` int(11) DEFAULT NULL COMMENT '粉丝数量',\n `collect_product_count` int(11) DEFAULT NULL,\n `collect_subject_count` int(11) DEFAULT NULL,\n `collect_topic_count` int(11) DEFAULT NULL,\n `collect_comment_count` int(11) DEFAULT NULL,\n `invite_friend_count` int(11) DEFAULT NULL,\n `recent_order_time` datetime DEFAULT NULL COMMENT '最后一次下订单时间',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_50` (`member_id`),\n CONSTRAINT `FK_Reference_50` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员统计信息'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "consume_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "累计消费金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "consume_amount" }, { "objectType": "TableField_MYSQL", "name": "order_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_count" }, { "objectType": "TableField_MYSQL", "name": "coupon_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "优惠券数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_count" }, { "objectType": "TableField_MYSQL", "name": "comment_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "评价数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "comment_count" }, { "objectType": "TableField_MYSQL", "name": "return_order_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退货数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "return_order_count" }, { "objectType": "TableField_MYSQL", "name": "login_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "登录次数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "login_count" }, { "objectType": "TableField_MYSQL", "name": "attend_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "关注数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "attend_count" }, { "objectType": "TableField_MYSQL", "name": "fans_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "粉丝数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "fans_count" }, { "objectType": "TableField_MYSQL", "name": "collect_product_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "collect_product_count" }, { "objectType": "TableField_MYSQL", "name": "collect_subject_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "collect_subject_count" }, { "objectType": "TableField_MYSQL", "name": "collect_topic_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "collect_topic_count" }, { "objectType": "TableField_MYSQL", "name": "collect_comment_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "collect_comment_count" }, { "objectType": "TableField_MYSQL", "name": "invite_friend_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "invite_friend_count" }, { "objectType": "TableField_MYSQL", "name": "recent_order_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "最后一次下订单时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recent_order_time" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_50", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_50", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_50", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_50" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_tag", "comment": "用户标签表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_tag", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_tag` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL,\n `finish_order_count` int(11) DEFAULT NULL COMMENT '自动打标签完成订单数量',\n `finish_order_amount` decimal(10,2) DEFAULT NULL COMMENT '自动打标签完成订单金额',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户标签表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "finish_order_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "自动打标签完成订单数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "finish_order_count" }, { "objectType": "TableField_MYSQL", "name": "finish_order_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "自动打标签完成订单金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "finish_order_amount" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_task", "comment": "会员任务表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_task", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_task` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL,\n `growth` int(11) DEFAULT NULL COMMENT '赠送成长值',\n `intergration` int(11) DEFAULT NULL COMMENT '赠送积分',\n `type` int(1) DEFAULT NULL COMMENT '任务类型:0->新手任务;1->日常任务',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员任务表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "growth", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "赠送成长值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "growth" }, { "objectType": "TableField_MYSQL", "name": "intergration", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "赠送积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "intergration" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "任务类型:0->新手任务;1->日常任务", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_menu", "comment": "后台菜单表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_menu", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_menu` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `parent_id` bigint(20) DEFAULT NULL COMMENT '父级ID',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `title` varchar(100) DEFAULT NULL COMMENT '菜单名称',\n `level` int(4) DEFAULT NULL COMMENT '菜单级数',\n `sort` int(4) DEFAULT NULL COMMENT '菜单排序',\n `name` varchar(100) DEFAULT NULL COMMENT '前端名称',\n `icon` varchar(200) DEFAULT NULL COMMENT '前端图标',\n `hidden` int(1) DEFAULT NULL COMMENT '前端隐藏',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_80` (`parent_id`),\n CONSTRAINT `FK_Reference_80` FOREIGN KEY (`parent_id`) REFERENCES `ums_menu` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台菜单表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "parent_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "父级ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "parent_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "title", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "菜单名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "title" }, { "objectType": "TableField_MYSQL", "name": "level", "type": "int", "length": 4, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "菜单级数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "level" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 4, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "菜单排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "前端名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "前端图标", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "hidden", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "前端隐藏", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "hidden" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_80", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_80", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "parent_id", "keyLength": 0, "order": "", "oldName": "parent_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_80", "fields": [ "parent_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_menu", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_80" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_permission", "comment": "后台用户权限表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_permission", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_permission` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `pid` bigint(20) DEFAULT NULL COMMENT '父级权限id',\n `name` varchar(100) DEFAULT NULL COMMENT '名称',\n `value` varchar(200) DEFAULT NULL COMMENT '权限值',\n `icon` varchar(500) DEFAULT NULL COMMENT '图标',\n `type` int(1) DEFAULT NULL COMMENT '权限类型:0->目录;1->菜单;2->按钮(接口绑定权限)',\n `uri` varchar(200) DEFAULT NULL COMMENT '前端资源路径',\n `status` int(1) DEFAULT NULL COMMENT '启用状态;0->禁用;1->启用',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `sort` int(11) DEFAULT NULL COMMENT '排序',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_68` (`pid`),\n CONSTRAINT `FK_Reference_68` FOREIGN KEY (`pid`) REFERENCES `ums_permission` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户权限表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "pid", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "父级权限id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pid" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "value", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "权限值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "value" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "图标", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "权限类型:0->目录;1->菜单;2->按钮(接口绑定权限)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" }, { "objectType": "TableField_MYSQL", "name": "uri", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "前端资源路径", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "uri" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "启用状态;0->禁用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_68", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_68", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "pid", "keyLength": 0, "order": "", "oldName": "pid" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_68", "fields": [ "pid" ], "referenceSchema": "mall-ref", "referenceTable": "ums_permission", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_68" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_resource", "comment": "后台资源表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_resource", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_resource` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `category_id` bigint(20) DEFAULT NULL COMMENT '资源分类ID',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `name` varchar(200) DEFAULT NULL COMMENT '资源名称',\n `url` varchar(200) DEFAULT NULL COMMENT '资源URL',\n `description` varchar(500) DEFAULT NULL COMMENT '描述',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_85` (`category_id`),\n CONSTRAINT `FK_Reference_85` FOREIGN KEY (`category_id`) REFERENCES `ums_resource_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台资源表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "资源分类ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "category_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "资源名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "url", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "资源URL", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "url" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "描述", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_85", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_85", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "category_id", "keyLength": 0, "order": "", "oldName": "category_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_85", "fields": [ "category_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_resource_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_85" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_resource_category", "comment": "资源分类表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_resource_category", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_resource_category` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `name` varchar(200) DEFAULT NULL COMMENT '分类名称',\n `sort` int(4) DEFAULT NULL COMMENT '排序',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='资源分类表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "分类名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 4, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_role", "comment": "后台用户角色表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_role", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_role` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL COMMENT '名称',\n `description` varchar(500) DEFAULT NULL COMMENT '描述',\n `admin_count` int(11) DEFAULT NULL COMMENT '后台用户数量',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `status` int(1) DEFAULT '1' COMMENT '启用状态:0->禁用;1->启用',\n `sort` int(11) DEFAULT '0',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户角色表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "描述", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" }, { "objectType": "TableField_MYSQL", "name": "admin_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "后台用户数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "admin_count" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "1", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "启用状态:0->禁用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_role_menu_relation", "comment": "后台角色菜单关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_role_menu_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_role_menu_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',\n `menu_id` bigint(20) DEFAULT NULL COMMENT '菜单ID',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_81` (`role_id`),\n KEY `FK_Reference_82` (`menu_id`),\n CONSTRAINT `FK_Reference_81` FOREIGN KEY (`role_id`) REFERENCES `ums_role` (`id`),\n CONSTRAINT `FK_Reference_82` FOREIGN KEY (`menu_id`) REFERENCES `ums_menu` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台角色菜单关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "role_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "角色ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "role_id" }, { "objectType": "TableField_MYSQL", "name": "menu_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "菜单ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "menu_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_81", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_81", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "role_id", "keyLength": 0, "order": "", "oldName": "role_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_82", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_82", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "menu_id", "keyLength": 0, "order": "", "oldName": "menu_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_81", "fields": [ "role_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_role", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_81" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_82", "fields": [ "menu_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_menu", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_82" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_role_permission_relation", "comment": "后台用户角色和权限关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_role_permission_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_role_permission_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `role_id` bigint(20) DEFAULT NULL,\n `permission_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_71` (`role_id`),\n KEY `FK_Reference_72` (`permission_id`),\n CONSTRAINT `FK_Reference_71` FOREIGN KEY (`role_id`) REFERENCES `ums_role` (`id`),\n CONSTRAINT `FK_Reference_72` FOREIGN KEY (`permission_id`) REFERENCES `ums_permission` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户角色和权限关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "role_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "role_id" }, { "objectType": "TableField_MYSQL", "name": "permission_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "permission_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_71", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_71", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "role_id", "keyLength": 0, "order": "", "oldName": "role_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_72", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_72", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "permission_id", "keyLength": 0, "order": "", "oldName": "permission_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_71", "fields": [ "role_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_role", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_71" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_72", "fields": [ "permission_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_permission", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_72" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_role_resource_relation", "comment": "后台角色资源关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_role_resource_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_role_resource_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',\n `resource_id` bigint(20) DEFAULT NULL COMMENT '资源ID',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_83` (`role_id`),\n KEY `FK_Reference_84` (`resource_id`),\n CONSTRAINT `FK_Reference_83` FOREIGN KEY (`role_id`) REFERENCES `ums_role` (`id`),\n CONSTRAINT `FK_Reference_84` FOREIGN KEY (`resource_id`) REFERENCES `ums_resource` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台角色资源关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "role_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "角色ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "role_id" }, { "objectType": "TableField_MYSQL", "name": "resource_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "资源ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "resource_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_83", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_83", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "role_id", "keyLength": 0, "order": "", "oldName": "role_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_84", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_84", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "resource_id", "keyLength": 0, "order": "", "oldName": "resource_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_83", "fields": [ "role_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_role", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_83" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_84", "fields": [ "resource_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_resource", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_84" } ], "triggers": [], "tablePartitions": [] } ], "views": [] } ] }, "diagrams": [ { "name": "mall数据库模型", "paperWidth": 5, "paperHeight": 4, "tableFont": "Arial Unicode MS", "tableFontSize": 14, "isBalckWhite": false, "showDBSchemaName": false, "showViewRelations": true, "notation": "default", "showFieldComment": false, "showTableComment": false, "shapes": [ { "type": "table", "schemaName": "mall-ref", "tableName": "cms_help", "x": 3680, "y": 4830, "width": 165, "height": 211, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "cms_help_category", "x": 3910, "y": 4850, "width": 164, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "cms_member_report", "x": 2780, "y": 4850, "width": 243, "height": 211, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "cms_prefrence_area", "x": 1130, "y": 3440, "width": 173, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "cms_prefrence_area_product_relation", "x": 780, "y": 3470, "width": 299, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "cms_subject", "x": 3590, "y": 250, "width": 205, "height": 371, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "cms_subject_category", "x": 3600, "y": 30, "width": 185, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "cms_subject_comment", "x": 3840, "y": 330, "width": 233, "height": 191, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "cms_subject_product_relation", "x": 1310, "y": 380, "width": 240, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "cms_topic", "x": 2270, "y": 4830, "width": 192, "height": 291, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "cms_topic_category", "x": 2540, "y": 4890, "width": 168, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "cms_topic_comment", "x": 1950, "y": 4880, "width": 233, "height": 191, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_cart_item", "x": 1920, "y": 3210, "width": 227, "height": 411, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_company_address", "x": 3300, "y": 480, "width": 202, "height": 251, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_order", "x": 3280, "y": 1710, "width": 250, "height": 931, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_order_item", "x": 1520, "y": 1130, "width": 235, "height": 471, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_order_operate_history", "x": 3580, "y": 2090, "width": 223, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_order_return_apply", "x": 3290, "y": 780, "width": 230, "height": 591, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_order_return_reason", "x": 4550, "y": 30, "width": 212, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_order_setting", "x": 3400, "y": 4850, "width": 213, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_album", "x": 4350, "y": 4850, "width": 187, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_album_pic", "x": 4150, "y": 4880, "width": 153, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_brand", "x": 260, "y": 3940, "width": 220, "height": 271, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_comment", "x": 520, "y": 3080, "width": 233, "height": 351, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_comment_replay", "x": 520, "y": 2840, "width": 233, "height": 191, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_feight_template", "x": 610, "y": 4570, "width": 218, "height": 211, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_member_price", "x": 2260, "y": 3580, "width": 237, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product", "x": 590, "y": 3640, "width": 262, "height": 891, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_attribute", "x": 2000, "y": 1880, "width": 262, "height": 291, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_attribute_category", "x": 1700, "y": 2040, "width": 258, "height": 131, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_attribute_value", "x": 2010, "y": 1710, "width": 234, "height": 131, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_category", "x": 2030, "y": 2430, "width": 189, "height": 291, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_category_attribute_relation", "x": 1970, "y": 2220, "width": 321, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_full_reduction", "x": 4260, "y": 4440, "width": 224, "height": 131, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_ladder", "x": 4260, "y": 4250, "width": 175, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_operate_log", "x": 3940, "y": 4110, "width": 214, "height": 291, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_vertify_record", "x": 3600, "y": 4180, "width": 226, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_sku_stock", "x": 1930, "y": 3800, "width": 218, "height": 271, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_coupon", "x": 2950, "y": 2070, "width": 183, "height": 411, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_coupon_history", "x": 3010, "y": 2540, "width": 220, "height": 271, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_coupon_product_category_relation", "x": 2270, "y": 2380, "width": 312, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_coupon_product_relation", "x": 2930, "y": 1610, "width": 241, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_flash_promotion", "x": 3320, "y": 4110, "width": 177, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_flash_promotion_log", "x": 1820, "y": 2970, "width": 207, "height": 191, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_flash_promotion_product_relation", "x": 2970, "y": 4100, "width": 303, "height": 211, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_flash_promotion_session", "x": 3000, "y": 3880, "width": 240, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_home_advertise", "x": 1700, "y": 4850, "width": 176, "height": 291, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_home_brand", "x": 30, "y": 4010, "width": 183, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_home_new_product", "x": 2670, "y": 4110, "width": 201, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_home_recommend_product", "x": 2310, "y": 4020, "width": 255, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_home_recommend_subject", "x": 3570, "y": 670, "width": 252, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_admin", "x": 260, "y": 5590, "width": 180, "height": 251, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_admin_login_log", "x": 30, "y": 5630, "width": 181, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_admin_permission_relation", "x": 220, "y": 5410, "width": 255, "height": 131, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_admin_role_relation", "x": 990, "y": 5660, "width": 205, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_growth_change_history", "x": 2810, "y": 3470, "width": 230, "height": 211, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_integration_change_history", "x": 3090, "y": 3620, "width": 259, "height": 211, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_integration_consume_setting", "x": 4600, "y": 4850, "width": 270, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member", "x": 3090, "y": 3150, "width": 248, "height": 431, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_level", "x": 2540, "y": 3370, "width": 223, "height": 311, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_login_log", "x": 3450, "y": 3450, "width": 193, "height": 191, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_member_tag_relation", "x": 3840, "y": 3350, "width": 279, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_product_category_relation", "x": 1980, "y": 2800, "width": 317, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_receive_address", "x": 3670, "y": 3000, "width": 247, "height": 251, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_rule_setting", "x": 3090, "y": 4850, "width": 237, "height": 191, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_statistics_info", "x": 3390, "y": 2780, "width": 228, "height": 371, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_tag", "x": 4170, "y": 3330, "width": 241, "height": 131, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_task", "x": 4830, "y": 30, "width": 158, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_menu", "x": 1470, "y": 5360, "width": 162, "height": 231, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_permission", "x": 510, "y": 5360, "width": 162, "height": 251, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_resource", "x": 990, "y": 5020, "width": 180, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_resource_category", "x": 980, "y": 4850, "width": 198, "height": 131, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_role", "x": 990, "y": 5380, "width": 180, "height": 191, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_role_menu_relation", "x": 1220, "y": 5430, "width": 201, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_role_permission_relation", "x": 710, "y": 5430, "width": 240, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_role_resource_relation", "x": 970, "y": 5230, "width": 226, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } } ], "layers": [], "relations": [ { "name": "FK_Reference_32", "sourceTableName": "cms_help", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3860, "y": 4930 }, { "x": 3895, "y": 4930 } ], "label": { "x": 3856, "y": 4932, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_18", "sourceTableName": "cms_prefrence_area_product_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1094, "y": 3520 }, { "x": 1115, "y": 3520 } ], "label": { "x": 1090, "y": 3522, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_28", "sourceTableName": "cms_subject", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3690, "y": 235 }, { "x": 3690, "y": 216 } ], "label": { "x": 3698, "y": 245, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_29", "sourceTableName": "cms_subject_comment", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3825, "y": 430 }, { "x": 3810, "y": 430 } ], "label": { "x": 3841, "y": 432, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_26", "sourceTableName": "cms_subject_product_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1565, "y": 430 }, { "x": 3575, "y": 430 } ], "label": { "x": 1561, "y": 432, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_31", "sourceTableName": "cms_topic", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2477, "y": 4970 }, { "x": 2525, "y": 4970 } ], "label": { "x": 2473, "y": 4972, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_30", "sourceTableName": "cms_topic_comment", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2198, "y": 4970 }, { "x": 2255, "y": 4970 } ], "label": { "x": 2194, "y": 4972, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_58", "sourceTableName": "oms_order_item", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1770, "y": 1550 }, { "x": 3220, "y": 1550 }, { "x": 3220, "y": 1760 }, { "x": 3265, "y": 1760 } ], "label": { "x": 1766, "y": 1552, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_62", "sourceTableName": "oms_order_operate_history", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3565, "y": 2180 }, { "x": 3545, "y": 2180 } ], "label": { "x": 3581, "y": 2182, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_63", "sourceTableName": "oms_order_return_apply", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3400, "y": 1386 }, { "x": 3400, "y": 1695 } ], "label": { "x": 3408, "y": 1376, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_64", "sourceTableName": "oms_order_return_apply", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3400, "y": 765 }, { "x": 3400, "y": 746 } ], "label": { "x": 3408, "y": 775, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_25", "sourceTableName": "pms_album_pic", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 4318, "y": 4930 }, { "x": 4335, "y": 4930 } ], "label": { "x": 4314, "y": 4932, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_24", "sourceTableName": "pms_comment_replay", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 640, "y": 3046 }, { "x": 640, "y": 3065 } ], "label": { "x": 648, "y": 3036, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_19", "sourceTableName": "cms_prefrence_area_product_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 765, "y": 3520 }, { "x": 730, "y": 3520 }, { "x": 730, "y": 3625 } ], "label": { "x": 781, "y": 3522, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_27", "sourceTableName": "cms_subject_product_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1360, "y": 506 }, { "x": 1360, "y": 3660 }, { "x": 867, "y": 3660 } ], "label": { "x": 1368, "y": 496, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_65", "sourceTableName": "oms_cart_item", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1905, "y": 3570 }, { "x": 1820, "y": 3570 }, { "x": 1820, "y": 4030 }, { "x": 867, "y": 4030 } ], "label": { "x": 1921, "y": 3557, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_59", "sourceTableName": "oms_order_item", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1505, "y": 1550 }, { "x": 1470, "y": 1550 }, { "x": 1470, "y": 3760 }, { "x": 867, "y": 3760 } ], "label": { "x": 1521, "y": 1537, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_75", "sourceTableName": "oms_order_return_apply", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3275, "y": 1080 }, { "x": 1410, "y": 1080 }, { "x": 1410, "y": 3710 }, { "x": 867, "y": 3710 } ], "label": { "x": 3291, "y": 1067, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_23", "sourceTableName": "pms_comment", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 640, "y": 3446 }, { "x": 640, "y": 3625 } ], "label": { "x": 648, "y": 3436, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_9", "sourceTableName": "pms_member_price", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2245, "y": 3670 }, { "x": 2200, "y": 3670 }, { "x": 2200, "y": 4130 }, { "x": 867, "y": 4130 } ], "label": { "x": 2261, "y": 3672, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_1", "sourceTableName": "pms_product", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 575, "y": 4080 }, { "x": 495, "y": 4080 } ], "label": { "x": 591, "y": 4082, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_6", "sourceTableName": "pms_product", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 720, "y": 4546 }, { "x": 720, "y": 4555 } ], "label": { "x": 728, "y": 4536, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_13", "sourceTableName": "pms_product", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 867, "y": 3900 }, { "x": 1650, "y": 3900 }, { "x": 1650, "y": 2120 }, { "x": 1685, "y": 2120 } ], "label": { "x": 863, "y": 3887, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_12", "sourceTableName": "pms_product_attribute", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1985, "y": 2110 }, { "x": 1973, "y": 2110 } ], "label": { "x": 2001, "y": 2112, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_14", "sourceTableName": "pms_product_attribute_value", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1995, "y": 1790 }, { "x": 1590, "y": 1790 }, { "x": 1590, "y": 3850 }, { "x": 867, "y": 3850 } ], "label": { "x": 2011, "y": 1792, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_15", "sourceTableName": "pms_product_attribute_value", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2130, "y": 1856 }, { "x": 2130, "y": 1865 } ], "label": { "x": 2138, "y": 1846, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_5", "sourceTableName": "pms_product", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 867, "y": 3940 }, { "x": 1700, "y": 3940 }, { "x": 1700, "y": 2670 }, { "x": 2015, "y": 2670 } ], "label": { "x": 863, "y": 3927, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_20", "sourceTableName": "pms_product_category", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2234, "y": 2690 }, { "x": 2260, "y": 2690 }, { "x": 2260, "y": 2750 }, { "x": 2200, "y": 2750 }, { "x": 2200, "y": 2736 } ], "label": { "x": 2224, "y": 2642, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_21", "sourceTableName": "pms_product_category_attribute_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2130, "y": 2346 }, { "x": 2130, "y": 2415 } ], "label": { "x": 2138, "y": 2336, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_22", "sourceTableName": "pms_product_category_attribute_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2130, "y": 2205 }, { "x": 2130, "y": 2186 } ], "label": { "x": 2138, "y": 2215, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_11", "sourceTableName": "pms_product_full_reduction", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 4245, "y": 4500 }, { "x": 867, "y": 4500 } ], "label": { "x": 4261, "y": 4502, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_10", "sourceTableName": "pms_product_ladder", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 4245, "y": 4350 }, { "x": 4210, "y": 4350 }, { "x": 4210, "y": 4450 }, { "x": 867, "y": 4450 } ], "label": { "x": 4261, "y": 4352, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_4", "sourceTableName": "pms_product_operate_log", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3925, "y": 4350 }, { "x": 3880, "y": 4350 }, { "x": 3880, "y": 4410 }, { "x": 867, "y": 4410 } ], "label": { "x": 3941, "y": 4352, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_3", "sourceTableName": "pms_product_vertify_record", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3585, "y": 4300 }, { "x": 3550, "y": 4300 }, { "x": 3550, "y": 4360 }, { "x": 867, "y": 4360 } ], "label": { "x": 3601, "y": 4302, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_66", "sourceTableName": "oms_cart_item", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2040, "y": 3636 }, { "x": 2040, "y": 3785 } ], "label": { "x": 2048, "y": 3611, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_2", "sourceTableName": "pms_sku_stock", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1915, "y": 4020 }, { "x": 1880, "y": 4020 }, { "x": 1880, "y": 4080 }, { "x": 867, "y": 4080 } ], "label": { "x": 1931, "y": 4022, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_61", "sourceTableName": "oms_order", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3265, "y": 2280 }, { "x": 3148, "y": 2280 } ], "label": { "x": 3281, "y": 2267, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_37", "sourceTableName": "sms_coupon_history", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3080, "y": 2525 }, { "x": 3080, "y": 2496 } ], "label": { "x": 3088, "y": 2535, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_76", "sourceTableName": "sms_coupon_history", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3245, "y": 2590 }, { "x": 3265, "y": 2590 } ], "label": { "x": 3241, "y": 2592, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_35", "sourceTableName": "sms_coupon_product_category_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2597, "y": 2430 }, { "x": 2935, "y": 2430 } ], "label": { "x": 2593, "y": 2432, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_36", "sourceTableName": "sms_coupon_product_category_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2255, "y": 2480 }, { "x": 2234, "y": 2480 } ], "label": { "x": 2271, "y": 2482, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_33", "sourceTableName": "sms_coupon_product_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3050, "y": 1776 }, { "x": 3050, "y": 2055 } ], "label": { "x": 3058, "y": 1766, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_34", "sourceTableName": "sms_coupon_product_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2915, "y": 1660 }, { "x": 1530, "y": 1660 }, { "x": 1530, "y": 3800 }, { "x": 867, "y": 3800 } ], "label": { "x": 2931, "y": 1662, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_45", "sourceTableName": "sms_flash_promotion_log", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1805, "y": 3110 }, { "x": 1760, "y": 3110 }, { "x": 1760, "y": 3990 }, { "x": 867, "y": 3990 } ], "label": { "x": 1821, "y": 3112, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_77", "sourceTableName": "sms_flash_promotion_product_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3288, "y": 4200 }, { "x": 3305, "y": 4200 } ], "label": { "x": 3284, "y": 4202, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_79", "sourceTableName": "sms_flash_promotion_product_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2955, "y": 4250 }, { "x": 2920, "y": 4250 }, { "x": 2920, "y": 4310 }, { "x": 867, "y": 4310 } ], "label": { "x": 2971, "y": 4252, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_78", "sourceTableName": "sms_flash_promotion_product_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3120, "y": 4085 }, { "x": 3120, "y": 4066 } ], "label": { "x": 3128, "y": 4095, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_39", "sourceTableName": "sms_home_brand", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 228, "y": 4080 }, { "x": 245, "y": 4080 } ], "label": { "x": 224, "y": 4082, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_40", "sourceTableName": "sms_home_new_product", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2655, "y": 4210 }, { "x": 2620, "y": 4210 }, { "x": 2620, "y": 4220 }, { "x": 867, "y": 4220 } ], "label": { "x": 2671, "y": 4212, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_41", "sourceTableName": "sms_home_recommend_product", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2295, "y": 4120 }, { "x": 2260, "y": 4120 }, { "x": 2260, "y": 4170 }, { "x": 867, "y": 4170 } ], "label": { "x": 2311, "y": 4122, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_42", "sourceTableName": "sms_home_recommend_subject", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3690, "y": 655 }, { "x": 3690, "y": 636 } ], "label": { "x": 3698, "y": 665, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_46", "sourceTableName": "ums_admin_login_log", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 226, "y": 5710 }, { "x": 245, "y": 5710 } ], "label": { "x": 222, "y": 5712, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_73", "sourceTableName": "ums_admin_permission_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 360, "y": 5556 }, { "x": 360, "y": 5575 } ], "label": { "x": 368, "y": 5546, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_69", "sourceTableName": "ums_admin_role_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 975, "y": 5710 }, { "x": 455, "y": 5710 } ], "label": { "x": 991, "y": 5712, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_67", "sourceTableName": "oms_cart_item", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2162, "y": 3320 }, { "x": 3075, "y": 3320 } ], "label": { "x": 2158, "y": 3307, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_57", "sourceTableName": "oms_order", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3330, "y": 2656 }, { "x": 3330, "y": 2690 }, { "x": 3320, "y": 2690 }, { "x": 3320, "y": 3135 } ], "label": { "x": 3338, "y": 2631, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_38", "sourceTableName": "sms_coupon_history", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3180, "y": 2826 }, { "x": 3180, "y": 3135 } ], "label": { "x": 3188, "y": 2816, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_44", "sourceTableName": "sms_flash_promotion_log", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2042, "y": 3110 }, { "x": 3040, "y": 3110 }, { "x": 3040, "y": 3200 }, { "x": 3075, "y": 3200 } ], "label": { "x": 2038, "y": 3112, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_56", "sourceTableName": "ums_growth_change_history", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3055, "y": 3520 }, { "x": 3075, "y": 3520 } ], "label": { "x": 3051, "y": 3522, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_55", "sourceTableName": "ums_integration_change_history", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3210, "y": 3605 }, { "x": 3210, "y": 3596 } ], "label": { "x": 3218, "y": 3615, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_60", "sourceTableName": "pms_member_price", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2512, "y": 3630 }, { "x": 2525, "y": 3630 } ], "label": { "x": 2508, "y": 3632, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_47", "sourceTableName": "ums_member", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3075, "y": 3420 }, { "x": 2778, "y": 3420 } ], "label": { "x": 3091, "y": 3407, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_52", "sourceTableName": "ums_member_login_log", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3435, "y": 3520 }, { "x": 3353, "y": 3520 } ], "label": { "x": 3451, "y": 3522, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_53", "sourceTableName": "ums_member_member_tag_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3825, "y": 3400 }, { "x": 3353, "y": 3400 } ], "label": { "x": 3841, "y": 3402, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_48", "sourceTableName": "ums_member_product_category_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2312, "y": 2860 }, { "x": 3120, "y": 2860 }, { "x": 3120, "y": 3135 } ], "label": { "x": 2308, "y": 2862, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_49", "sourceTableName": "ums_member_product_category_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 2130, "y": 2785 }, { "x": 2130, "y": 2736 } ], "label": { "x": 2138, "y": 2795, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_51", "sourceTableName": "ums_member_receive_address", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3790, "y": 3266 }, { "x": 3790, "y": 3300 }, { "x": 3353, "y": 3300 } ], "label": { "x": 3798, "y": 3256, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_50", "sourceTableName": "ums_member_statistics_info", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 3500, "y": 3166 }, { "x": 3500, "y": 3200 }, { "x": 3353, "y": 3200 } ], "label": { "x": 3508, "y": 3156, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_54", "sourceTableName": "ums_member_member_tag_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 4134, "y": 3400 }, { "x": 4155, "y": 3400 } ], "label": { "x": 4130, "y": 3402, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_80", "sourceTableName": "ums_menu", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1647, "y": 5570 }, { "x": 1670, "y": 5570 }, { "x": 1670, "y": 5630 }, { "x": 1610, "y": 5630 }, { "x": 1610, "y": 5606 } ], "label": { "x": 1637, "y": 5522, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_74", "sourceTableName": "ums_admin_permission_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 490, "y": 5490 }, { "x": 495, "y": 5490 } ], "label": { "x": 486, "y": 5492, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_68", "sourceTableName": "ums_permission", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 687, "y": 5590 }, { "x": 710, "y": 5590 }, { "x": 710, "y": 5650 }, { "x": 650, "y": 5650 }, { "x": 650, "y": 5626 } ], "label": { "x": 677, "y": 5542, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_85", "sourceTableName": "ums_resource", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1100, "y": 5005 }, { "x": 1100, "y": 4996 } ], "label": { "x": 1108, "y": 5015, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_70", "sourceTableName": "ums_admin_role_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1100, "y": 5645 }, { "x": 1100, "y": 5586 } ], "label": { "x": 1108, "y": 5655, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_81", "sourceTableName": "ums_role_menu_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1205, "y": 5490 }, { "x": 1185, "y": 5490 } ], "label": { "x": 1221, "y": 5492, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_82", "sourceTableName": "ums_role_menu_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1436, "y": 5490 }, { "x": 1455, "y": 5490 } ], "label": { "x": 1432, "y": 5492, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_71", "sourceTableName": "ums_role_permission_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 965, "y": 5490 }, { "x": 975, "y": 5490 } ], "label": { "x": 961, "y": 5492, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_72", "sourceTableName": "ums_role_permission_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 695, "y": 5490 }, { "x": 687, "y": 5490 } ], "label": { "x": 711, "y": 5492, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_83", "sourceTableName": "ums_role_resource_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1100, "y": 5356 }, { "x": 1100, "y": 5365 } ], "label": { "x": 1108, "y": 5346, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_84", "sourceTableName": "ums_role_resource_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1100, "y": 5215 }, { "x": 1100, "y": 5206 } ], "label": { "x": 1108, "y": 5225, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } } ], "viewRelations": [] } ] } ================================================ FILE: document/navicat/会员模块数据库模型.ndm2 ================================================ { "paper": { "name": "A4", "leftMargin": 0.5, "rightMargin": 0.5, "topMargin": 0.5, "bottomMargin": 0.5, "isPortriat": true }, "modelVersion": 2.01, "defaultSchema": "Default", "server": { "objectType": "Server_MYSQL", "name": "Default", "serverVersion": 50799, "edition": "Default", "lowerCaseTableNames": 0, "schemas": [ { "objectType": "Schema_MYSQL", "name": "Default", "tables": [], "views": [] }, { "objectType": "Schema_MYSQL", "name": "mall-ref", "tables": [ { "objectType": "Table_MYSQL", "name": "ums_admin", "comment": "后台用户表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_admin", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_admin` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `username` varchar(64) DEFAULT NULL COMMENT '用户名',\n `password` varchar(64) DEFAULT NULL COMMENT '密码',\n `icon` varchar(500) DEFAULT NULL COMMENT '头像',\n `email` varchar(100) DEFAULT NULL COMMENT '邮箱',\n `nick_name` varchar(200) DEFAULT NULL COMMENT '昵称',\n `note` varchar(500) DEFAULT NULL COMMENT '备注信息',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `login_time` datetime DEFAULT NULL COMMENT '最后登录时间',\n `status` int(1) DEFAULT '1' COMMENT '帐号启用状态:0->禁用;1->启用',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "username", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "用户名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "username" }, { "objectType": "TableField_MYSQL", "name": "password", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "密码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "password" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "头像", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "email", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "邮箱", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "email" }, { "objectType": "TableField_MYSQL", "name": "nick_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "昵称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "nick_name" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "备注信息", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "login_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "最后登录时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "login_time" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "1", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "帐号启用状态:0->禁用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_admin_login_log", "comment": "后台用户登录日志表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_admin_login_log", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_admin_login_log` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `admin_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `ip` varchar(64) DEFAULT NULL,\n `address` varchar(100) DEFAULT NULL,\n `user_agent` varchar(100) DEFAULT NULL COMMENT '浏览器登录类型',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_46` (`admin_id`),\n CONSTRAINT `FK_Reference_46` FOREIGN KEY (`admin_id`) REFERENCES `ums_admin` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户登录日志表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "admin_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "admin_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "ip", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "ip" }, { "objectType": "TableField_MYSQL", "name": "address", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "address" }, { "objectType": "TableField_MYSQL", "name": "user_agent", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "浏览器登录类型", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "user_agent" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_46", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_46", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "admin_id", "keyLength": 0, "order": "", "oldName": "admin_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_46", "fields": [ "admin_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_admin", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_46" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_admin_permission_relation", "comment": "后台用户和权限关系表(除角色中定义的权限以外的加减权限)", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_admin_permission_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_admin_permission_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `admin_id` bigint(20) DEFAULT NULL,\n `permission_id` bigint(20) DEFAULT NULL,\n `type` int(1) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_73` (`admin_id`),\n KEY `FK_Reference_74` (`permission_id`),\n CONSTRAINT `FK_Reference_73` FOREIGN KEY (`admin_id`) REFERENCES `ums_admin` (`id`),\n CONSTRAINT `FK_Reference_74` FOREIGN KEY (`permission_id`) REFERENCES `ums_permission` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户和权限关系表(除角色中定义的权限以外的加减权限)'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "admin_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "admin_id" }, { "objectType": "TableField_MYSQL", "name": "permission_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "permission_id" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_73", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_73", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "admin_id", "keyLength": 0, "order": "", "oldName": "admin_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_74", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_74", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "permission_id", "keyLength": 0, "order": "", "oldName": "permission_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_73", "fields": [ "admin_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_admin", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_73" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_74", "fields": [ "permission_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_permission", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_74" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_admin_role_relation", "comment": "后台用户和角色关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_admin_role_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_admin_role_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `admin_id` bigint(20) DEFAULT NULL,\n `role_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_69` (`admin_id`),\n KEY `FK_Reference_70` (`role_id`),\n CONSTRAINT `FK_Reference_69` FOREIGN KEY (`admin_id`) REFERENCES `ums_admin` (`id`),\n CONSTRAINT `FK_Reference_70` FOREIGN KEY (`role_id`) REFERENCES `ums_role` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户和角色关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "admin_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "admin_id" }, { "objectType": "TableField_MYSQL", "name": "role_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "role_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_69", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_69", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "admin_id", "keyLength": 0, "order": "", "oldName": "admin_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_70", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_70", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "role_id", "keyLength": 0, "order": "", "oldName": "role_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_69", "fields": [ "admin_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_admin", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_69" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_70", "fields": [ "role_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_role", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_70" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_growth_change_history", "comment": "成长值变化历史记录表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_growth_change_history", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_growth_change_history` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `change_type` int(1) DEFAULT NULL COMMENT '改变类型:0->增加;1->减少',\n `change_count` int(11) DEFAULT NULL COMMENT '积分改变数量',\n `operate_man` varchar(100) DEFAULT NULL COMMENT '操作人员',\n `operate_note` varchar(200) DEFAULT NULL COMMENT '操作备注',\n `source_type` int(1) DEFAULT NULL COMMENT '积分来源:0->购物;1->管理员修改',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_56` (`member_id`),\n CONSTRAINT `FK_Reference_56` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='成长值变化历史记录表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "change_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "改变类型:0->增加;1->减少", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "change_type" }, { "objectType": "TableField_MYSQL", "name": "change_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分改变数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "change_count" }, { "objectType": "TableField_MYSQL", "name": "operate_man", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作人员", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_man" }, { "objectType": "TableField_MYSQL", "name": "operate_note", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_note" }, { "objectType": "TableField_MYSQL", "name": "source_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分来源:0->购物;1->管理员修改", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "source_type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_56", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_56", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_56", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_56" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_integration_change_history", "comment": "积分变化历史记录表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_integration_change_history", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_integration_change_history` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `change_type` int(1) DEFAULT NULL COMMENT '改变类型:0->增加;1->减少',\n `change_count` int(11) DEFAULT NULL COMMENT '积分改变数量',\n `operate_man` varchar(100) DEFAULT NULL COMMENT '操作人员',\n `operate_note` varchar(200) DEFAULT NULL COMMENT '操作备注',\n `source_type` int(1) DEFAULT NULL COMMENT '积分来源:0->购物;1->管理员修改',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_55` (`member_id`),\n CONSTRAINT `FK_Reference_55` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='积分变化历史记录表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "change_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "改变类型:0->增加;1->减少", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "change_type" }, { "objectType": "TableField_MYSQL", "name": "change_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分改变数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "change_count" }, { "objectType": "TableField_MYSQL", "name": "operate_man", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作人员", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_man" }, { "objectType": "TableField_MYSQL", "name": "operate_note", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_note" }, { "objectType": "TableField_MYSQL", "name": "source_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分来源:0->购物;1->管理员修改", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "source_type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_55", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_55", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_55", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_55" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_integration_consume_setting", "comment": "积分消费设置", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_integration_consume_setting", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_integration_consume_setting` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `deduction_per_amount` int(11) DEFAULT NULL COMMENT '每一元需要抵扣的积分数量',\n `max_percent_per_order` int(11) DEFAULT NULL COMMENT '每笔订单最高抵用百分比',\n `use_unit` int(11) DEFAULT NULL COMMENT '每次使用积分最小单位100',\n `coupon_status` int(1) DEFAULT NULL COMMENT '是否可以和优惠券同用;0->不可以;1->可以',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='积分消费设置'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "deduction_per_amount", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每一元需要抵扣的积分数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "deduction_per_amount" }, { "objectType": "TableField_MYSQL", "name": "max_percent_per_order", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每笔订单最高抵用百分比", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "max_percent_per_order" }, { "objectType": "TableField_MYSQL", "name": "use_unit", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每次使用积分最小单位100", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_unit" }, { "objectType": "TableField_MYSQL", "name": "coupon_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否可以和优惠券同用;0->不可以;1->可以", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_status" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member", "comment": "会员表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_level_id` bigint(20) DEFAULT NULL,\n `username` varchar(64) DEFAULT NULL COMMENT '用户名',\n `password` varchar(64) DEFAULT NULL COMMENT '密码',\n `nickname` varchar(64) DEFAULT NULL COMMENT '昵称',\n `phone` varchar(64) DEFAULT NULL COMMENT '手机号码',\n `status` int(1) DEFAULT NULL COMMENT '帐号启用状态:0->禁用;1->启用',\n `create_time` datetime DEFAULT NULL COMMENT '注册时间',\n `icon` varchar(500) DEFAULT NULL COMMENT '头像',\n `gender` int(1) DEFAULT NULL COMMENT '性别:0->未知;1->男;2->女',\n `birthday` date DEFAULT NULL COMMENT '生日',\n `city` varchar(64) DEFAULT NULL COMMENT '所做城市',\n `job` varchar(100) DEFAULT NULL COMMENT '职业',\n `personalized_signature` varchar(200) DEFAULT NULL COMMENT '个性签名',\n `source_type` int(1) DEFAULT NULL COMMENT '用户来源',\n `integration` int(11) DEFAULT NULL COMMENT '积分',\n `growth` int(11) DEFAULT NULL COMMENT '成长值',\n `luckey_count` int(11) DEFAULT NULL COMMENT '剩余抽奖次数',\n `history_integration` int(11) DEFAULT NULL COMMENT '历史积分数量',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_47` (`member_level_id`),\n CONSTRAINT `FK_Reference_47` FOREIGN KEY (`member_level_id`) REFERENCES `ums_member_level` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_level_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_level_id" }, { "objectType": "TableField_MYSQL", "name": "username", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "用户名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "username" }, { "objectType": "TableField_MYSQL", "name": "password", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "密码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "password" }, { "objectType": "TableField_MYSQL", "name": "nickname", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "昵称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "nickname" }, { "objectType": "TableField_MYSQL", "name": "phone", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "手机号码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "phone" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "帐号启用状态:0->禁用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "注册时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "头像", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "gender", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "性别:0->未知;1->男;2->女", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gender" }, { "objectType": "TableField_MYSQL", "name": "birthday", "type": "date", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "生日", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "birthday" }, { "objectType": "TableField_MYSQL", "name": "city", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "所做城市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "city" }, { "objectType": "TableField_MYSQL", "name": "job", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "职业", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "job" }, { "objectType": "TableField_MYSQL", "name": "personalized_signature", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "个性签名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "personalized_signature" }, { "objectType": "TableField_MYSQL", "name": "source_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "用户来源", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "source_type" }, { "objectType": "TableField_MYSQL", "name": "integration", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "integration" }, { "objectType": "TableField_MYSQL", "name": "growth", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "成长值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "growth" }, { "objectType": "TableField_MYSQL", "name": "luckey_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "剩余抽奖次数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "luckey_count" }, { "objectType": "TableField_MYSQL", "name": "history_integration", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "历史积分数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "history_integration" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_47", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_47", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_level_id", "keyLength": 0, "order": "", "oldName": "member_level_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_47", "fields": [ "member_level_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member_level", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_47" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_level", "comment": "会员等级表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_level", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_level` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL,\n `growth_point` int(11) DEFAULT NULL,\n `default_status` int(1) DEFAULT NULL COMMENT '是否为默认等级:0->不是;1->是',\n `free_freight_point` decimal(10,2) DEFAULT NULL COMMENT '免运费标准',\n `comment_growth_point` int(11) DEFAULT NULL COMMENT '每次评价获取的成长值',\n `priviledge_free_freight` int(1) DEFAULT NULL COMMENT '是否有免邮特权',\n `priviledge_sign_in` int(1) DEFAULT NULL COMMENT '是否有签到特权',\n `priviledge_comment` int(1) DEFAULT NULL COMMENT '是否有评论获奖励特权',\n `priviledge_promotion` int(1) DEFAULT NULL COMMENT '是否有专享活动特权',\n `priviledge_member_price` int(1) DEFAULT NULL COMMENT '是否有会员价格特权',\n `priviledge_birthday` int(1) DEFAULT NULL COMMENT '是否有生日特权',\n `note` varchar(200) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员等级表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "growth_point", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "growth_point" }, { "objectType": "TableField_MYSQL", "name": "default_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否为默认等级:0->不是;1->是", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "default_status" }, { "objectType": "TableField_MYSQL", "name": "free_freight_point", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "免运费标准", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "free_freight_point" }, { "objectType": "TableField_MYSQL", "name": "comment_growth_point", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每次评价获取的成长值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "comment_growth_point" }, { "objectType": "TableField_MYSQL", "name": "priviledge_free_freight", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否有免邮特权", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "priviledge_free_freight" }, { "objectType": "TableField_MYSQL", "name": "priviledge_sign_in", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否有签到特权", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "priviledge_sign_in" }, { "objectType": "TableField_MYSQL", "name": "priviledge_comment", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否有评论获奖励特权", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "priviledge_comment" }, { "objectType": "TableField_MYSQL", "name": "priviledge_promotion", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否有专享活动特权", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "priviledge_promotion" }, { "objectType": "TableField_MYSQL", "name": "priviledge_member_price", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否有会员价格特权", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "priviledge_member_price" }, { "objectType": "TableField_MYSQL", "name": "priviledge_birthday", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否有生日特权", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "priviledge_birthday" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_login_log", "comment": "会员登录记录", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_login_log", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_login_log` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `ip` varchar(64) DEFAULT NULL,\n `city` varchar(64) DEFAULT NULL,\n `login_type` int(1) DEFAULT NULL COMMENT '登录类型:0->PC;1->android;2->ios;3->小程序',\n `province` varchar(64) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_52` (`member_id`),\n CONSTRAINT `FK_Reference_52` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员登录记录'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "ip", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "ip" }, { "objectType": "TableField_MYSQL", "name": "city", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "city" }, { "objectType": "TableField_MYSQL", "name": "login_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "登录类型:0->PC;1->android;2->ios;3->小程序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "login_type" }, { "objectType": "TableField_MYSQL", "name": "province", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "province" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_52", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_52", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_52", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_52" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_member_tag_relation", "comment": "用户和标签关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_member_tag_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_member_tag_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `tag_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_53` (`member_id`),\n KEY `FK_Reference_54` (`tag_id`),\n CONSTRAINT `FK_Reference_53` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`),\n CONSTRAINT `FK_Reference_54` FOREIGN KEY (`tag_id`) REFERENCES `ums_member_tag` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户和标签关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "tag_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "tag_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_53", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_53", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_54", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_54", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "tag_id", "keyLength": 0, "order": "", "oldName": "tag_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_53", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_53" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_54", "fields": [ "tag_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member_tag", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_54" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_product_category_relation", "comment": "会员与产品分类关系表(用户喜欢的分类)", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_product_category_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_product_category_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `product_category_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_48` (`member_id`),\n KEY `FK_Reference_49` (`product_category_id`),\n CONSTRAINT `FK_Reference_48` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`),\n CONSTRAINT `FK_Reference_49` FOREIGN KEY (`product_category_id`) REFERENCES `pms_product_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员与产品分类关系表(用户喜欢的分类)'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "product_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_48", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_48", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_49", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_49", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_category_id", "keyLength": 0, "order": "", "oldName": "product_category_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_48", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_48" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_49", "fields": [ "product_category_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_49" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_receive_address", "comment": "会员收货地址表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_receive_address", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_receive_address` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `name` varchar(100) DEFAULT NULL COMMENT '收货人名称',\n `phone_number` varchar(64) DEFAULT NULL,\n `default_status` int(1) DEFAULT NULL COMMENT '是否为默认',\n `post_code` varchar(100) DEFAULT NULL COMMENT '邮政编码',\n `province` varchar(100) DEFAULT NULL COMMENT '省份/直辖市',\n `city` varchar(100) DEFAULT NULL COMMENT '城市',\n `region` varchar(100) DEFAULT NULL COMMENT '区',\n `detail_address` varchar(128) DEFAULT NULL COMMENT '详细地址(街道)',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_51` (`member_id`),\n CONSTRAINT `FK_Reference_51` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员收货地址表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货人名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "phone_number", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "phone_number" }, { "objectType": "TableField_MYSQL", "name": "default_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否为默认", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "default_status" }, { "objectType": "TableField_MYSQL", "name": "post_code", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "邮政编码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "post_code" }, { "objectType": "TableField_MYSQL", "name": "province", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "省份/直辖市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "province" }, { "objectType": "TableField_MYSQL", "name": "city", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "城市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "city" }, { "objectType": "TableField_MYSQL", "name": "region", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "区", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "region" }, { "objectType": "TableField_MYSQL", "name": "detail_address", "type": "varchar", "length": 128, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "详细地址(街道)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail_address" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_51", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_51", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_51", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_51" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_rule_setting", "comment": "会员积分成长规则表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_rule_setting", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_rule_setting` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `continue_sign_day` int(11) DEFAULT NULL COMMENT '连续签到天数',\n `continue_sign_point` int(11) DEFAULT NULL COMMENT '连续签到赠送数量',\n `consume_per_point` decimal(10,2) DEFAULT NULL COMMENT '每消费多少元获取1个点',\n `low_order_amount` decimal(10,2) DEFAULT NULL COMMENT '最低获取点数的订单金额',\n `max_point_per_order` int(11) DEFAULT NULL COMMENT '每笔订单最高获取点数',\n `type` int(1) DEFAULT NULL COMMENT '类型:0->积分规则;1->成长值规则',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员积分成长规则表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "continue_sign_day", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "连续签到天数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "continue_sign_day" }, { "objectType": "TableField_MYSQL", "name": "continue_sign_point", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "连续签到赠送数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "continue_sign_point" }, { "objectType": "TableField_MYSQL", "name": "consume_per_point", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每消费多少元获取1个点", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "consume_per_point" }, { "objectType": "TableField_MYSQL", "name": "low_order_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "最低获取点数的订单金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "low_order_amount" }, { "objectType": "TableField_MYSQL", "name": "max_point_per_order", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每笔订单最高获取点数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "max_point_per_order" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "类型:0->积分规则;1->成长值规则", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_statistics_info", "comment": "会员统计信息", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_statistics_info", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_statistics_info` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `consume_amount` decimal(10,2) DEFAULT NULL COMMENT '累计消费金额',\n `order_count` int(11) DEFAULT NULL COMMENT '订单数量',\n `coupon_count` int(11) DEFAULT NULL COMMENT '优惠券数量',\n `comment_count` int(11) DEFAULT NULL COMMENT '评价数',\n `return_order_count` int(11) DEFAULT NULL COMMENT '退货数量',\n `login_count` int(11) DEFAULT NULL COMMENT '登录次数',\n `attend_count` int(11) DEFAULT NULL COMMENT '关注数量',\n `fans_count` int(11) DEFAULT NULL COMMENT '粉丝数量',\n `collect_product_count` int(11) DEFAULT NULL,\n `collect_subject_count` int(11) DEFAULT NULL,\n `collect_topic_count` int(11) DEFAULT NULL,\n `collect_comment_count` int(11) DEFAULT NULL,\n `invite_friend_count` int(11) DEFAULT NULL,\n `recent_order_time` datetime DEFAULT NULL COMMENT '最后一次下订单时间',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_50` (`member_id`),\n CONSTRAINT `FK_Reference_50` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员统计信息'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "consume_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "累计消费金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "consume_amount" }, { "objectType": "TableField_MYSQL", "name": "order_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_count" }, { "objectType": "TableField_MYSQL", "name": "coupon_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "优惠券数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_count" }, { "objectType": "TableField_MYSQL", "name": "comment_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "评价数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "comment_count" }, { "objectType": "TableField_MYSQL", "name": "return_order_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退货数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "return_order_count" }, { "objectType": "TableField_MYSQL", "name": "login_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "登录次数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "login_count" }, { "objectType": "TableField_MYSQL", "name": "attend_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "关注数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "attend_count" }, { "objectType": "TableField_MYSQL", "name": "fans_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "粉丝数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "fans_count" }, { "objectType": "TableField_MYSQL", "name": "collect_product_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "collect_product_count" }, { "objectType": "TableField_MYSQL", "name": "collect_subject_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "collect_subject_count" }, { "objectType": "TableField_MYSQL", "name": "collect_topic_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "collect_topic_count" }, { "objectType": "TableField_MYSQL", "name": "collect_comment_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "collect_comment_count" }, { "objectType": "TableField_MYSQL", "name": "invite_friend_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "invite_friend_count" }, { "objectType": "TableField_MYSQL", "name": "recent_order_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "最后一次下订单时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recent_order_time" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_50", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_50", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_50", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_50" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_tag", "comment": "用户标签表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_tag", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_tag` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL,\n `finish_order_count` int(11) DEFAULT NULL COMMENT '自动打标签完成订单数量',\n `finish_order_amount` decimal(10,2) DEFAULT NULL COMMENT '自动打标签完成订单金额',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户标签表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "finish_order_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "自动打标签完成订单数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "finish_order_count" }, { "objectType": "TableField_MYSQL", "name": "finish_order_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "自动打标签完成订单金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "finish_order_amount" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_member_task", "comment": "会员任务表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_member_task", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_member_task` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL,\n `growth` int(11) DEFAULT NULL COMMENT '赠送成长值',\n `intergration` int(11) DEFAULT NULL COMMENT '赠送积分',\n `type` int(1) DEFAULT NULL COMMENT '任务类型:0->新手任务;1->日常任务',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员任务表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "growth", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "赠送成长值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "growth" }, { "objectType": "TableField_MYSQL", "name": "intergration", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "赠送积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "intergration" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "任务类型:0->新手任务;1->日常任务", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_menu", "comment": "后台菜单表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_menu", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_menu` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `parent_id` bigint(20) DEFAULT NULL COMMENT '父级ID',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `title` varchar(100) DEFAULT NULL COMMENT '菜单名称',\n `level` int(4) DEFAULT NULL COMMENT '菜单级数',\n `sort` int(4) DEFAULT NULL COMMENT '菜单排序',\n `name` varchar(100) DEFAULT NULL COMMENT '前端名称',\n `icon` varchar(200) DEFAULT NULL COMMENT '前端图标',\n `hidden` int(1) DEFAULT NULL COMMENT '前端隐藏',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_80` (`parent_id`),\n CONSTRAINT `FK_Reference_80` FOREIGN KEY (`parent_id`) REFERENCES `ums_menu` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台菜单表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "parent_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "父级ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "parent_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "title", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "菜单名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "title" }, { "objectType": "TableField_MYSQL", "name": "level", "type": "int", "length": 4, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "菜单级数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "level" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 4, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "菜单排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "前端名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "前端图标", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "hidden", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "前端隐藏", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "hidden" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_80", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_80", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "parent_id", "keyLength": 0, "order": "", "oldName": "parent_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_80", "fields": [ "parent_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_menu", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_80" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_permission", "comment": "后台用户权限表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_permission", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_permission` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `pid` bigint(20) DEFAULT NULL COMMENT '父级权限id',\n `name` varchar(100) DEFAULT NULL COMMENT '名称',\n `value` varchar(200) DEFAULT NULL COMMENT '权限值',\n `icon` varchar(500) DEFAULT NULL COMMENT '图标',\n `type` int(1) DEFAULT NULL COMMENT '权限类型:0->目录;1->菜单;2->按钮(接口绑定权限)',\n `uri` varchar(200) DEFAULT NULL COMMENT '前端资源路径',\n `status` int(1) DEFAULT NULL COMMENT '启用状态;0->禁用;1->启用',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `sort` int(11) DEFAULT NULL COMMENT '排序',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_68` (`pid`),\n CONSTRAINT `FK_Reference_68` FOREIGN KEY (`pid`) REFERENCES `ums_permission` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户权限表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "pid", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "父级权限id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pid" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "value", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "权限值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "value" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "图标", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "权限类型:0->目录;1->菜单;2->按钮(接口绑定权限)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" }, { "objectType": "TableField_MYSQL", "name": "uri", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "前端资源路径", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "uri" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "启用状态;0->禁用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_68", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_68", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "pid", "keyLength": 0, "order": "", "oldName": "pid" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_68", "fields": [ "pid" ], "referenceSchema": "mall-ref", "referenceTable": "ums_permission", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_68" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_resource", "comment": "后台资源表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_resource", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_resource` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `category_id` bigint(20) DEFAULT NULL COMMENT '资源分类ID',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `name` varchar(200) DEFAULT NULL COMMENT '资源名称',\n `url` varchar(200) DEFAULT NULL COMMENT '资源URL',\n `description` varchar(500) DEFAULT NULL COMMENT '描述',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_85` (`category_id`),\n CONSTRAINT `FK_Reference_85` FOREIGN KEY (`category_id`) REFERENCES `ums_resource_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台资源表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "资源分类ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "category_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "资源名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "url", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "资源URL", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "url" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "描述", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_85", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_85", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "category_id", "keyLength": 0, "order": "", "oldName": "category_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_85", "fields": [ "category_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_resource_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_85" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_resource_category", "comment": "资源分类表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_resource_category", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_resource_category` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `name` varchar(200) DEFAULT NULL COMMENT '分类名称',\n `sort` int(4) DEFAULT NULL COMMENT '排序',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='资源分类表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "分类名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 4, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_role", "comment": "后台用户角色表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_role", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_role` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL COMMENT '名称',\n `description` varchar(500) DEFAULT NULL COMMENT '描述',\n `admin_count` int(11) DEFAULT NULL COMMENT '后台用户数量',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `status` int(1) DEFAULT '1' COMMENT '启用状态:0->禁用;1->启用',\n `sort` int(11) DEFAULT '0',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户角色表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "描述", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" }, { "objectType": "TableField_MYSQL", "name": "admin_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "后台用户数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "admin_count" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "1", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "启用状态:0->禁用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_role_menu_relation", "comment": "后台角色菜单关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_role_menu_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_role_menu_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',\n `menu_id` bigint(20) DEFAULT NULL COMMENT '菜单ID',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_81` (`role_id`),\n KEY `FK_Reference_82` (`menu_id`),\n CONSTRAINT `FK_Reference_81` FOREIGN KEY (`role_id`) REFERENCES `ums_role` (`id`),\n CONSTRAINT `FK_Reference_82` FOREIGN KEY (`menu_id`) REFERENCES `ums_menu` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台角色菜单关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "role_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "角色ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "role_id" }, { "objectType": "TableField_MYSQL", "name": "menu_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "菜单ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "menu_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_81", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_81", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "role_id", "keyLength": 0, "order": "", "oldName": "role_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_82", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_82", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "menu_id", "keyLength": 0, "order": "", "oldName": "menu_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_81", "fields": [ "role_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_role", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_81" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_82", "fields": [ "menu_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_menu", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_82" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_role_permission_relation", "comment": "后台用户角色和权限关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_role_permission_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_role_permission_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `role_id` bigint(20) DEFAULT NULL,\n `permission_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_71` (`role_id`),\n KEY `FK_Reference_72` (`permission_id`),\n CONSTRAINT `FK_Reference_71` FOREIGN KEY (`role_id`) REFERENCES `ums_role` (`id`),\n CONSTRAINT `FK_Reference_72` FOREIGN KEY (`permission_id`) REFERENCES `ums_permission` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户角色和权限关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "role_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "role_id" }, { "objectType": "TableField_MYSQL", "name": "permission_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "permission_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_71", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_71", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "role_id", "keyLength": 0, "order": "", "oldName": "role_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_72", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_72", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "permission_id", "keyLength": 0, "order": "", "oldName": "permission_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_71", "fields": [ "role_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_role", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_71" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_72", "fields": [ "permission_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_permission", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_72" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_role_resource_relation", "comment": "后台角色资源关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_role_resource_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_role_resource_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',\n `resource_id` bigint(20) DEFAULT NULL COMMENT '资源ID',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_83` (`role_id`),\n KEY `FK_Reference_84` (`resource_id`),\n CONSTRAINT `FK_Reference_83` FOREIGN KEY (`role_id`) REFERENCES `ums_role` (`id`),\n CONSTRAINT `FK_Reference_84` FOREIGN KEY (`resource_id`) REFERENCES `ums_resource` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台角色资源关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "role_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "角色ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "role_id" }, { "objectType": "TableField_MYSQL", "name": "resource_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "资源ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "resource_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_83", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_83", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "role_id", "keyLength": 0, "order": "", "oldName": "role_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_84", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_84", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "resource_id", "keyLength": 0, "order": "", "oldName": "resource_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_83", "fields": [ "role_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_role", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_83" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_84", "fields": [ "resource_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_resource", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_84" } ], "triggers": [], "tablePartitions": [] } ], "views": [] } ] }, "diagrams": [ { "name": "会员模块数据库模型", "paperWidth": 3, "paperHeight": 1, "tableFont": "Arial Unicode MS", "tableFontSize": 14, "isBalckWhite": false, "showDBSchemaName": false, "showViewRelations": true, "notation": "default", "showFieldComment": false, "showTableComment": false, "shapes": [ { "type": "table", "schemaName": "mall-ref", "tableName": "ums_growth_change_history", "x": 280, "y": 450, "width": 230, "height": 211, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_integration_change_history", "x": 10, "y": 340, "width": 259, "height": 211, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_integration_consume_setting", "x": 310, "y": 790, "width": 270, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member", "x": 610, "y": 290, "width": 248, "height": 431, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_level", "x": 40, "y": 0, "width": 223, "height": 311, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_login_log", "x": 30, "y": 580, "width": 193, "height": 191, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_member_tag_relation", "x": 940, "y": 380, "width": 279, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_product_category_relation", "x": 890, "y": 40, "width": 317, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_receive_address", "x": 570, "y": 10, "width": 247, "height": 251, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_rule_setting", "x": 50, "y": 790, "width": 237, "height": 191, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_statistics_info", "x": 310, "y": 10, "width": 228, "height": 371, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_tag", "x": 930, "y": 540, "width": 241, "height": 131, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_member_task", "x": 620, "y": 790, "width": 158, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } } ], "layers": [], "relations": [ { "name": "FK_Reference_56", "sourceTableName": "ums_growth_change_history", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 525, "y": 520 }, { "x": 595, "y": 520 } ], "label": { "x": 280, "y": 20, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_55", "sourceTableName": "ums_integration_change_history", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 284, "y": 430 }, { "x": 595, "y": 430 } ], "label": { "x": 152, "y": 140, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_47", "sourceTableName": "ums_member", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 595, "y": 510 }, { "x": 560, "y": 510 }, { "x": 560, "y": 240 }, { "x": 278, "y": 240 } ], "label": { "x": 610, "y": 170, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_52", "sourceTableName": "ums_member_login_log", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 238, "y": 690 }, { "x": 595, "y": 690 } ], "label": { "x": 233, "y": 187, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_53", "sourceTableName": "ums_member_member_tag_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 925, "y": 440 }, { "x": 873, "y": 440 } ], "label": { "x": 940, "y": 100, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_48", "sourceTableName": "ums_member_product_category_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1070, "y": 166 }, { "x": 1070, "y": 340 }, { "x": 873, "y": 340 } ], "label": { "x": 750, "y": 40, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_51", "sourceTableName": "ums_member_receive_address", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 640, "y": 276 }, { "x": 640, "y": 275 } ], "label": { "x": 520, "y": 84, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_50", "sourceTableName": "ums_member_statistics_info", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 553, "y": 310 }, { "x": 595, "y": 310 } ], "label": { "x": 431, "y": 10, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_54", "sourceTableName": "ums_member_member_tag_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1060, "y": 506 }, { "x": 1060, "y": 525 }, { "x": 1060, "y": 525 }, { "x": 1060, "y": 525 } ], "label": { "x": 778, "y": 154, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } } ], "viewRelations": [] } ] } ================================================ FILE: document/navicat/商品模块数据库模型.ndm2 ================================================ { "paper": { "name": "A4", "leftMargin": 0.5, "rightMargin": 0.5, "topMargin": 0.5, "bottomMargin": 0.5, "isPortriat": true }, "modelVersion": 2.01, "defaultSchema": "Default", "server": { "objectType": "Server_MYSQL", "name": "Default", "serverVersion": 50799, "edition": "Default", "lowerCaseTableNames": 0, "schemas": [ { "objectType": "Schema_MYSQL", "name": "Default", "tables": [], "views": [] }, { "objectType": "Schema_MYSQL", "name": "mall-ref", "tables": [ { "objectType": "Table_MYSQL", "name": "pms_album", "comment": "相册表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_album", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_album` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(64) DEFAULT NULL,\n `cover_pic` varchar(1000) DEFAULT NULL,\n `pic_count` int(11) DEFAULT NULL,\n `sort` int(11) DEFAULT NULL,\n `description` varchar(1000) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='相册表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "cover_pic", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "cover_pic" }, { "objectType": "TableField_MYSQL", "name": "pic_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pic_count" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_album_pic", "comment": "画册图片表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_album_pic", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_album_pic` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `album_id` bigint(20) DEFAULT NULL,\n `pic` varchar(1000) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_25` (`album_id`),\n CONSTRAINT `FK_Reference_25` FOREIGN KEY (`album_id`) REFERENCES `pms_album` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='画册图片表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "album_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "album_id" }, { "objectType": "TableField_MYSQL", "name": "pic", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pic" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_25", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_25", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "album_id", "keyLength": 0, "order": "", "oldName": "album_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_25", "fields": [ "album_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_album", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_25" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_brand", "comment": "品牌表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_brand", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_brand` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(64) DEFAULT NULL,\n `first_letter` varchar(8) DEFAULT NULL COMMENT '首字母',\n `sort` int(11) DEFAULT NULL,\n `factory_status` int(1) DEFAULT NULL COMMENT '是否为品牌制造商:0->不是;1->是',\n `show_status` int(1) DEFAULT NULL,\n `product_count` int(11) DEFAULT NULL COMMENT '产品数量',\n `product_comment_count` int(11) DEFAULT NULL COMMENT '产品评论数量',\n `logo` varchar(255) DEFAULT NULL COMMENT '品牌logo',\n `big_pic` varchar(255) DEFAULT NULL COMMENT '专区大图',\n `brand_story` text COMMENT '品牌故事',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='品牌表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "first_letter", "type": "varchar", "length": 8, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "首字母", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "first_letter" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "factory_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否为品牌制造商:0->不是;1->是", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "factory_status" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" }, { "objectType": "TableField_MYSQL", "name": "product_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "产品数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_count" }, { "objectType": "TableField_MYSQL", "name": "product_comment_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "产品评论数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_comment_count" }, { "objectType": "TableField_MYSQL", "name": "logo", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "品牌logo", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "logo" }, { "objectType": "TableField_MYSQL", "name": "big_pic", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "专区大图", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "big_pic" }, { "objectType": "TableField_MYSQL", "name": "brand_story", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "品牌故事", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "brand_story" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_comment", "comment": "商品评价表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_comment", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_comment` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `member_nick_name` varchar(255) DEFAULT NULL,\n `product_name` varchar(255) DEFAULT NULL,\n `star` int(3) DEFAULT NULL COMMENT '评价星数:0->5',\n `member_ip` varchar(64) DEFAULT NULL COMMENT '评价的ip',\n `create_time` datetime DEFAULT NULL,\n `show_status` int(1) DEFAULT NULL,\n `product_attribute` varchar(255) DEFAULT NULL COMMENT '购买时的商品属性',\n `collect_couont` int(11) DEFAULT NULL,\n `read_count` int(11) DEFAULT NULL,\n `content` text,\n `pics` varchar(1000) DEFAULT NULL COMMENT '上传图片地址,以逗号隔开',\n `member_icon` varchar(255) DEFAULT NULL COMMENT '评论用户头像',\n `replay_count` int(11) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_23` (`product_id`),\n CONSTRAINT `FK_Reference_23` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品评价表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "member_nick_name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_nick_name" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "star", "type": "int", "length": 3, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "评价星数:0->5", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "star" }, { "objectType": "TableField_MYSQL", "name": "member_ip", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "评价的ip", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_ip" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" }, { "objectType": "TableField_MYSQL", "name": "product_attribute", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "购买时的商品属性", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attribute" }, { "objectType": "TableField_MYSQL", "name": "collect_couont", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "collect_couont" }, { "objectType": "TableField_MYSQL", "name": "read_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "read_count" }, { "objectType": "TableField_MYSQL", "name": "content", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "content" }, { "objectType": "TableField_MYSQL", "name": "pics", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "上传图片地址,以逗号隔开", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pics" }, { "objectType": "TableField_MYSQL", "name": "member_icon", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "评论用户头像", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_icon" }, { "objectType": "TableField_MYSQL", "name": "replay_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "replay_count" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_23", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_23", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_23", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_23" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_comment_replay", "comment": "产品评价回复表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_comment_replay", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_comment_replay` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `comment_id` bigint(20) DEFAULT NULL,\n `member_nick_name` varchar(255) DEFAULT NULL,\n `member_icon` varchar(255) DEFAULT NULL,\n `content` varchar(1000) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `type` int(1) DEFAULT NULL COMMENT '评论人员类型;0->会员;1->管理员',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_24` (`comment_id`),\n CONSTRAINT `FK_Reference_24` FOREIGN KEY (`comment_id`) REFERENCES `pms_comment` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品评价回复表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "comment_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "comment_id" }, { "objectType": "TableField_MYSQL", "name": "member_nick_name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_nick_name" }, { "objectType": "TableField_MYSQL", "name": "member_icon", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_icon" }, { "objectType": "TableField_MYSQL", "name": "content", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "content" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "评论人员类型;0->会员;1->管理员", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_24", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_24", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "comment_id", "keyLength": 0, "order": "", "oldName": "comment_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_24", "fields": [ "comment_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_comment", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_24" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_feight_template", "comment": "运费模版", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_feight_template", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_feight_template` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(64) DEFAULT NULL,\n `charge_type` int(1) DEFAULT NULL COMMENT '计费类型:0->按重量;1->按件数',\n `first_weight` decimal(10,2) DEFAULT NULL COMMENT '首重kg',\n `first_fee` decimal(10,2) DEFAULT NULL COMMENT '首费(元)',\n `continue_weight` decimal(10,2) DEFAULT NULL,\n `continme_fee` decimal(10,2) DEFAULT NULL,\n `dest` varchar(255) DEFAULT NULL COMMENT '目的地(省、市)',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='运费模版'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "charge_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "计费类型:0->按重量;1->按件数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "charge_type" }, { "objectType": "TableField_MYSQL", "name": "first_weight", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "首重kg", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "first_weight" }, { "objectType": "TableField_MYSQL", "name": "first_fee", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "首费(元)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "first_fee" }, { "objectType": "TableField_MYSQL", "name": "continue_weight", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "continue_weight" }, { "objectType": "TableField_MYSQL", "name": "continme_fee", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "continme_fee" }, { "objectType": "TableField_MYSQL", "name": "dest", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "目的地(省、市)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "dest" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_member_price", "comment": "商品会员价格表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_member_price", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_member_price` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `member_level_id` bigint(20) DEFAULT NULL,\n `member_price` decimal(10,2) DEFAULT NULL COMMENT '会员价格',\n `member_level_name` varchar(100) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_60` (`member_level_id`),\n KEY `FK_Reference_9` (`product_id`),\n CONSTRAINT `FK_Reference_60` FOREIGN KEY (`member_level_id`) REFERENCES `ums_member_level` (`id`),\n CONSTRAINT `FK_Reference_9` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品会员价格表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "member_level_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_level_id" }, { "objectType": "TableField_MYSQL", "name": "member_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "会员价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_price" }, { "objectType": "TableField_MYSQL", "name": "member_level_name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_level_name" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_60", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_60", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_level_id", "keyLength": 0, "order": "", "oldName": "member_level_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_9", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_9", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_60", "fields": [ "member_level_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member_level", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_60" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_9", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_9" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_attribute", "comment": "商品属性参数表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_attribute", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_attribute` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_attribute_category_id` bigint(20) DEFAULT NULL,\n `name` varchar(64) DEFAULT NULL,\n `select_type` int(1) DEFAULT NULL COMMENT '属性选择类型:0->唯一;1->单选;2->多选;对应属性和参数意义不同;',\n `input_type` int(1) DEFAULT NULL COMMENT '属性录入方式:0->手工录入;1->从列表中选取',\n `input_list` varchar(255) DEFAULT NULL COMMENT '可选值列表,以逗号隔开',\n `sort` int(11) DEFAULT NULL COMMENT '排序字段:最高的可以单独上传图片',\n `filter_type` int(1) DEFAULT NULL COMMENT '分类筛选样式:1->普通;1->颜色',\n `search_type` int(1) DEFAULT NULL COMMENT '检索类型;0->不需要进行检索;1->关键字检索;2->范围检索',\n `related_status` int(1) DEFAULT NULL COMMENT '相同属性产品是否关联;0->不关联;1->关联',\n `hand_add_status` int(1) DEFAULT NULL COMMENT '是否支持手动新增;0->不支持;1->支持',\n `type` int(1) DEFAULT NULL COMMENT '属性的类型;0->规格;1->参数',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_12` (`product_attribute_category_id`),\n CONSTRAINT `FK_Reference_12` FOREIGN KEY (`product_attribute_category_id`) REFERENCES `pms_product_attribute_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品属性参数表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_attribute_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attribute_category_id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "select_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "属性选择类型:0->唯一;1->单选;2->多选;对应属性和参数意义不同;", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "select_type" }, { "objectType": "TableField_MYSQL", "name": "input_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "属性录入方式:0->手工录入;1->从列表中选取", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "input_type" }, { "objectType": "TableField_MYSQL", "name": "input_list", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "可选值列表,以逗号隔开", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "input_list" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序字段:最高的可以单独上传图片", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "filter_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "分类筛选样式:1->普通;1->颜色", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "filter_type" }, { "objectType": "TableField_MYSQL", "name": "search_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "检索类型;0->不需要进行检索;1->关键字检索;2->范围检索", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "search_type" }, { "objectType": "TableField_MYSQL", "name": "related_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "相同属性产品是否关联;0->不关联;1->关联", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "related_status" }, { "objectType": "TableField_MYSQL", "name": "hand_add_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否支持手动新增;0->不支持;1->支持", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "hand_add_status" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "属性的类型;0->规格;1->参数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_12", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_12", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_attribute_category_id", "keyLength": 0, "order": "", "oldName": "product_attribute_category_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_12", "fields": [ "product_attribute_category_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_attribute_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_12" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_attribute_category", "comment": "产品属性分类表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_attribute_category", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_attribute_category` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(64) DEFAULT NULL,\n `attribute_count` int(11) DEFAULT NULL COMMENT '属性数量',\n `param_count` int(11) DEFAULT NULL COMMENT '参数数量',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品属性分类表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "attribute_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "属性数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "attribute_count" }, { "objectType": "TableField_MYSQL", "name": "param_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "参数数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "param_count" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_attribute_value", "comment": "存储产品参数信息的表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_attribute_value", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_attribute_value` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `product_attribute_id` bigint(20) DEFAULT NULL,\n `value` varchar(64) DEFAULT NULL COMMENT '手动添加规格或参数的值,参数单值,规格有多个时以逗号隔开',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_14` (`product_id`),\n KEY `FK_Reference_15` (`product_attribute_id`),\n CONSTRAINT `FK_Reference_14` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`),\n CONSTRAINT `FK_Reference_15` FOREIGN KEY (`product_attribute_id`) REFERENCES `pms_product_attribute` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储产品参数信息的表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "product_attribute_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attribute_id" }, { "objectType": "TableField_MYSQL", "name": "value", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "手动添加规格或参数的值,参数单值,规格有多个时以逗号隔开", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "value" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_14", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_14", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_15", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_15", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_attribute_id", "keyLength": 0, "order": "", "oldName": "product_attribute_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_14", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_14" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_15", "fields": [ "product_attribute_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_attribute", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_15" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_category", "comment": "产品分类", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_category", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_category` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `parent_id` bigint(20) DEFAULT NULL COMMENT '上机分类的编号:0表示一级分类',\n `name` varchar(64) DEFAULT NULL,\n `level` int(1) DEFAULT NULL COMMENT '分类级别:0->1级;1->2级',\n `product_count` int(11) DEFAULT NULL,\n `product_unit` varchar(64) DEFAULT NULL,\n `nav_status` int(1) DEFAULT NULL COMMENT '是否显示在导航栏:0->不显示;1->显示',\n `show_status` int(1) DEFAULT NULL COMMENT '显示状态:0->不显示;1->显示',\n `sort` int(11) DEFAULT NULL,\n `icon` varchar(255) DEFAULT NULL COMMENT '图标',\n `keywords` varchar(255) DEFAULT NULL,\n `description` text COMMENT '描述',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_20` (`parent_id`),\n CONSTRAINT `FK_Reference_20` FOREIGN KEY (`parent_id`) REFERENCES `pms_product_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品分类'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "parent_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "上机分类的编号:0表示一级分类", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "parent_id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "level", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "分类级别:0->1级;1->2级", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "level" }, { "objectType": "TableField_MYSQL", "name": "product_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_count" }, { "objectType": "TableField_MYSQL", "name": "product_unit", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_unit" }, { "objectType": "TableField_MYSQL", "name": "nav_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否显示在导航栏:0->不显示;1->显示", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "nav_status" }, { "objectType": "TableField_MYSQL", "name": "show_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "显示状态:0->不显示;1->显示", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "show_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "图标", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "keywords", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "keywords" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "描述", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_20", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_20", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "parent_id", "keyLength": 0, "order": "", "oldName": "parent_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_20", "fields": [ "parent_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_20" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_category_attribute_relation", "comment": "产品的分类和属性的关系表,用于设置分类筛选条件(只支持一级分类)", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_category_attribute_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_category_attribute_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_category_id` bigint(20) DEFAULT NULL,\n `product_attribute_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_21` (`product_category_id`),\n KEY `FK_Reference_22` (`product_attribute_id`),\n CONSTRAINT `FK_Reference_21` FOREIGN KEY (`product_category_id`) REFERENCES `pms_product_category` (`id`),\n CONSTRAINT `FK_Reference_22` FOREIGN KEY (`product_attribute_id`) REFERENCES `pms_product_attribute` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品的分类和属性的关系表,用于设置分类筛选条件(只支持一级分类)'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_id" }, { "objectType": "TableField_MYSQL", "name": "product_attribute_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attribute_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_21", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_21", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_category_id", "keyLength": 0, "order": "", "oldName": "product_category_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_22", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_22", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_attribute_id", "keyLength": 0, "order": "", "oldName": "product_attribute_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_21", "fields": [ "product_category_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_21" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_22", "fields": [ "product_attribute_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_attribute", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_22" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_full_reduction", "comment": "产品满减表(只针对同商品)", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_full_reduction", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_full_reduction` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `full_price` decimal(10,2) DEFAULT NULL,\n `reduce_price` decimal(10,2) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_11` (`product_id`),\n CONSTRAINT `FK_Reference_11` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品满减表(只针对同商品)'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "full_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "full_price" }, { "objectType": "TableField_MYSQL", "name": "reduce_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "reduce_price" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_11", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_11", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_11", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_11" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_ladder", "comment": "产品阶梯价格表(只针对同商品)", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_ladder", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_ladder` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `count` int(11) DEFAULT NULL COMMENT '满足的商品数量',\n `discount` decimal(10,2) DEFAULT NULL COMMENT '折扣',\n `price` decimal(10,2) DEFAULT NULL COMMENT '折后价格',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_10` (`product_id`),\n CONSTRAINT `FK_Reference_10` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品阶梯价格表(只针对同商品)'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "满足的商品数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "count" }, { "objectType": "TableField_MYSQL", "name": "discount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "折扣", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "discount" }, { "objectType": "TableField_MYSQL", "name": "price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "折后价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "price" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_10", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_10", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_10", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_10" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_operate_log", "comment": "", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_operate_log", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:55", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_operate_log` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `price_old` decimal(10,2) DEFAULT NULL,\n `price_new` decimal(10,2) DEFAULT NULL,\n `sale_price_old` decimal(10,2) DEFAULT NULL,\n `sale_price_new` decimal(10,2) DEFAULT NULL,\n `gift_point_old` int(11) DEFAULT NULL COMMENT '赠送的积分',\n `gift_point_new` int(11) DEFAULT NULL,\n `use_point_limit_old` int(11) DEFAULT NULL,\n `use_point_limit_new` int(11) DEFAULT NULL,\n `operate_man` varchar(64) DEFAULT NULL COMMENT '操作人',\n `create_time` datetime DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_4` (`product_id`),\n CONSTRAINT `FK_Reference_4` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "price_old", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "price_old" }, { "objectType": "TableField_MYSQL", "name": "price_new", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "price_new" }, { "objectType": "TableField_MYSQL", "name": "sale_price_old", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sale_price_old" }, { "objectType": "TableField_MYSQL", "name": "sale_price_new", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sale_price_new" }, { "objectType": "TableField_MYSQL", "name": "gift_point_old", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "赠送的积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gift_point_old" }, { "objectType": "TableField_MYSQL", "name": "gift_point_new", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gift_point_new" }, { "objectType": "TableField_MYSQL", "name": "use_point_limit_old", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_point_limit_old" }, { "objectType": "TableField_MYSQL", "name": "use_point_limit_new", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_point_limit_new" }, { "objectType": "TableField_MYSQL", "name": "operate_man", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作人", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_man" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_4", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_4", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_4", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_4" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product_vertify_record", "comment": "商品审核记录", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product_vertify_record", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product_vertify_record` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `vertify_man` varchar(64) DEFAULT NULL COMMENT '审核人',\n `status` int(1) DEFAULT NULL COMMENT '审核后的状态:0->未通过;2->已通过',\n `detail` varchar(255) DEFAULT NULL COMMENT '反馈详情',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_3` (`product_id`),\n CONSTRAINT `FK_Reference_3` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品审核记录'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "vertify_man", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "审核人", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "vertify_man" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "审核后的状态:0->未通过;2->已通过", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "detail", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "反馈详情", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_3", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_3", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_3", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_3" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_sku_stock", "comment": "sku的库存", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_sku_stock", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_sku_stock` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `sku_code` varchar(64) NOT NULL COMMENT 'sku编码',\n `price` decimal(10,2) DEFAULT NULL,\n `stock` int(11) DEFAULT '0' COMMENT '库存',\n `low_stock` int(11) DEFAULT NULL COMMENT '预警库存',\n `pic` varchar(255) DEFAULT NULL COMMENT '展示图片',\n `sale` int(11) DEFAULT NULL COMMENT '销量',\n `promotion_price` decimal(10,2) DEFAULT NULL COMMENT '单品促销价格',\n `lock_stock` int(11) DEFAULT '0' COMMENT '锁定库存',\n `sp_data` varchar(500) DEFAULT NULL COMMENT '商品销售属性,json格式',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_2` (`product_id`),\n CONSTRAINT `FK_Reference_2` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='sku的库存'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "sku_code", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "sku编码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sku_code" }, { "objectType": "TableField_MYSQL", "name": "price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "price" }, { "objectType": "TableField_MYSQL", "name": "stock", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "库存", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "stock" }, { "objectType": "TableField_MYSQL", "name": "low_stock", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "预警库存", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "low_stock" }, { "objectType": "TableField_MYSQL", "name": "pic", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "展示图片", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pic" }, { "objectType": "TableField_MYSQL", "name": "sale", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "销量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sale" }, { "objectType": "TableField_MYSQL", "name": "promotion_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "单品促销价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_price" }, { "objectType": "TableField_MYSQL", "name": "lock_stock", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "锁定库存", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "lock_stock" }, { "objectType": "TableField_MYSQL", "name": "sp_data", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品销售属性,json格式", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sp_data" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_2", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_2", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_2", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_2" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "pms_product", "comment": "商品信息", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "pms_product", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 65536, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `pms_product` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `brand_id` bigint(20) DEFAULT NULL,\n `product_category_id` bigint(20) DEFAULT NULL,\n `feight_template_id` bigint(20) DEFAULT NULL,\n `product_attribute_category_id` bigint(20) DEFAULT NULL,\n `name` varchar(64) NOT NULL,\n `pic` varchar(255) DEFAULT NULL,\n `product_sn` varchar(64) NOT NULL COMMENT '货号',\n `delete_status` int(1) DEFAULT NULL COMMENT '删除状态:0->未删除;1->已删除',\n `publish_status` int(1) DEFAULT NULL COMMENT '上架状态:0->下架;1->上架',\n `new_status` int(1) DEFAULT NULL COMMENT '新品状态:0->不是新品;1->新品',\n `recommand_status` int(1) DEFAULT NULL COMMENT '推荐状态;0->不推荐;1->推荐',\n `verify_status` int(1) DEFAULT NULL COMMENT '审核状态:0->未审核;1->审核通过',\n `sort` int(11) DEFAULT NULL COMMENT '排序',\n `sale` int(11) DEFAULT NULL COMMENT '销量',\n `price` decimal(10,2) DEFAULT NULL,\n `promotion_price` decimal(10,2) DEFAULT NULL COMMENT '促销价格',\n `gift_growth` int(11) DEFAULT '0' COMMENT '赠送的成长值',\n `gift_point` int(11) DEFAULT '0' COMMENT '赠送的积分',\n `use_point_limit` int(11) DEFAULT NULL COMMENT '限制使用的积分数',\n `sub_title` varchar(255) DEFAULT NULL COMMENT '副标题',\n `description` text COMMENT '商品描述',\n `original_price` decimal(10,2) DEFAULT NULL COMMENT '市场价',\n `stock` int(11) DEFAULT NULL COMMENT '库存',\n `low_stock` int(11) DEFAULT NULL COMMENT '库存预警值',\n `unit` varchar(16) DEFAULT NULL COMMENT '单位',\n `weight` decimal(10,2) DEFAULT NULL COMMENT '商品重量,默认为克',\n `preview_status` int(1) DEFAULT NULL COMMENT '是否为预告商品:0->不是;1->是',\n `service_ids` varchar(64) DEFAULT NULL COMMENT '以逗号分割的产品服务:1->无忧退货;2->快速退款;3->免费包邮',\n `keywords` varchar(255) DEFAULT NULL,\n `note` varchar(255) DEFAULT NULL,\n `album_pics` varchar(255) DEFAULT NULL COMMENT '画册图片,连产品图片限制为5张,以逗号分割',\n `detail_title` varchar(255) DEFAULT NULL,\n `detail_desc` text,\n `detail_html` text COMMENT '产品详情网页内容',\n `detail_mobile_html` text COMMENT '移动端网页详情',\n `promotion_start_time` datetime DEFAULT NULL COMMENT '促销开始时间',\n `promotion_end_time` datetime DEFAULT NULL COMMENT '促销结束时间',\n `promotion_per_limit` int(11) DEFAULT NULL COMMENT '活动限购数量',\n `promotion_type` int(1) DEFAULT NULL COMMENT '促销类型:0->没有促销使用原价;1->使用促销价;2->使用会员价;3->使用阶梯价格;4->使用满减价格;5->限时购',\n `product_category_name` varchar(255) DEFAULT NULL COMMENT '产品分类名称',\n `brand_name` varchar(255) DEFAULT NULL COMMENT '品牌名称',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_1` (`brand_id`),\n KEY `FK_Reference_13` (`product_attribute_category_id`),\n KEY `FK_Reference_5` (`product_category_id`),\n KEY `FK_Reference_6` (`feight_template_id`),\n CONSTRAINT `FK_Reference_1` FOREIGN KEY (`brand_id`) REFERENCES `pms_brand` (`id`),\n CONSTRAINT `FK_Reference_13` FOREIGN KEY (`product_attribute_category_id`) REFERENCES `pms_product_attribute_category` (`id`),\n CONSTRAINT `FK_Reference_5` FOREIGN KEY (`product_category_id`) REFERENCES `pms_product_category` (`id`),\n CONSTRAINT `FK_Reference_6` FOREIGN KEY (`feight_template_id`) REFERENCES `pms_feight_template` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品信息'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "brand_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "brand_id" }, { "objectType": "TableField_MYSQL", "name": "product_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_id" }, { "objectType": "TableField_MYSQL", "name": "feight_template_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "feight_template_id" }, { "objectType": "TableField_MYSQL", "name": "product_attribute_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attribute_category_id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "pic", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pic" }, { "objectType": "TableField_MYSQL", "name": "product_sn", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "货号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sn" }, { "objectType": "TableField_MYSQL", "name": "delete_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "删除状态:0->未删除;1->已删除", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "delete_status" }, { "objectType": "TableField_MYSQL", "name": "publish_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "上架状态:0->下架;1->上架", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "publish_status" }, { "objectType": "TableField_MYSQL", "name": "new_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "新品状态:0->不是新品;1->新品", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "new_status" }, { "objectType": "TableField_MYSQL", "name": "recommand_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "推荐状态;0->不推荐;1->推荐", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recommand_status" }, { "objectType": "TableField_MYSQL", "name": "verify_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "审核状态:0->未审核;1->审核通过", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "verify_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "sale", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "销量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sale" }, { "objectType": "TableField_MYSQL", "name": "price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "price" }, { "objectType": "TableField_MYSQL", "name": "promotion_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "促销价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_price" }, { "objectType": "TableField_MYSQL", "name": "gift_growth", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "赠送的成长值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gift_growth" }, { "objectType": "TableField_MYSQL", "name": "gift_point", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "赠送的积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gift_point" }, { "objectType": "TableField_MYSQL", "name": "use_point_limit", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "限制使用的积分数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_point_limit" }, { "objectType": "TableField_MYSQL", "name": "sub_title", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "副标题", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sub_title" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品描述", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" }, { "objectType": "TableField_MYSQL", "name": "original_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "市场价", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "original_price" }, { "objectType": "TableField_MYSQL", "name": "stock", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "库存", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "stock" }, { "objectType": "TableField_MYSQL", "name": "low_stock", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "库存预警值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "low_stock" }, { "objectType": "TableField_MYSQL", "name": "unit", "type": "varchar", "length": 16, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "单位", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "unit" }, { "objectType": "TableField_MYSQL", "name": "weight", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品重量,默认为克", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "weight" }, { "objectType": "TableField_MYSQL", "name": "preview_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否为预告商品:0->不是;1->是", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "preview_status" }, { "objectType": "TableField_MYSQL", "name": "service_ids", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "以逗号分割的产品服务:1->无忧退货;2->快速退款;3->免费包邮", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "service_ids" }, { "objectType": "TableField_MYSQL", "name": "keywords", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "keywords" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" }, { "objectType": "TableField_MYSQL", "name": "album_pics", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "画册图片,连产品图片限制为5张,以逗号分割", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "album_pics" }, { "objectType": "TableField_MYSQL", "name": "detail_title", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail_title" }, { "objectType": "TableField_MYSQL", "name": "detail_desc", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail_desc" }, { "objectType": "TableField_MYSQL", "name": "detail_html", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "产品详情网页内容", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail_html" }, { "objectType": "TableField_MYSQL", "name": "detail_mobile_html", "type": "text", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "移动端网页详情", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail_mobile_html" }, { "objectType": "TableField_MYSQL", "name": "promotion_start_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "促销开始时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_start_time" }, { "objectType": "TableField_MYSQL", "name": "promotion_end_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "促销结束时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_end_time" }, { "objectType": "TableField_MYSQL", "name": "promotion_per_limit", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "活动限购数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_per_limit" }, { "objectType": "TableField_MYSQL", "name": "promotion_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "促销类型:0->没有促销使用原价;1->使用促销价;2->使用会员价;3->使用阶梯价格;4->使用满减价格;5->限时购", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_type" }, { "objectType": "TableField_MYSQL", "name": "product_category_name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "产品分类名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_name" }, { "objectType": "TableField_MYSQL", "name": "brand_name", "type": "varchar", "length": 255, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "品牌名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "brand_name" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_1", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_1", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "brand_id", "keyLength": 0, "order": "", "oldName": "brand_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_13", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_13", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_attribute_category_id", "keyLength": 0, "order": "", "oldName": "product_attribute_category_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_5", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_5", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_category_id", "keyLength": 0, "order": "", "oldName": "product_category_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_6", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_6", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "feight_template_id", "keyLength": 0, "order": "", "oldName": "feight_template_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_1", "fields": [ "brand_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_brand", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_1" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_13", "fields": [ "product_attribute_category_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_attribute_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_13" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_5", "fields": [ "product_category_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_5" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_6", "fields": [ "feight_template_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_feight_template", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_6" } ], "triggers": [], "tablePartitions": [] } ], "views": [] } ] }, "diagrams": [ { "name": "商品模块数据库表模型", "paperWidth": 4, "paperHeight": 1, "tableFont": "Arial Unicode MS", "tableFontSize": 14, "isBalckWhite": false, "showDBSchemaName": false, "showViewRelations": true, "notation": "default", "showFieldComment": false, "showTableComment": false, "shapes": [ { "type": "table", "schemaName": "mall-ref", "tableName": "pms_album", "x": 460, "y": 940, "width": 187, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_album_pic", "x": 110, "y": 990, "width": 153, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_brand", "x": 1030, "y": 800, "width": 220, "height": 271, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_comment", "x": 70, "y": 630, "width": 233, "height": 351, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_comment_replay", "x": 40, "y": 310, "width": 233, "height": 191, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_feight_template", "x": 1270, "y": 700, "width": 218, "height": 211, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_member_price", "x": 380, "y": 750, "width": 237, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product", "x": 690, "y": 200, "width": 262, "height": 891, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_attribute", "x": 1430, "y": 410, "width": 262, "height": 291, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_attribute_category", "x": 1020, "y": 370, "width": 258, "height": 131, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_attribute_value", "x": 990, "y": 550, "width": 234, "height": 131, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_category", "x": 1070, "y": 20, "width": 189, "height": 291, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_category_attribute_relation", "x": 1330, "y": 90, "width": 321, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_full_reduction", "x": 740, "y": 10, "width": 224, "height": 131, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_ladder", "x": 470, "y": 10, "width": 175, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_operate_log", "x": 240, "y": 10, "width": 214, "height": 291, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_product_vertify_record", "x": 280, "y": 300, "width": 226, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "pms_sku_stock", "x": 380, "y": 470, "width": 218, "height": 271, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } } ], "layers": [], "relations": [ { "name": "FK_Reference_25", "sourceTableName": "pms_album_pic", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 278, "y": 1050 }, { "x": 361, "y": 1050 }, { "x": 361, "y": 1030 }, { "x": 445, "y": 1030 } ], "label": { "x": 110, "y": 10, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_24", "sourceTableName": "pms_comment_replay", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 160, "y": 516 }, { "x": 160, "y": 560 }, { "x": 130, "y": 560 }, { "x": 130, "y": 615 } ], "label": { "x": 168, "y": 270, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_23", "sourceTableName": "pms_comment", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 318, "y": 920 }, { "x": 675, "y": 920 } ], "label": { "x": 314, "y": 270, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_9", "sourceTableName": "pms_member_price", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 632, "y": 830 }, { "x": 660, "y": 830 }, { "x": 660, "y": 830 }, { "x": 675, "y": 830 } ], "label": { "x": 300, "y": 270, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_1", "sourceTableName": "pms_product", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 967, "y": 920 }, { "x": 1015, "y": 920 } ], "label": { "x": 963, "y": 200, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_6", "sourceTableName": "pms_product", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 967, "y": 820 }, { "x": 1020, "y": 820 }, { "x": 1020, "y": 790 }, { "x": 1255, "y": 790 } ], "label": { "x": 963, "y": 200, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_13", "sourceTableName": "pms_product", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 967, "y": 420 }, { "x": 1050, "y": 420 }, { "x": 1050, "y": 420 }, { "x": 1005, "y": 420 } ], "label": { "x": 963, "y": 200, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_12", "sourceTableName": "pms_product_attribute", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1415, "y": 540 }, { "x": 1360, "y": 540 }, { "x": 1360, "y": 420 }, { "x": 1293, "y": 420 } ], "label": { "x": 512, "y": 270, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_14", "sourceTableName": "pms_product_attribute_value", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 975, "y": 610 }, { "x": 967, "y": 610 } ], "label": { "x": 300, "y": 520, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_15", "sourceTableName": "pms_product_attribute_value", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1239, "y": 590 }, { "x": 1330, "y": 590 }, { "x": 1330, "y": 570 }, { "x": 1415, "y": 570 } ], "label": { "x": 300, "y": 446, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_5", "sourceTableName": "pms_product", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 967, "y": 230 }, { "x": 1040, "y": 230 }, { "x": 1040, "y": 150 }, { "x": 1055, "y": 150 } ], "label": { "x": 963, "y": 200, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_20", "sourceTableName": "pms_product_category", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1274, "y": 280 }, { "x": 1290, "y": 280 }, { "x": 1290, "y": 340 }, { "x": 1230, "y": 340 }, { "x": 1230, "y": 326 } ], "label": { "x": 300, "y": 20, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_21", "sourceTableName": "pms_product_category_attribute_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1315, "y": 150 }, { "x": 1320, "y": 150 }, { "x": 1320, "y": 150 }, { "x": 1274, "y": 150 } ], "label": { "x": 303, "y": 90, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_22", "sourceTableName": "pms_product_category_attribute_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 1490, "y": 216 }, { "x": 1490, "y": 400 }, { "x": 1560, "y": 400 }, { "x": 1560, "y": 395 } ], "label": { "x": 470, "y": 90, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_11", "sourceTableName": "pms_product_full_reduction", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 820, "y": 156 }, { "x": 820, "y": 200 }, { "x": 820, "y": 200 }, { "x": 820, "y": 185 } ], "label": { "x": 300, "y": 10, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_10", "sourceTableName": "pms_product_ladder", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 660, "y": 80 }, { "x": 690, "y": 80 }, { "x": 690, "y": 260 }, { "x": 675, "y": 260 } ], "label": { "x": 300, "y": 10, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_4", "sourceTableName": "pms_product_operate_log", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 469, "y": 220 }, { "x": 550, "y": 220 }, { "x": 550, "y": 390 }, { "x": 675, "y": 390 } ], "label": { "x": 240, "y": 10, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_3", "sourceTableName": "pms_product_vertify_record", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 521, "y": 380 }, { "x": 530, "y": 380 }, { "x": 530, "y": 430 }, { "x": 675, "y": 430 } ], "label": { "x": 280, "y": 270, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_2", "sourceTableName": "pms_sku_stock", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 613, "y": 620 }, { "x": 675, "y": 620 } ], "label": { "x": 300, "y": 270, "width": 114, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } } ], "viewRelations": [] } ] } ================================================ FILE: document/navicat/权限模块数据库模型.ndm2 ================================================ { "paper": { "name": "A4", "leftMargin": 0.5, "rightMargin": 0.5, "topMargin": 0.5, "bottomMargin": 0.5, "isPortriat": true }, "modelVersion": 2.01, "defaultSchema": "Default", "server": { "objectType": "Server_MYSQL", "name": "Default", "serverVersion": 50799, "edition": "Default", "lowerCaseTableNames": 0, "schemas": [ { "objectType": "Schema_MYSQL", "name": "Default", "tables": [], "views": [] }, { "objectType": "Schema_MYSQL", "name": "mall-ref", "tables": [ { "objectType": "Table_MYSQL", "name": "ums_admin", "comment": "后台用户表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_admin", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_admin` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `username` varchar(64) DEFAULT NULL COMMENT '用户名',\n `password` varchar(64) DEFAULT NULL COMMENT '密码',\n `icon` varchar(500) DEFAULT NULL COMMENT '头像',\n `email` varchar(100) DEFAULT NULL COMMENT '邮箱',\n `nick_name` varchar(200) DEFAULT NULL COMMENT '昵称',\n `note` varchar(500) DEFAULT NULL COMMENT '备注信息',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `login_time` datetime DEFAULT NULL COMMENT '最后登录时间',\n `status` int(1) DEFAULT '1' COMMENT '帐号启用状态:0->禁用;1->启用',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "username", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "用户名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "username" }, { "objectType": "TableField_MYSQL", "name": "password", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "密码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "password" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "头像", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "email", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "邮箱", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "email" }, { "objectType": "TableField_MYSQL", "name": "nick_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "昵称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "nick_name" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "备注信息", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "login_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "最后登录时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "login_time" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "1", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "帐号启用状态:0->禁用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_admin_login_log", "comment": "后台用户登录日志表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_admin_login_log", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_admin_login_log` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `admin_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `ip` varchar(64) DEFAULT NULL,\n `address` varchar(100) DEFAULT NULL,\n `user_agent` varchar(100) DEFAULT NULL COMMENT '浏览器登录类型',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_46` (`admin_id`),\n CONSTRAINT `FK_Reference_46` FOREIGN KEY (`admin_id`) REFERENCES `ums_admin` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户登录日志表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "admin_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "admin_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "ip", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "ip" }, { "objectType": "TableField_MYSQL", "name": "address", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "address" }, { "objectType": "TableField_MYSQL", "name": "user_agent", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "浏览器登录类型", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "user_agent" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_46", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_46", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "admin_id", "keyLength": 0, "order": "", "oldName": "admin_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_46", "fields": [ "admin_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_admin", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_46" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_admin_permission_relation", "comment": "后台用户和权限关系表(除角色中定义的权限以外的加减权限)", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_admin_permission_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_admin_permission_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `admin_id` bigint(20) DEFAULT NULL,\n `permission_id` bigint(20) DEFAULT NULL,\n `type` int(1) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_73` (`admin_id`),\n KEY `FK_Reference_74` (`permission_id`),\n CONSTRAINT `FK_Reference_73` FOREIGN KEY (`admin_id`) REFERENCES `ums_admin` (`id`),\n CONSTRAINT `FK_Reference_74` FOREIGN KEY (`permission_id`) REFERENCES `ums_permission` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户和权限关系表(除角色中定义的权限以外的加减权限)'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "admin_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "admin_id" }, { "objectType": "TableField_MYSQL", "name": "permission_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "permission_id" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_73", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_73", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "admin_id", "keyLength": 0, "order": "", "oldName": "admin_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_74", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_74", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "permission_id", "keyLength": 0, "order": "", "oldName": "permission_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_73", "fields": [ "admin_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_admin", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_73" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_74", "fields": [ "permission_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_permission", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_74" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_admin_role_relation", "comment": "后台用户和角色关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_admin_role_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_admin_role_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `admin_id` bigint(20) DEFAULT NULL,\n `role_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_69` (`admin_id`),\n KEY `FK_Reference_70` (`role_id`),\n CONSTRAINT `FK_Reference_69` FOREIGN KEY (`admin_id`) REFERENCES `ums_admin` (`id`),\n CONSTRAINT `FK_Reference_70` FOREIGN KEY (`role_id`) REFERENCES `ums_role` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户和角色关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "admin_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "admin_id" }, { "objectType": "TableField_MYSQL", "name": "role_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "role_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_69", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_69", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "admin_id", "keyLength": 0, "order": "", "oldName": "admin_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_70", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_70", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "role_id", "keyLength": 0, "order": "", "oldName": "role_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_69", "fields": [ "admin_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_admin", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_69" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_70", "fields": [ "role_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_role", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_70" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_growth_change_history", "comment": "成长值变化历史记录表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_growth_change_history", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_growth_change_history` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `change_type` int(1) DEFAULT NULL COMMENT '改变类型:0->增加;1->减少',\n `change_count` int(11) DEFAULT NULL COMMENT '积分改变数量',\n `operate_man` varchar(100) DEFAULT NULL COMMENT '操作人员',\n `operate_note` varchar(200) DEFAULT NULL COMMENT '操作备注',\n `source_type` int(1) DEFAULT NULL COMMENT '积分来源:0->购物;1->管理员修改',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_56` (`member_id`),\n CONSTRAINT `FK_Reference_56` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='成长值变化历史记录表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "change_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "改变类型:0->增加;1->减少", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "change_type" }, { "objectType": "TableField_MYSQL", "name": "change_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分改变数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "change_count" }, { "objectType": "TableField_MYSQL", "name": "operate_man", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作人员", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_man" }, { "objectType": "TableField_MYSQL", "name": "operate_note", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_note" }, { "objectType": "TableField_MYSQL", "name": "source_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分来源:0->购物;1->管理员修改", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "source_type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_56", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_56", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_56", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_56" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_integration_change_history", "comment": "积分变化历史记录表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_integration_change_history", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_integration_change_history` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `create_time` datetime DEFAULT NULL,\n `change_type` int(1) DEFAULT NULL COMMENT '改变类型:0->增加;1->减少',\n `change_count` int(11) DEFAULT NULL COMMENT '积分改变数量',\n `operate_man` varchar(100) DEFAULT NULL COMMENT '操作人员',\n `operate_note` varchar(200) DEFAULT NULL COMMENT '操作备注',\n `source_type` int(1) DEFAULT NULL COMMENT '积分来源:0->购物;1->管理员修改',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_55` (`member_id`),\n CONSTRAINT `FK_Reference_55` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='积分变化历史记录表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "change_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "改变类型:0->增加;1->减少", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "change_type" }, { "objectType": "TableField_MYSQL", "name": "change_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分改变数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "change_count" }, { "objectType": "TableField_MYSQL", "name": "operate_man", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作人员", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_man" }, { "objectType": "TableField_MYSQL", "name": "operate_note", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_note" }, { "objectType": "TableField_MYSQL", "name": "source_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分来源:0->购物;1->管理员修改", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "source_type" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_55", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_55", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_55", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_55" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_integration_consume_setting", "comment": "积分消费设置", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_integration_consume_setting", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_integration_consume_setting` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `deduction_per_amount` int(11) DEFAULT NULL COMMENT '每一元需要抵扣的积分数量',\n `max_percent_per_order` int(11) DEFAULT NULL COMMENT '每笔订单最高抵用百分比',\n `use_unit` int(11) DEFAULT NULL COMMENT '每次使用积分最小单位100',\n `coupon_status` int(1) DEFAULT NULL COMMENT '是否可以和优惠券同用;0->不可以;1->可以',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='积分消费设置'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "deduction_per_amount", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每一元需要抵扣的积分数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "deduction_per_amount" }, { "objectType": "TableField_MYSQL", "name": "max_percent_per_order", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每笔订单最高抵用百分比", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "max_percent_per_order" }, { "objectType": "TableField_MYSQL", "name": "use_unit", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每次使用积分最小单位100", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_unit" }, { "objectType": "TableField_MYSQL", "name": "coupon_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否可以和优惠券同用;0->不可以;1->可以", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_status" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_menu", "comment": "后台菜单表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_menu", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_menu` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `parent_id` bigint(20) DEFAULT NULL COMMENT '父级ID',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `title` varchar(100) DEFAULT NULL COMMENT '菜单名称',\n `level` int(4) DEFAULT NULL COMMENT '菜单级数',\n `sort` int(4) DEFAULT NULL COMMENT '菜单排序',\n `name` varchar(100) DEFAULT NULL COMMENT '前端名称',\n `icon` varchar(200) DEFAULT NULL COMMENT '前端图标',\n `hidden` int(1) DEFAULT NULL COMMENT '前端隐藏',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_80` (`parent_id`),\n CONSTRAINT `FK_Reference_80` FOREIGN KEY (`parent_id`) REFERENCES `ums_menu` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台菜单表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "parent_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "父级ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "parent_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "title", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "菜单名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "title" }, { "objectType": "TableField_MYSQL", "name": "level", "type": "int", "length": 4, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "菜单级数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "level" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 4, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "菜单排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "前端名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "前端图标", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "hidden", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "前端隐藏", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "hidden" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_80", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_80", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "parent_id", "keyLength": 0, "order": "", "oldName": "parent_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_80", "fields": [ "parent_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_menu", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_80" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_permission", "comment": "后台用户权限表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_permission", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_permission` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `pid` bigint(20) DEFAULT NULL COMMENT '父级权限id',\n `name` varchar(100) DEFAULT NULL COMMENT '名称',\n `value` varchar(200) DEFAULT NULL COMMENT '权限值',\n `icon` varchar(500) DEFAULT NULL COMMENT '图标',\n `type` int(1) DEFAULT NULL COMMENT '权限类型:0->目录;1->菜单;2->按钮(接口绑定权限)',\n `uri` varchar(200) DEFAULT NULL COMMENT '前端资源路径',\n `status` int(1) DEFAULT NULL COMMENT '启用状态;0->禁用;1->启用',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `sort` int(11) DEFAULT NULL COMMENT '排序',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_68` (`pid`),\n CONSTRAINT `FK_Reference_68` FOREIGN KEY (`pid`) REFERENCES `ums_permission` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户权限表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "pid", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "父级权限id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pid" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "value", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "权限值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "value" }, { "objectType": "TableField_MYSQL", "name": "icon", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "图标", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "icon" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "权限类型:0->目录;1->菜单;2->按钮(接口绑定权限)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" }, { "objectType": "TableField_MYSQL", "name": "uri", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "前端资源路径", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "uri" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "启用状态;0->禁用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_68", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_68", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "pid", "keyLength": 0, "order": "", "oldName": "pid" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_68", "fields": [ "pid" ], "referenceSchema": "mall-ref", "referenceTable": "ums_permission", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_68" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_resource", "comment": "后台资源表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_resource", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_resource` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `category_id` bigint(20) DEFAULT NULL COMMENT '资源分类ID',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `name` varchar(200) DEFAULT NULL COMMENT '资源名称',\n `url` varchar(200) DEFAULT NULL COMMENT '资源URL',\n `description` varchar(500) DEFAULT NULL COMMENT '描述',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_85` (`category_id`),\n CONSTRAINT `FK_Reference_85` FOREIGN KEY (`category_id`) REFERENCES `ums_resource_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台资源表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "资源分类ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "category_id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "资源名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "url", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "资源URL", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "url" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "描述", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_85", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_85", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "category_id", "keyLength": 0, "order": "", "oldName": "category_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_85", "fields": [ "category_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_resource_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_85" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_resource_category", "comment": "资源分类表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_resource_category", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_resource_category` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `name` varchar(200) DEFAULT NULL COMMENT '分类名称',\n `sort` int(4) DEFAULT NULL COMMENT '排序',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='资源分类表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "分类名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 4, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_role", "comment": "后台用户角色表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_role", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_role` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL COMMENT '名称',\n `description` varchar(500) DEFAULT NULL COMMENT '描述',\n `admin_count` int(11) DEFAULT NULL COMMENT '后台用户数量',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n `status` int(1) DEFAULT '1' COMMENT '启用状态:0->禁用;1->启用',\n `sort` int(11) DEFAULT '0',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户角色表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "描述", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" }, { "objectType": "TableField_MYSQL", "name": "admin_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "后台用户数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "admin_count" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "1", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "启用状态:0->禁用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_role_menu_relation", "comment": "后台角色菜单关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_role_menu_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_role_menu_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',\n `menu_id` bigint(20) DEFAULT NULL COMMENT '菜单ID',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_81` (`role_id`),\n KEY `FK_Reference_82` (`menu_id`),\n CONSTRAINT `FK_Reference_81` FOREIGN KEY (`role_id`) REFERENCES `ums_role` (`id`),\n CONSTRAINT `FK_Reference_82` FOREIGN KEY (`menu_id`) REFERENCES `ums_menu` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台角色菜单关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "role_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "角色ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "role_id" }, { "objectType": "TableField_MYSQL", "name": "menu_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "菜单ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "menu_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_81", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_81", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "role_id", "keyLength": 0, "order": "", "oldName": "role_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_82", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_82", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "menu_id", "keyLength": 0, "order": "", "oldName": "menu_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_81", "fields": [ "role_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_role", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_81" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_82", "fields": [ "menu_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_menu", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_82" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_role_permission_relation", "comment": "后台用户角色和权限关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_role_permission_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_role_permission_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `role_id` bigint(20) DEFAULT NULL,\n `permission_id` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_71` (`role_id`),\n KEY `FK_Reference_72` (`permission_id`),\n CONSTRAINT `FK_Reference_71` FOREIGN KEY (`role_id`) REFERENCES `ums_role` (`id`),\n CONSTRAINT `FK_Reference_72` FOREIGN KEY (`permission_id`) REFERENCES `ums_permission` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台用户角色和权限关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "role_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "role_id" }, { "objectType": "TableField_MYSQL", "name": "permission_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "permission_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_71", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_71", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "role_id", "keyLength": 0, "order": "", "oldName": "role_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_72", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_72", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "permission_id", "keyLength": 0, "order": "", "oldName": "permission_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_71", "fields": [ "role_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_role", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_71" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_72", "fields": [ "permission_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_permission", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_72" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "ums_role_resource_relation", "comment": "后台角色资源关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "ums_role_resource_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `ums_role_resource_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',\n `resource_id` bigint(20) DEFAULT NULL COMMENT '资源ID',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_83` (`role_id`),\n KEY `FK_Reference_84` (`resource_id`),\n CONSTRAINT `FK_Reference_83` FOREIGN KEY (`role_id`) REFERENCES `ums_role` (`id`),\n CONSTRAINT `FK_Reference_84` FOREIGN KEY (`resource_id`) REFERENCES `ums_resource` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台角色资源关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "role_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "角色ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "role_id" }, { "objectType": "TableField_MYSQL", "name": "resource_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "资源ID", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "resource_id" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_83", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_83", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "role_id", "keyLength": 0, "order": "", "oldName": "role_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_84", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_84", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "resource_id", "keyLength": 0, "order": "", "oldName": "resource_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_83", "fields": [ "role_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_role", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_83" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_84", "fields": [ "resource_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_resource", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_84" } ], "triggers": [], "tablePartitions": [] } ], "views": [] } ] }, "diagrams": [ { "name": "权限模块数据库表模型", "paperWidth": 2, "paperHeight": 1, "tableFont": "Arial Unicode MS", "tableFontSize": 14, "isBalckWhite": false, "showDBSchemaName": false, "showViewRelations": true, "notation": "default", "showFieldComment": false, "showTableComment": false, "shapes": [ { "type": "table", "schemaName": "mall-ref", "tableName": "ums_admin", "x": 50, "y": 220, "width": 180, "height": 251, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_admin_login_log", "x": 50, "y": 10, "width": 181, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_admin_role_relation", "x": 10, "y": 540, "width": 205, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_menu", "x": 540, "y": 450, "width": 162, "height": 231, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_resource", "x": 510, "y": 270, "width": 180, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_resource_category", "x": 530, "y": 40, "width": 198, "height": 131, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_role", "x": 290, "y": 230, "width": 180, "height": 191, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_role_menu_relation", "x": 290, "y": 450, "width": 201, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "ums_role_resource_relation", "x": 260, "y": 40, "width": 226, "height": 111, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } } ], "layers": [], "relations": [ { "name": "FK_Reference_46", "sourceTableName": "ums_admin_login_log", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 120, "y": 196 }, { "x": 120, "y": 205 } ], "label": { "x": 128, "y": 94, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_69", "sourceTableName": "ums_admin_role_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 120, "y": 525 }, { "x": 120, "y": 520 }, { "x": 120, "y": 520 }, { "x": 120, "y": 486 } ], "label": { "x": 128, "y": 223, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_80", "sourceTableName": "ums_menu", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 717, "y": 650 }, { "x": 730, "y": 650 }, { "x": 730, "y": 710 }, { "x": 670, "y": 710 }, { "x": 670, "y": 696 } ], "label": { "x": 480, "y": 270, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_85", "sourceTableName": "ums_resource", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 590, "y": 255 }, { "x": 590, "y": 231 }, { "x": 590, "y": 186 } ], "label": { "x": 548, "y": 70, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_70", "sourceTableName": "ums_admin_role_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 230, "y": 590 }, { "x": 265, "y": 590 }, { "x": 265, "y": 360 }, { "x": 275, "y": 360 } ], "label": { "x": 176, "y": 280, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_81", "sourceTableName": "ums_role_menu_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 390, "y": 435 }, { "x": 390, "y": 435 }, { "x": 390, "y": 435 }, { "x": 390, "y": 436 } ], "label": { "x": 377, "y": 450, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_82", "sourceTableName": "ums_role_menu_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 506, "y": 520 }, { "x": 525, "y": 520 }, { "x": 525, "y": 520 }, { "x": 525, "y": 520 } ], "label": { "x": 270, "y": 406, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_83", "sourceTableName": "ums_role_resource_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 350, "y": 166 }, { "x": 350, "y": 200 }, { "x": 350, "y": 200 }, { "x": 350, "y": 215 } ], "label": { "x": 308, "y": 20, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_84", "sourceTableName": "ums_role_resource_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 501, "y": 100 }, { "x": 503, "y": 100 }, { "x": 503, "y": 290 }, { "x": 495, "y": 290 } ], "label": { "x": 447, "y": 87, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } } ], "viewRelations": [] } ] } ================================================ FILE: document/navicat/营销模块数据库模型.ndm2 ================================================ { "paper": { "name": "A4", "leftMargin": 0.5, "rightMargin": 0.5, "topMargin": 0.5, "bottomMargin": 0.5, "isPortriat": true }, "modelVersion": 2.01, "defaultSchema": "Default", "server": { "objectType": "Server_MYSQL", "name": "Default", "serverVersion": 50799, "edition": "Default", "lowerCaseTableNames": 0, "schemas": [ { "objectType": "Schema_MYSQL", "name": "Default", "tables": [], "views": [] }, { "objectType": "Schema_MYSQL", "name": "mall-ref", "tables": [ { "objectType": "Table_MYSQL", "name": "sms_coupon", "comment": "优惠卷表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_coupon", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_coupon` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `type` int(1) DEFAULT NULL COMMENT '优惠卷类型;0->全场赠券;1->会员赠券;2->购物赠券;3->注册赠券',\n `name` varchar(100) DEFAULT NULL,\n `platform` int(1) DEFAULT NULL COMMENT '使用平台:0->全部;1->移动;2->PC',\n `count` int(11) DEFAULT NULL COMMENT '数量',\n `amount` decimal(10,2) DEFAULT NULL COMMENT '金额',\n `per_limit` int(11) DEFAULT NULL COMMENT '每人限领张数',\n `min_point` decimal(10,2) DEFAULT NULL COMMENT '使用门槛;0表示无门槛',\n `start_time` datetime DEFAULT NULL,\n `end_time` datetime DEFAULT NULL,\n `use_type` int(1) DEFAULT NULL COMMENT '使用类型:0->全场通用;1->指定分类;2->指定商品',\n `note` varchar(200) DEFAULT NULL COMMENT '备注',\n `publish_count` int(11) DEFAULT NULL COMMENT '发行数量',\n `use_count` int(11) DEFAULT NULL COMMENT '已使用数量',\n `receive_count` int(11) DEFAULT NULL COMMENT '领取数量',\n `enable_time` datetime DEFAULT NULL COMMENT '可以领取的日期',\n `code` varchar(64) DEFAULT NULL COMMENT '优惠码',\n `member_level` int(1) DEFAULT NULL COMMENT '可领取的会员类型:0->无限时',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='优惠卷表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "优惠卷类型;0->全场赠券;1->会员赠券;2->购物赠券;3->注册赠券", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "platform", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "使用平台:0->全部;1->移动;2->PC", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "platform" }, { "objectType": "TableField_MYSQL", "name": "count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "count" }, { "objectType": "TableField_MYSQL", "name": "amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "amount" }, { "objectType": "TableField_MYSQL", "name": "per_limit", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每人限领张数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "per_limit" }, { "objectType": "TableField_MYSQL", "name": "min_point", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "使用门槛;0表示无门槛", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "min_point" }, { "objectType": "TableField_MYSQL", "name": "start_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "start_time" }, { "objectType": "TableField_MYSQL", "name": "end_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "end_time" }, { "objectType": "TableField_MYSQL", "name": "use_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "使用类型:0->全场通用;1->指定分类;2->指定商品", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_type" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" }, { "objectType": "TableField_MYSQL", "name": "publish_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "发行数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "publish_count" }, { "objectType": "TableField_MYSQL", "name": "use_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "已使用数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_count" }, { "objectType": "TableField_MYSQL", "name": "receive_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "领取数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receive_count" }, { "objectType": "TableField_MYSQL", "name": "enable_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "可以领取的日期", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "enable_time" }, { "objectType": "TableField_MYSQL", "name": "code", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "优惠码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "code" }, { "objectType": "TableField_MYSQL", "name": "member_level", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "可领取的会员类型:0->无限时", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_level" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_coupon_history", "comment": "优惠券使用、领取历史表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_coupon_history", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 49152, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_coupon_history` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `coupon_id` bigint(20) DEFAULT NULL,\n `member_id` bigint(20) DEFAULT NULL,\n `order_id` bigint(20) DEFAULT NULL COMMENT '订单id',\n `coupon_code` varchar(64) DEFAULT NULL,\n `member_nickname` varchar(64) DEFAULT NULL COMMENT '领取人昵称',\n `get_type` int(1) DEFAULT NULL COMMENT '获取类型:0->后台赠送;1->主动获取',\n `create_time` datetime DEFAULT NULL,\n `use_status` int(1) DEFAULT NULL COMMENT '使用状态:0->未使用;1->已使用;2->已过期',\n `use_time` datetime DEFAULT NULL COMMENT '使用时间',\n `order_sn` varchar(100) DEFAULT NULL COMMENT '订单号码',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_37` (`coupon_id`),\n KEY `FK_Reference_38` (`member_id`),\n KEY `FK_Reference_76` (`order_id`),\n CONSTRAINT `FK_Reference_37` FOREIGN KEY (`coupon_id`) REFERENCES `sms_coupon` (`id`),\n CONSTRAINT `FK_Reference_38` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`),\n CONSTRAINT `FK_Reference_76` FOREIGN KEY (`order_id`) REFERENCES `oms_order` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='优惠券使用、领取历史表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "coupon_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "order_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_id" }, { "objectType": "TableField_MYSQL", "name": "coupon_code", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_code" }, { "objectType": "TableField_MYSQL", "name": "member_nickname", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "领取人昵称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_nickname" }, { "objectType": "TableField_MYSQL", "name": "get_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "获取类型:0->后台赠送;1->主动获取", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "get_type" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "use_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "使用状态:0->未使用;1->已使用;2->已过期", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_status" }, { "objectType": "TableField_MYSQL", "name": "use_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "使用时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_time" }, { "objectType": "TableField_MYSQL", "name": "order_sn", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单号码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_sn" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_37", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_37", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "coupon_id", "keyLength": 0, "order": "", "oldName": "coupon_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_38", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_38", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_76", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_76", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "order_id", "keyLength": 0, "order": "", "oldName": "order_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_37", "fields": [ "coupon_id" ], "referenceSchema": "mall-ref", "referenceTable": "sms_coupon", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_37" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_38", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_38" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_76", "fields": [ "order_id" ], "referenceSchema": "mall-ref", "referenceTable": "oms_order", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_76" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_coupon_product_category_relation", "comment": "优惠券和产品分类关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_coupon_product_category_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_coupon_product_category_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `coupon_id` bigint(20) DEFAULT NULL,\n `product_category_id` bigint(20) DEFAULT NULL,\n `product_category_name` varchar(200) DEFAULT NULL COMMENT '产品分类名称',\n `parent_category_name` varchar(200) DEFAULT NULL COMMENT '父分类名称',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_35` (`coupon_id`),\n KEY `FK_Reference_36` (`product_category_id`),\n CONSTRAINT `FK_Reference_35` FOREIGN KEY (`coupon_id`) REFERENCES `sms_coupon` (`id`),\n CONSTRAINT `FK_Reference_36` FOREIGN KEY (`product_category_id`) REFERENCES `pms_product_category` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='优惠券和产品分类关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "coupon_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_id" }, { "objectType": "TableField_MYSQL", "name": "product_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_id" }, { "objectType": "TableField_MYSQL", "name": "product_category_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "产品分类名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_name" }, { "objectType": "TableField_MYSQL", "name": "parent_category_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "父分类名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "parent_category_name" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_35", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_35", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "coupon_id", "keyLength": 0, "order": "", "oldName": "coupon_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_36", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_36", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_category_id", "keyLength": 0, "order": "", "oldName": "product_category_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_35", "fields": [ "coupon_id" ], "referenceSchema": "mall-ref", "referenceTable": "sms_coupon", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_35" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_36", "fields": [ "product_category_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product_category", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_36" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_coupon_product_relation", "comment": "优惠券和产品的关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_coupon_product_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_coupon_product_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `coupon_id` bigint(20) DEFAULT NULL,\n `product_id` bigint(20) DEFAULT NULL,\n `product_name` varchar(500) DEFAULT NULL COMMENT '商品名称',\n `product_sn` varchar(200) DEFAULT NULL COMMENT '商品编码',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_33` (`coupon_id`),\n KEY `FK_Reference_34` (`product_id`),\n CONSTRAINT `FK_Reference_33` FOREIGN KEY (`coupon_id`) REFERENCES `sms_coupon` (`id`),\n CONSTRAINT `FK_Reference_34` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='优惠券和产品的关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "coupon_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "product_sn", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品编码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sn" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_33", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_33", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "coupon_id", "keyLength": 0, "order": "", "oldName": "coupon_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_34", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_34", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_33", "fields": [ "coupon_id" ], "referenceSchema": "mall-ref", "referenceTable": "sms_coupon", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_33" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_34", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_34" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_flash_promotion", "comment": "限时购表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_flash_promotion", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_flash_promotion` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `title` varchar(200) DEFAULT NULL,\n `start_date` date DEFAULT NULL COMMENT '开始日期',\n `end_date` date DEFAULT NULL COMMENT '结束日期',\n `status` int(1) DEFAULT NULL COMMENT '上下线状态',\n `create_time` datetime DEFAULT NULL COMMENT '秒杀时间段名称',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='限时购表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "title", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "title" }, { "objectType": "TableField_MYSQL", "name": "start_date", "type": "date", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "开始日期", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "start_date" }, { "objectType": "TableField_MYSQL", "name": "end_date", "type": "date", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "结束日期", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "end_date" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "上下线状态", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "秒杀时间段名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_flash_promotion_log", "comment": "限时购通知记录", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_flash_promotion_log", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_flash_promotion_log` (\n `id` int(11) NOT NULL AUTO_INCREMENT,\n `member_id` bigint(20) DEFAULT NULL,\n `product_id` bigint(20) DEFAULT NULL,\n `member_phone` varchar(64) DEFAULT NULL,\n `product_name` varchar(100) DEFAULT NULL,\n `subscribe_time` datetime DEFAULT NULL COMMENT '会员订阅时间',\n `send_time` datetime DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_44` (`member_id`),\n KEY `FK_Reference_45` (`product_id`),\n CONSTRAINT `FK_Reference_44` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`),\n CONSTRAINT `FK_Reference_45` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='限时购通知记录'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "member_phone", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_phone" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "subscribe_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "会员订阅时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "subscribe_time" }, { "objectType": "TableField_MYSQL", "name": "send_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "send_time" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_44", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_44", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_45", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_45", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_44", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_44" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_45", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_45" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_flash_promotion_product_relation", "comment": "商品限时购与商品关系表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_flash_promotion_product_relation", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 49152, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_flash_promotion_product_relation` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',\n `flash_promotion_id` bigint(20) DEFAULT NULL,\n `flash_promotion_session_id` bigint(20) DEFAULT NULL COMMENT '编号',\n `product_id` bigint(20) DEFAULT NULL,\n `flash_promotion_price` decimal(10,2) DEFAULT NULL COMMENT '限时购价格',\n `flash_promotion_count` int(11) DEFAULT NULL COMMENT '限时购数量',\n `flash_promotion_limit` int(11) DEFAULT NULL COMMENT '每人限购数量',\n `sort` int(11) DEFAULT NULL COMMENT '排序',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_77` (`flash_promotion_id`),\n KEY `FK_Reference_78` (`flash_promotion_session_id`),\n KEY `FK_Reference_79` (`product_id`),\n CONSTRAINT `FK_Reference_77` FOREIGN KEY (`flash_promotion_id`) REFERENCES `sms_flash_promotion` (`id`),\n CONSTRAINT `FK_Reference_78` FOREIGN KEY (`flash_promotion_session_id`) REFERENCES `sms_flash_promotion_session` (`id`),\n CONSTRAINT `FK_Reference_79` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品限时购与商品关系表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "flash_promotion_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "flash_promotion_id" }, { "objectType": "TableField_MYSQL", "name": "flash_promotion_session_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "flash_promotion_session_id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "flash_promotion_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "限时购价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "flash_promotion_price" }, { "objectType": "TableField_MYSQL", "name": "flash_promotion_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "限时购数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "flash_promotion_count" }, { "objectType": "TableField_MYSQL", "name": "flash_promotion_limit", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每人限购数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "flash_promotion_limit" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_77", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_77", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "flash_promotion_id", "keyLength": 0, "order": "", "oldName": "flash_promotion_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_78", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_78", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "flash_promotion_session_id", "keyLength": 0, "order": "", "oldName": "flash_promotion_session_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_79", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_79", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_77", "fields": [ "flash_promotion_id" ], "referenceSchema": "mall-ref", "referenceTable": "sms_flash_promotion", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_77" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_78", "fields": [ "flash_promotion_session_id" ], "referenceSchema": "mall-ref", "referenceTable": "sms_flash_promotion_session", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_78" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_79", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_79" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_flash_promotion_session", "comment": "限时购场次表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_flash_promotion_session", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_flash_promotion_session` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',\n `name` varchar(200) DEFAULT NULL COMMENT '场次名称',\n `start_time` time DEFAULT NULL COMMENT '每日开始时间',\n `end_time` time DEFAULT NULL COMMENT '每日结束时间',\n `status` int(1) DEFAULT NULL COMMENT '启用状态:0->不启用;1->启用',\n `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='限时购场次表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "场次名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "start_time", "type": "time", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每日开始时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "start_time" }, { "objectType": "TableField_MYSQL", "name": "end_time", "type": "time", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "每日结束时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "end_time" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "启用状态:0->不启用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_home_advertise", "comment": "首页轮播广告表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_home_advertise", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_home_advertise` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL,\n `type` int(1) DEFAULT NULL COMMENT '轮播位置:0->PC首页轮播;1->app首页轮播',\n `pic` varchar(500) DEFAULT NULL,\n `start_time` datetime DEFAULT NULL,\n `end_time` datetime DEFAULT NULL,\n `status` int(1) DEFAULT NULL COMMENT '上下线状态:0->下线;1->上线',\n `click_count` int(11) DEFAULT NULL COMMENT '点击数',\n `order_count` int(11) DEFAULT NULL COMMENT '下单数',\n `url` varchar(500) DEFAULT NULL COMMENT '链接地址',\n `note` varchar(500) DEFAULT NULL COMMENT '备注',\n `sort` int(11) DEFAULT '0' COMMENT '排序',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='首页轮播广告表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "轮播位置:0->PC首页轮播;1->app首页轮播", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "type" }, { "objectType": "TableField_MYSQL", "name": "pic", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pic" }, { "objectType": "TableField_MYSQL", "name": "start_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "start_time" }, { "objectType": "TableField_MYSQL", "name": "end_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "end_time" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "上下线状态:0->下线;1->上线", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "click_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "点击数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "click_count" }, { "objectType": "TableField_MYSQL", "name": "order_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "下单数", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_count" }, { "objectType": "TableField_MYSQL", "name": "url", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "链接地址", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "url" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "排序", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_home_brand", "comment": "首页推荐品牌表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_home_brand", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_home_brand` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `brand_id` bigint(20) DEFAULT NULL,\n `brand_name` varchar(64) DEFAULT NULL,\n `recommend_status` int(1) DEFAULT NULL,\n `sort` int(11) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_39` (`brand_id`),\n CONSTRAINT `FK_Reference_39` FOREIGN KEY (`brand_id`) REFERENCES `pms_brand` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='首页推荐品牌表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "brand_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "brand_id" }, { "objectType": "TableField_MYSQL", "name": "brand_name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "brand_name" }, { "objectType": "TableField_MYSQL", "name": "recommend_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recommend_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_39", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_39", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "brand_id", "keyLength": 0, "order": "", "oldName": "brand_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_39", "fields": [ "brand_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_brand", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_39" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_home_new_product", "comment": "新鲜好物表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_home_new_product", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_home_new_product` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `product_name` varchar(64) DEFAULT NULL,\n `recommend_status` int(1) DEFAULT NULL,\n `sort` int(1) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_40` (`product_id`),\n CONSTRAINT `FK_Reference_40` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='新鲜好物表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "recommend_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recommend_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_40", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_40", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_40", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_40" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_home_recommend_product", "comment": "人气推荐商品表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_home_recommend_product", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_home_recommend_product` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `product_name` varchar(64) DEFAULT NULL,\n `recommend_status` int(1) DEFAULT NULL,\n `sort` int(1) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_41` (`product_id`),\n CONSTRAINT `FK_Reference_41` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='人气推荐商品表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "recommend_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recommend_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_41", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_41", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_41", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_41" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "sms_home_recommend_subject", "comment": "首页推荐专题表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "sms_home_recommend_subject", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `sms_home_recommend_subject` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `subject_id` bigint(20) DEFAULT NULL,\n `subject_name` varchar(64) DEFAULT NULL,\n `recommend_status` int(1) DEFAULT NULL,\n `sort` int(11) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_Reference_42` (`subject_id`),\n CONSTRAINT `FK_Reference_42` FOREIGN KEY (`subject_id`) REFERENCES `cms_subject` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='首页推荐专题表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "subject_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "subject_id" }, { "objectType": "TableField_MYSQL", "name": "subject_name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "subject_name" }, { "objectType": "TableField_MYSQL", "name": "recommend_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "recommend_status" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_42", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_42", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "subject_id", "keyLength": 0, "order": "", "oldName": "subject_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_42", "fields": [ "subject_id" ], "referenceSchema": "mall-ref", "referenceTable": "cms_subject", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_42" } ], "triggers": [], "tablePartitions": [] } ], "views": [] } ] }, "diagrams": [ { "name": "营销模块数据库模型", "paperWidth": 2, "paperHeight": 1, "tableFont": "Arial Unicode MS", "tableFontSize": 14, "isBalckWhite": false, "showDBSchemaName": false, "showViewRelations": true, "notation": "default", "showFieldComment": false, "showTableComment": false, "shapes": [ { "type": "table", "schemaName": "mall-ref", "tableName": "sms_coupon", "x": 310, "y": 220, "width": 183, "height": 411, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_coupon_history", "x": 10, "y": 40, "width": 220, "height": 271, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_coupon_product_category_relation", "x": 250, "y": 0, "width": 312, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_coupon_product_relation", "x": 10, "y": 390, "width": 241, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_flash_promotion", "x": 670, "y": 670, "width": 177, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_flash_promotion_log", "x": 820, "y": 400, "width": 207, "height": 191, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_flash_promotion_product_relation", "x": 320, "y": 650, "width": 303, "height": 211, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_flash_promotion_session", "x": 10, "y": 670, "width": 240, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_home_advertise", "x": 600, "y": 350, "width": 176, "height": 291, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_home_brand", "x": 880, "y": 190, "width": 183, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_home_new_product", "x": 590, "y": 190, "width": 201, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_home_recommend_product", "x": 840, "y": 20, "width": 255, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "sms_home_recommend_subject", "x": 580, "y": 20, "width": 252, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } } ], "layers": [], "relations": [ { "name": "FK_Reference_37", "sourceTableName": "sms_coupon_history", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 245, "y": 170 }, { "x": 290, "y": 170 }, { "x": 290, "y": 420 }, { "x": 295, "y": 420 } ], "label": { "x": 0, "y": 0, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_35", "sourceTableName": "sms_coupon_product_category_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 420, "y": 166 }, { "x": 420, "y": 210 }, { "x": 420, "y": 210 }, { "x": 420, "y": 205 } ], "label": { "x": 250, "y": 0, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_33", "sourceTableName": "sms_coupon_product_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 266, "y": 470 }, { "x": 300, "y": 470 }, { "x": 300, "y": 470 }, { "x": 295, "y": 470 } ], "label": { "x": 261, "y": 356, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_77", "sourceTableName": "sms_flash_promotion_product_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 638, "y": 750 }, { "x": 631, "y": 750 }, { "x": 631, "y": 750 }, { "x": 655, "y": 750 } ], "label": { "x": 10, "y": 0, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_78", "sourceTableName": "sms_flash_promotion_product_relation", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 305, "y": 750 }, { "x": 305, "y": 750 }, { "x": 305, "y": 750 }, { "x": 265, "y": 750 } ], "label": { "x": 10, "y": 0, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } } ], "viewRelations": [] } ] } ================================================ FILE: document/navicat/订单模块数据库模型.ndm2 ================================================ { "paper": { "name": "A4", "leftMargin": 0.5, "rightMargin": 0.5, "topMargin": 0.5, "bottomMargin": 0.5, "isPortriat": true }, "modelVersion": 2.01, "defaultSchema": "Default", "server": { "objectType": "Server_MYSQL", "name": "Default", "serverVersion": 50799, "edition": "Default", "lowerCaseTableNames": 0, "schemas": [ { "objectType": "Schema_MYSQL", "name": "Default", "tables": [], "views": [] }, { "objectType": "Schema_MYSQL", "name": "mall-ref", "tables": [ { "objectType": "Table_MYSQL", "name": "oms_cart_item", "comment": "购物车表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_cart_item", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 49152, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_cart_item` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `product_id` bigint(20) DEFAULT NULL,\n `product_sku_id` bigint(20) DEFAULT NULL,\n `member_id` bigint(20) DEFAULT NULL,\n `quantity` int(11) DEFAULT NULL COMMENT '购买数量',\n `price` decimal(10,2) DEFAULT NULL COMMENT '添加到购物车的价格',\n `product_pic` varchar(1000) DEFAULT NULL COMMENT '商品主图',\n `product_name` varchar(500) DEFAULT NULL COMMENT '商品名称',\n `product_brand` varchar(200) DEFAULT NULL,\n `product_sn` varchar(200) DEFAULT NULL,\n `product_sub_title` varchar(500) DEFAULT NULL COMMENT '商品副标题(卖点)',\n `product_sku_code` varchar(200) DEFAULT NULL COMMENT '商品sku条码',\n `member_nickname` varchar(500) DEFAULT NULL COMMENT '会员昵称',\n `create_date` datetime DEFAULT NULL COMMENT '创建时间',\n `modify_date` datetime DEFAULT NULL COMMENT '修改时间',\n `delete_status` int(1) DEFAULT '0' COMMENT '是否删除',\n `product_category_id` bigint(20) DEFAULT NULL COMMENT '商品的分类',\n `product_attr` varchar(500) DEFAULT NULL COMMENT '商品销售属性:[{\"key\":\"颜色\",\"value\":\"银色\"},{\"key\":\"容量\",\"value\":\"4G\"}]',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_65` (`product_id`),\n KEY `FK_Reference_66` (`product_sku_id`),\n KEY `FK_Reference_67` (`member_id`),\n CONSTRAINT `FK_Reference_65` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`),\n CONSTRAINT `FK_Reference_66` FOREIGN KEY (`product_sku_id`) REFERENCES `pms_sku_stock` (`id`),\n CONSTRAINT `FK_Reference_67` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='购物车表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "product_sku_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sku_id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "quantity", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "购买数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "quantity" }, { "objectType": "TableField_MYSQL", "name": "price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "添加到购物车的价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "price" }, { "objectType": "TableField_MYSQL", "name": "product_pic", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品主图", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_pic" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "product_brand", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_brand" }, { "objectType": "TableField_MYSQL", "name": "product_sn", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sn" }, { "objectType": "TableField_MYSQL", "name": "product_sub_title", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品副标题(卖点)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sub_title" }, { "objectType": "TableField_MYSQL", "name": "product_sku_code", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品sku条码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sku_code" }, { "objectType": "TableField_MYSQL", "name": "member_nickname", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "会员昵称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_nickname" }, { "objectType": "TableField_MYSQL", "name": "create_date", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "创建时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_date" }, { "objectType": "TableField_MYSQL", "name": "modify_date", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "修改时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "modify_date" }, { "objectType": "TableField_MYSQL", "name": "delete_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否删除", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "delete_status" }, { "objectType": "TableField_MYSQL", "name": "product_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品的分类", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_id" }, { "objectType": "TableField_MYSQL", "name": "product_attr", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品销售属性:[{\"key\":\"颜色\",\"value\":\"银色\"},{\"key\":\"容量\",\"value\":\"4G\"}]", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attr" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_65", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_65", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_66", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_66", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_sku_id", "keyLength": 0, "order": "", "oldName": "product_sku_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_67", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_67", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_65", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_65" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_66", "fields": [ "product_sku_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_sku_stock", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_66" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_67", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_67" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_company_address", "comment": "公司收发货地址表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_company_address", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_company_address` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `address_name` varchar(200) DEFAULT NULL COMMENT '地址名称',\n `send_status` int(1) DEFAULT NULL COMMENT '默认发货地址:0->否;1->是',\n `receive_status` int(1) DEFAULT NULL COMMENT '是否默认收货地址:0->否;1->是',\n `name` varchar(64) DEFAULT NULL COMMENT '收发货人姓名',\n `phone` varchar(64) DEFAULT NULL COMMENT '收货人电话',\n `province` varchar(64) DEFAULT NULL COMMENT '省/直辖市',\n `city` varchar(64) DEFAULT NULL COMMENT '市',\n `region` varchar(64) DEFAULT NULL COMMENT '区',\n `detail_address` varchar(200) DEFAULT NULL COMMENT '详细地址',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='公司收发货地址表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "address_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "地址名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "address_name" }, { "objectType": "TableField_MYSQL", "name": "send_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "默认发货地址:0->否;1->是", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "send_status" }, { "objectType": "TableField_MYSQL", "name": "receive_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "是否默认收货地址:0->否;1->是", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receive_status" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收发货人姓名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "phone", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货人电话", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "phone" }, { "objectType": "TableField_MYSQL", "name": "province", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "省/直辖市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "province" }, { "objectType": "TableField_MYSQL", "name": "city", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "city" }, { "objectType": "TableField_MYSQL", "name": "region", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "区", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "region" }, { "objectType": "TableField_MYSQL", "name": "detail_address", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "详细地址", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "detail_address" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_order", "comment": "订单表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_order", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_order` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单id',\n `member_id` bigint(20) NOT NULL,\n `coupon_id` bigint(20) DEFAULT NULL,\n `order_sn` varchar(64) DEFAULT NULL COMMENT '订单编号',\n `create_time` datetime DEFAULT NULL COMMENT '提交时间',\n `member_username` varchar(64) DEFAULT NULL COMMENT '用户帐号',\n `total_amount` decimal(10,2) DEFAULT NULL COMMENT '订单总金额',\n `pay_amount` decimal(10,2) DEFAULT NULL COMMENT '应付金额(实际支付金额)',\n `freight_amount` decimal(10,2) DEFAULT NULL COMMENT '运费金额',\n `promotion_amount` decimal(10,2) DEFAULT NULL COMMENT '促销优化金额(促销价、满减、阶梯价)',\n `integration_amount` decimal(10,2) DEFAULT NULL COMMENT '积分抵扣金额',\n `coupon_amount` decimal(10,2) DEFAULT NULL COMMENT '优惠券抵扣金额',\n `discount_amount` decimal(10,2) DEFAULT NULL COMMENT '管理员后台调整订单使用的折扣金额',\n `pay_type` int(1) DEFAULT NULL COMMENT '支付方式:0->未支付;1->支付宝;2->微信',\n `source_type` int(1) DEFAULT NULL COMMENT '订单来源:0->PC订单;1->app订单',\n `status` int(1) DEFAULT NULL COMMENT '订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单',\n `order_type` int(1) DEFAULT NULL COMMENT '订单类型:0->正常订单;1->秒杀订单',\n `delivery_company` varchar(64) DEFAULT NULL COMMENT '物流公司(配送方式)',\n `delivery_sn` varchar(64) DEFAULT NULL COMMENT '物流单号',\n `auto_confirm_day` int(11) DEFAULT NULL COMMENT '自动确认时间(天)',\n `integration` int(11) DEFAULT NULL COMMENT '可以获得的积分',\n `growth` int(11) DEFAULT NULL COMMENT '可以活动的成长值',\n `promotion_info` varchar(100) DEFAULT NULL COMMENT '活动信息',\n `bill_type` int(1) DEFAULT NULL COMMENT '发票类型:0->不开发票;1->电子发票;2->纸质发票',\n `bill_header` varchar(200) DEFAULT NULL COMMENT '发票抬头',\n `bill_content` varchar(200) DEFAULT NULL COMMENT '发票内容',\n `bill_receiver_phone` varchar(32) DEFAULT NULL COMMENT '收票人电话',\n `bill_receiver_email` varchar(64) DEFAULT NULL COMMENT '收票人邮箱',\n `receiver_name` varchar(100) NOT NULL COMMENT '收货人姓名',\n `receiver_phone` varchar(32) NOT NULL COMMENT '收货人电话',\n `receiver_post_code` varchar(32) DEFAULT NULL COMMENT '收货人邮编',\n `receiver_province` varchar(32) DEFAULT NULL COMMENT '省份/直辖市',\n `receiver_city` varchar(32) DEFAULT NULL COMMENT '城市',\n `receiver_region` varchar(32) DEFAULT NULL COMMENT '区',\n `receiver_detail_address` varchar(200) DEFAULT NULL COMMENT '详细地址',\n `note` varchar(500) DEFAULT NULL COMMENT '订单备注',\n `confirm_status` int(1) DEFAULT NULL COMMENT '确认收货状态:0->未确认;1->已确认',\n `delete_status` int(1) NOT NULL DEFAULT '0' COMMENT '删除状态:0->未删除;1->已删除',\n `use_integration` int(11) DEFAULT NULL COMMENT '下单时使用的积分',\n `payment_time` datetime DEFAULT NULL COMMENT '支付时间',\n `delivery_time` datetime DEFAULT NULL COMMENT '发货时间',\n `receive_time` datetime DEFAULT NULL COMMENT '确认收货时间',\n `comment_time` datetime DEFAULT NULL COMMENT '评价时间',\n `modify_time` datetime DEFAULT NULL COMMENT '修改时间',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_57` (`member_id`),\n KEY `FK_Reference_61` (`coupon_id`),\n CONSTRAINT `FK_Reference_57` FOREIGN KEY (`member_id`) REFERENCES `ums_member` (`id`),\n CONSTRAINT `FK_Reference_61` FOREIGN KEY (`coupon_id`) REFERENCES `sms_coupon` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "订单id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "member_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_id" }, { "objectType": "TableField_MYSQL", "name": "coupon_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_id" }, { "objectType": "TableField_MYSQL", "name": "order_sn", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_sn" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "提交时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "member_username", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "用户帐号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_username" }, { "objectType": "TableField_MYSQL", "name": "total_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单总金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "total_amount" }, { "objectType": "TableField_MYSQL", "name": "pay_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "应付金额(实际支付金额)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pay_amount" }, { "objectType": "TableField_MYSQL", "name": "freight_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "运费金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "freight_amount" }, { "objectType": "TableField_MYSQL", "name": "promotion_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "促销优化金额(促销价、满减、阶梯价)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_amount" }, { "objectType": "TableField_MYSQL", "name": "integration_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分抵扣金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "integration_amount" }, { "objectType": "TableField_MYSQL", "name": "coupon_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "优惠券抵扣金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_amount" }, { "objectType": "TableField_MYSQL", "name": "discount_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "管理员后台调整订单使用的折扣金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "discount_amount" }, { "objectType": "TableField_MYSQL", "name": "pay_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "支付方式:0->未支付;1->支付宝;2->微信", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "pay_type" }, { "objectType": "TableField_MYSQL", "name": "source_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单来源:0->PC订单;1->app订单", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "source_type" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "order_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单类型:0->正常订单;1->秒杀订单", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_type" }, { "objectType": "TableField_MYSQL", "name": "delivery_company", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "物流公司(配送方式)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "delivery_company" }, { "objectType": "TableField_MYSQL", "name": "delivery_sn", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "物流单号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "delivery_sn" }, { "objectType": "TableField_MYSQL", "name": "auto_confirm_day", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "自动确认时间(天)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "auto_confirm_day" }, { "objectType": "TableField_MYSQL", "name": "integration", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "可以获得的积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "integration" }, { "objectType": "TableField_MYSQL", "name": "growth", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "可以活动的成长值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "growth" }, { "objectType": "TableField_MYSQL", "name": "promotion_info", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "活动信息", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_info" }, { "objectType": "TableField_MYSQL", "name": "bill_type", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "发票类型:0->不开发票;1->电子发票;2->纸质发票", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "bill_type" }, { "objectType": "TableField_MYSQL", "name": "bill_header", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "发票抬头", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "bill_header" }, { "objectType": "TableField_MYSQL", "name": "bill_content", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "发票内容", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "bill_content" }, { "objectType": "TableField_MYSQL", "name": "bill_receiver_phone", "type": "varchar", "length": 32, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收票人电话", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "bill_receiver_phone" }, { "objectType": "TableField_MYSQL", "name": "bill_receiver_email", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收票人邮箱", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "bill_receiver_email" }, { "objectType": "TableField_MYSQL", "name": "receiver_name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货人姓名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_name" }, { "objectType": "TableField_MYSQL", "name": "receiver_phone", "type": "varchar", "length": 32, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货人电话", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_phone" }, { "objectType": "TableField_MYSQL", "name": "receiver_post_code", "type": "varchar", "length": 32, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货人邮编", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_post_code" }, { "objectType": "TableField_MYSQL", "name": "receiver_province", "type": "varchar", "length": 32, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "省份/直辖市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_province" }, { "objectType": "TableField_MYSQL", "name": "receiver_city", "type": "varchar", "length": 32, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "城市", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_city" }, { "objectType": "TableField_MYSQL", "name": "receiver_region", "type": "varchar", "length": 32, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "区", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_region" }, { "objectType": "TableField_MYSQL", "name": "receiver_detail_address", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "详细地址", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receiver_detail_address" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" }, { "objectType": "TableField_MYSQL", "name": "confirm_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "确认收货状态:0->未确认;1->已确认", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "confirm_status" }, { "objectType": "TableField_MYSQL", "name": "delete_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "删除状态:0->未删除;1->已删除", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "delete_status" }, { "objectType": "TableField_MYSQL", "name": "use_integration", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "下单时使用的积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "use_integration" }, { "objectType": "TableField_MYSQL", "name": "payment_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "支付时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "payment_time" }, { "objectType": "TableField_MYSQL", "name": "delivery_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "发货时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "delivery_time" }, { "objectType": "TableField_MYSQL", "name": "receive_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "确认收货时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receive_time" }, { "objectType": "TableField_MYSQL", "name": "comment_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "评价时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "comment_time" }, { "objectType": "TableField_MYSQL", "name": "modify_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "修改时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "modify_time" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_57", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_57", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "member_id", "keyLength": 0, "order": "", "oldName": "member_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_61", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_61", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "coupon_id", "keyLength": 0, "order": "", "oldName": "coupon_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_57", "fields": [ "member_id" ], "referenceSchema": "mall-ref", "referenceTable": "ums_member", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "OneAndOnlyOneRelationship", "oldName": "FK_Reference_57" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_61", "fields": [ "coupon_id" ], "referenceSchema": "mall-ref", "referenceTable": "sms_coupon", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_61" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_order_item", "comment": "订单中所包含的商品", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_order_item", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 32768, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_order_item` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `order_id` bigint(20) DEFAULT NULL COMMENT '订单id',\n `order_sn` varchar(64) DEFAULT NULL COMMENT '订单编号',\n `product_id` bigint(20) DEFAULT NULL,\n `product_pic` varchar(500) DEFAULT NULL,\n `product_name` varchar(200) DEFAULT NULL,\n `product_brand` varchar(200) DEFAULT NULL,\n `product_sn` varchar(64) DEFAULT NULL,\n `product_price` decimal(10,2) DEFAULT NULL COMMENT '销售价格',\n `product_quantity` int(11) DEFAULT NULL COMMENT '购买数量',\n `product_sku_id` bigint(20) DEFAULT NULL COMMENT '商品sku编号',\n `product_sku_code` varchar(50) DEFAULT NULL COMMENT '商品sku条码',\n `product_category_id` bigint(20) DEFAULT NULL COMMENT '商品分类id',\n `promotion_name` varchar(200) DEFAULT NULL COMMENT '商品促销名称',\n `promotion_amount` decimal(10,2) DEFAULT NULL COMMENT '商品促销分解金额',\n `coupon_amount` decimal(10,2) DEFAULT NULL COMMENT '优惠券优惠分解金额',\n `integration_amount` decimal(10,2) DEFAULT NULL COMMENT '积分优惠分解金额',\n `real_amount` decimal(10,2) DEFAULT NULL COMMENT '该商品经过优惠后的分解金额',\n `gift_integration` int(11) NOT NULL DEFAULT '0' COMMENT '商品赠送积分',\n `gift_growth` int(11) NOT NULL DEFAULT '0' COMMENT '商品赠送成长值',\n `product_attr` varchar(500) DEFAULT NULL COMMENT '商品销售属性:[{\"key\":\"颜色\",\"value\":\"颜色\"},{\"key\":\"容量\",\"value\":\"4G\"}]',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_58` (`order_id`),\n KEY `FK_Reference_59` (`product_id`),\n CONSTRAINT `FK_Reference_58` FOREIGN KEY (`order_id`) REFERENCES `oms_order` (`id`),\n CONSTRAINT `FK_Reference_59` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单中所包含的商品'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "order_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_id" }, { "objectType": "TableField_MYSQL", "name": "order_sn", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_sn" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "product_pic", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_pic" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "product_brand", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_brand" }, { "objectType": "TableField_MYSQL", "name": "product_sn", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sn" }, { "objectType": "TableField_MYSQL", "name": "product_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "销售价格", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_price" }, { "objectType": "TableField_MYSQL", "name": "product_quantity", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "购买数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_quantity" }, { "objectType": "TableField_MYSQL", "name": "product_sku_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品sku编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sku_id" }, { "objectType": "TableField_MYSQL", "name": "product_sku_code", "type": "varchar", "length": 50, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品sku条码", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_sku_code" }, { "objectType": "TableField_MYSQL", "name": "product_category_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品分类id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_category_id" }, { "objectType": "TableField_MYSQL", "name": "promotion_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品促销名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_name" }, { "objectType": "TableField_MYSQL", "name": "promotion_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品促销分解金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "promotion_amount" }, { "objectType": "TableField_MYSQL", "name": "coupon_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "优惠券优惠分解金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "coupon_amount" }, { "objectType": "TableField_MYSQL", "name": "integration_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "积分优惠分解金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "integration_amount" }, { "objectType": "TableField_MYSQL", "name": "real_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "该商品经过优惠后的分解金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "real_amount" }, { "objectType": "TableField_MYSQL", "name": "gift_integration", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品赠送积分", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gift_integration" }, { "objectType": "TableField_MYSQL", "name": "gift_growth", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "0", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品赠送成长值", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "gift_growth" }, { "objectType": "TableField_MYSQL", "name": "product_attr", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品销售属性:[{\"key\":\"颜色\",\"value\":\"颜色\"},{\"key\":\"容量\",\"value\":\"4G\"}]", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attr" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_58", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_58", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "order_id", "keyLength": 0, "order": "", "oldName": "order_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_59", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_59", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_58", "fields": [ "order_id" ], "referenceSchema": "mall-ref", "referenceTable": "oms_order", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_58" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_59", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_59" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_order_operate_history", "comment": "订单操作历史记录", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_order_operate_history", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 16384, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_order_operate_history` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `order_id` bigint(20) DEFAULT NULL COMMENT '订单id',\n `operate_man` varchar(100) DEFAULT NULL COMMENT '操作人:用户;系统;后台管理员',\n `create_time` datetime DEFAULT NULL COMMENT '操作时间',\n `order_status` int(1) DEFAULT NULL COMMENT '订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单',\n `note` varchar(500) DEFAULT NULL COMMENT '备注',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_62` (`order_id`),\n CONSTRAINT `FK_Reference_62` FOREIGN KEY (`order_id`) REFERENCES `oms_order` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单操作历史记录'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "order_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_id" }, { "objectType": "TableField_MYSQL", "name": "operate_man", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作人:用户;系统;后台管理员", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "operate_man" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "操作时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "order_status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_status" }, { "objectType": "TableField_MYSQL", "name": "note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "note" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_62", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_62", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "order_id", "keyLength": 0, "order": "", "oldName": "order_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_62", "fields": [ "order_id" ], "referenceSchema": "mall-ref", "referenceTable": "oms_order", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_62" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_order_return_apply", "comment": "订单退货申请", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_order_return_apply", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 49152, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_order_return_apply` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `order_id` bigint(20) DEFAULT NULL COMMENT '订单id',\n `company_address_id` bigint(20) DEFAULT NULL COMMENT '收货地址表id',\n `product_id` bigint(20) DEFAULT NULL COMMENT '退货商品id',\n `order_sn` varchar(64) DEFAULT NULL COMMENT '订单编号',\n `create_time` datetime DEFAULT NULL COMMENT '申请时间',\n `member_username` varchar(64) DEFAULT NULL COMMENT '会员用户名',\n `return_amount` decimal(10,2) DEFAULT NULL COMMENT '退款金额',\n `return_name` varchar(100) DEFAULT NULL COMMENT '退货人姓名',\n `return_phone` varchar(100) DEFAULT NULL COMMENT '退货人电话',\n `status` int(1) DEFAULT NULL COMMENT '申请状态:0->待处理;1->退货中;2->已完成;3->已拒绝',\n `handle_time` datetime DEFAULT NULL COMMENT '处理时间',\n `product_pic` varchar(500) DEFAULT NULL COMMENT '商品图片',\n `product_name` varchar(200) DEFAULT NULL COMMENT '商品名称',\n `product_brand` varchar(200) DEFAULT NULL COMMENT '商品品牌',\n `product_attr` varchar(500) DEFAULT NULL COMMENT '商品销售属性:颜色:红色;尺码:xl;',\n `product_count` int(11) DEFAULT NULL COMMENT '退货数量',\n `product_price` decimal(10,2) DEFAULT NULL COMMENT '商品单价',\n `product_real_price` decimal(10,2) DEFAULT NULL COMMENT '商品实际支付单价',\n `reason` varchar(200) DEFAULT NULL COMMENT '原因',\n `description` varchar(500) DEFAULT NULL COMMENT '描述',\n `proof_pics` varchar(1000) DEFAULT NULL COMMENT '凭证图片,以逗号隔开',\n `handle_note` varchar(500) DEFAULT NULL COMMENT '处理备注',\n `handle_man` varchar(100) DEFAULT NULL COMMENT '处理人员',\n `receive_man` varchar(100) DEFAULT NULL COMMENT '收货人',\n `receive_time` datetime DEFAULT NULL COMMENT '收货时间',\n `receive_note` varchar(500) DEFAULT NULL COMMENT '收货备注',\n PRIMARY KEY (`id`),\n KEY `FK_Reference_63` (`order_id`),\n KEY `FK_Reference_64` (`company_address_id`),\n KEY `FK_Reference_75` (`product_id`),\n CONSTRAINT `FK_Reference_63` FOREIGN KEY (`order_id`) REFERENCES `oms_order` (`id`),\n CONSTRAINT `FK_Reference_64` FOREIGN KEY (`company_address_id`) REFERENCES `oms_company_address` (`id`),\n CONSTRAINT `FK_Reference_75` FOREIGN KEY (`product_id`) REFERENCES `pms_product` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单退货申请'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "order_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_id" }, { "objectType": "TableField_MYSQL", "name": "company_address_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货地址表id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "company_address_id" }, { "objectType": "TableField_MYSQL", "name": "product_id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退货商品id", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_id" }, { "objectType": "TableField_MYSQL", "name": "order_sn", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单编号", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "order_sn" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "申请时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" }, { "objectType": "TableField_MYSQL", "name": "member_username", "type": "varchar", "length": 64, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "会员用户名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "member_username" }, { "objectType": "TableField_MYSQL", "name": "return_amount", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退款金额", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "return_amount" }, { "objectType": "TableField_MYSQL", "name": "return_name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退货人姓名", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "return_name" }, { "objectType": "TableField_MYSQL", "name": "return_phone", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退货人电话", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "return_phone" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "申请状态:0->待处理;1->退货中;2->已完成;3->已拒绝", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "handle_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "处理时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "handle_time" }, { "objectType": "TableField_MYSQL", "name": "product_pic", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品图片", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_pic" }, { "objectType": "TableField_MYSQL", "name": "product_name", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品名称", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_name" }, { "objectType": "TableField_MYSQL", "name": "product_brand", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品品牌", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_brand" }, { "objectType": "TableField_MYSQL", "name": "product_attr", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品销售属性:颜色:红色;尺码:xl;", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_attr" }, { "objectType": "TableField_MYSQL", "name": "product_count", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退货数量", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_count" }, { "objectType": "TableField_MYSQL", "name": "product_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品单价", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_price" }, { "objectType": "TableField_MYSQL", "name": "product_real_price", "type": "decimal", "length": 10, "decimals": 2, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "商品实际支付单价", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "product_real_price" }, { "objectType": "TableField_MYSQL", "name": "reason", "type": "varchar", "length": 200, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "原因", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "reason" }, { "objectType": "TableField_MYSQL", "name": "description", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "描述", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "description" }, { "objectType": "TableField_MYSQL", "name": "proof_pics", "type": "varchar", "length": 1000, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "凭证图片,以逗号隔开", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "proof_pics" }, { "objectType": "TableField_MYSQL", "name": "handle_note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "处理备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "handle_note" }, { "objectType": "TableField_MYSQL", "name": "handle_man", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "处理人员", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "handle_man" }, { "objectType": "TableField_MYSQL", "name": "receive_man", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货人", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receive_man" }, { "objectType": "TableField_MYSQL", "name": "receive_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receive_time" }, { "objectType": "TableField_MYSQL", "name": "receive_note", "type": "varchar", "length": 500, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "收货备注", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "receive_note" } ], "indexes": [ { "objectType": "Index_MYSQL", "name": "FK_Reference_63", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_63", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "order_id", "keyLength": 0, "order": "", "oldName": "order_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_64", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_64", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "company_address_id", "keyLength": 0, "order": "", "oldName": "company_address_id" } ] }, { "objectType": "Index_MYSQL", "name": "FK_Reference_75", "type": "NORMAL", "method": "BTREE", "comment": "", "oldName": "FK_Reference_75", "online": false, "keyBlockSize": 0, "parser": "", "algorithm": "", "lock": "", "collation": "A", "cardinality": "0", "packed": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "product_id", "keyLength": 0, "order": "", "oldName": "product_id" } ] } ], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [ { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_63", "fields": [ "order_id" ], "referenceSchema": "mall-ref", "referenceTable": "oms_order", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_63" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_64", "fields": [ "company_address_id" ], "referenceSchema": "mall-ref", "referenceTable": "oms_company_address", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_64" }, { "objectType": "ForeignKey_MYSQL", "name": "FK_Reference_75", "fields": [ "product_id" ], "referenceSchema": "mall-ref", "referenceTable": "pms_product", "referenceFields": [ "id" ], "onDelete": "RESTRICT", "onUpdate": "RESTRICT", "sourceCardinality": "ZeroOrManyRelationship", "targetCardinality": "ZeroOrOneRelationship", "oldName": "FK_Reference_75" } ], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_order_return_reason", "comment": "退货原因表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_order_return_reason", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_order_return_reason` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `name` varchar(100) DEFAULT NULL COMMENT '退货类型',\n `sort` int(11) DEFAULT NULL,\n `status` int(1) DEFAULT NULL COMMENT '状态:0->不启用;1->启用',\n `create_time` datetime DEFAULT NULL COMMENT '添加时间',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='退货原因表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "name", "type": "varchar", "length": 100, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "utf8", "collation": "utf8_general_ci", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "退货类型", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "name" }, { "objectType": "TableField_MYSQL", "name": "sort", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "sort" }, { "objectType": "TableField_MYSQL", "name": "status", "type": "int", "length": 1, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "状态:0->不启用;1->启用", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "status" }, { "objectType": "TableField_MYSQL", "name": "create_time", "type": "datetime", "length": -2147483648, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "添加时间", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "create_time" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] }, { "objectType": "Table_MYSQL", "name": "oms_order_setting", "comment": "订单设置表", "engine": "InnoDB", "characterSet": "utf8", "collation": "utf8_general_ci", "autoIncrement": 1, "tablespace": "", "storage": "", "insertMethod": "", "connection": "", "checksum": false, "rowFormat": "Dynamic", "avgRowLength": 0, "maxRows": 0, "minRows": 0, "keyBlockSize": 0, "packKeys": "", "delayKeyWrite": false, "dataDirectory": "", "indexDirectory": "", "statsAutoRecalc": "", "statsPersistent": "", "statsSamplePages": 0, "union": "", "pageCheckSum": false, "transactional": false, "compression": "", "oldName": "oms_order_setting", "encryption": false, "createOptions": "", "createTime": "2022-12-16 09:58:53", "checkTime": "", "dataFree": 0, "dataLength": 16384, "indexLength": 0, "maxDataLength": 0, "rows": 0, "updateTime": "", "DDL": "CREATE TABLE `oms_order_setting` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `flash_order_overtime` int(11) DEFAULT NULL COMMENT '秒杀订单超时关闭时间(分)',\n `normal_order_overtime` int(11) DEFAULT NULL COMMENT '正常订单超时时间(分)',\n `confirm_overtime` int(11) DEFAULT NULL COMMENT '发货后自动确认收货时间(天)',\n `finish_overtime` int(11) DEFAULT NULL COMMENT '自动完成交易时间,不能申请售后(天)',\n `comment_overtime` int(11) DEFAULT NULL COMMENT '订单完成后自动好评时间(天)',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单设置表'", "partitionBy": "", "partitionByExpr": "", "partitions": 0, "partitionKeyAlgorithm": "", "subPartitionBy": "", "subPartitionByExpr": "", "subPartitions": 0, "subPartitionKeyAlgorithm": "", "fields": [ { "objectType": "TableField_MYSQL", "name": "id", "type": "bigint", "length": 20, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": false, "defaultType": "Others", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": true, "comment": "", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "id" }, { "objectType": "TableField_MYSQL", "name": "flash_order_overtime", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "秒杀订单超时关闭时间(分)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "flash_order_overtime" }, { "objectType": "TableField_MYSQL", "name": "normal_order_overtime", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "正常订单超时时间(分)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "normal_order_overtime" }, { "objectType": "TableField_MYSQL", "name": "confirm_overtime", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "发货后自动确认收货时间(天)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "confirm_overtime" }, { "objectType": "TableField_MYSQL", "name": "finish_overtime", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "自动完成交易时间,不能申请售后(天)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "finish_overtime" }, { "objectType": "TableField_MYSQL", "name": "comment_overtime", "type": "int", "length": 11, "decimals": -2147483648, "isUnsigned": false, "isZeroFill": false, "setEnumValues": "", "isBinary": false, "charset": "", "collation": "", "isNullable": true, "defaultType": "Null", "defaultValue": "", "isOnUpdateCurrentTimestamp": false, "isAutoInc": false, "comment": "订单完成后自动好评时间(天)", "columnFormat": "", "storage": "", "isVirtual": false, "isGeneratedAlways": false, "virtualExpr": "", "virtualType": "", "oldName": "comment_overtime" } ], "indexes": [], "primaryKey": { "objectType": "PrimaryKey_MYSQL", "name": "", "fields": [ { "objectType": "IndexField_MYSQL", "name": "id", "keyLength": 0, "order": "", "oldName": "id" } ], "oldName": "", "indexMethod": "BTREE", "comment": "" }, "foreignKeys": [], "triggers": [], "tablePartitions": [] } ], "views": [] } ] }, "diagrams": [ { "name": "订单模块数据库模型", "paperWidth": 2, "paperHeight": 1, "tableFont": "Arial Unicode MS", "tableFontSize": 14, "isBalckWhite": false, "showDBSchemaName": false, "showViewRelations": true, "notation": "default", "showFieldComment": false, "showTableComment": false, "shapes": [ { "type": "table", "schemaName": "mall-ref", "tableName": "oms_cart_item", "x": 40, "y": 0, "width": 227, "height": 411, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_company_address", "x": 890, "y": 540, "width": 202, "height": 251, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_order", "x": 310, "y": 200, "width": 250, "height": 931, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_order_item", "x": 20, "y": 430, "width": 235, "height": 471, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_order_operate_history", "x": 350, "y": 0, "width": 223, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_order_return_apply", "x": 610, "y": 370, "width": 230, "height": 591, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_order_return_reason", "x": 620, "y": 10, "width": 212, "height": 151, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } }, { "type": "table", "schemaName": "mall-ref", "tableName": "oms_order_setting", "x": 620, "y": 180, "width": 213, "height": 171, "isBold": false, "titleColor": { "r": 55, "g": 131, "b": 192, "a": 1 } } ], "layers": [], "relations": [ { "name": "FK_Reference_58", "sourceTableName": "oms_order_item", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 270, "y": 670 }, { "x": 292, "y": 670 }, { "x": 292, "y": 670 }, { "x": 295, "y": 670 } ], "label": { "x": 20, "y": 672, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_62", "sourceTableName": "oms_order_operate_history", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 440, "y": 186 }, { "x": 440, "y": 215 }, { "x": 440, "y": 215 }, { "x": 440, "y": 185 } ], "label": { "x": 350, "y": 0, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_63", "sourceTableName": "oms_order_return_apply", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 595, "y": 670 }, { "x": 580, "y": 670 }, { "x": 580, "y": 670 }, { "x": 575, "y": 670 } ], "label": { "x": 610, "y": 672, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } }, { "name": "FK_Reference_64", "sourceTableName": "oms_order_return_apply", "sourceSchemaName": "mall-ref", "lineWidth": 1, "visible": true, "vertices": [ { "x": 855, "y": 670 }, { "x": 835, "y": 670 }, { "x": 835, "y": 670 }, { "x": 875, "y": 670 } ], "label": { "x": 610, "y": 672, "width": 122, "height": 40, "fontName": "Arial Unicode MS", "fontSize": 14, "fontColor": { "r": 51, "g": 51, "b": 51, "a": 1 }, "isFontBold": false, "isFontItalic": false, "isVisible": false } } ], "viewRelations": [] } ] } ================================================ FILE: document/pos/app.pos ================================================ {"diagram":{"image":{"x":0,"width":200,"y":0,"pngdata":"iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAsUlEQVR4nO3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8GXHmAAFMgHIEAAAAAElFTkSuQmCC","height":200},"elements":{"leftChildren":[],"children":[{"parent":"root","lineStyle":{"randomLineColor":"#02ACF4"},"children":[{"parent":"7fdaa671c089","children":[],"id":"d4cbdc7edd21","title":"轮播广告"},{"parent":"7fdaa671c089","children":[{"parent":"c7e6739c4ae6","children":[],"id":"80d6ce7cfc76","title":"专题"},{"parent":"c7e6739c4ae6","children":[],"id":"0a11d36e446c650a","title":"话题"},{"parent":"c7e6739c4ae6","children":[],"id":"340e90c4e51d1649","title":"优选"},{"parent":"c7e6739c4ae6","children":[],"id":"970241b50cc05678","title":"特惠"}],"collapsed":true,"id":"c7e6739c4ae6","title":"功能模块"},{"parent":"7fdaa671c089","note":"对应后台品牌推荐功能","children":[{"parent":"dc18f5472ea5e02b","note":"LOGO、名称、商品数量","children":[],"id":"976dfe6b0150ec33","title":"数据列表"},{"parent":"dc18f5472ea5e02b","note":"品牌大图、品牌信息、品牌故事、相关商品","children":[],"id":"00bfa14a38826172","title":"品牌详情"}],"collapsed":true,"id":"dc18f5472ea5e02b","title":"品牌制造商直供"},{"parent":"7fdaa671c089","note":"对应后台秒杀活动列表功能","children":[{"parent":"45034b1150312f55","children":[],"id":"643411ea6b72ccf2","title":"秒杀商品列表"}],"collapsed":true,"id":"45034b1150312f55","title":"秒杀专区"},{"parent":"7fdaa671c089","note":"对应后台新品推荐功能","children":[{"parent":"d6b972cbcee22066","note":"图片、名称、副标题","children":[],"id":"edaf29b58df1243f","title":"商品列表"}],"collapsed":true,"id":"d6b972cbcee22066","title":"新鲜好物"},{"parent":"7fdaa671c089","note":"对应后台人气推荐功能","children":[{"parent":"ac88582e15c2eef0","children":[],"id":"3eb8632e3642988d","title":"商品列表"}],"collapsed":true,"id":"ac88582e15c2eef0","title":"人气推荐"},{"parent":"7fdaa671c089","children":[{"parent":"8814bf9e11d41af7","children":[],"id":"32f37b2047b77095","title":"商品列表"}],"collapsed":true,"id":"8814bf9e11d41af7","title":"猜你喜欢"}],"collapsed":false,"id":"7fdaa671c089","title":"首页"},{"parent":"root","lineStyle":{"randomLineColor":"#7549C5"},"children":[{"parent":"a42d702e0ee9","note":"支持二级分类,显示LOGO及名称","children":[],"id":"9c88e281f046a24f","title":"分类"},{"parent":"a42d702e0ee9","children":[{"parent":"31c90c493bf05852","note":"图片、名称、副标题、价格、销量","children":[],"id":"e299829aa4810002","title":"数据列表"},{"parent":"31c90c493bf05852","note":"综合排序、销量优先、价格","children":[],"id":"bf810f293421a8ef","title":"排序"},{"parent":"31c90c493bf05852","note":"按分类筛选","children":[],"id":"0f4a6ff1d79bb2fb","title":"筛选"}],"collapsed":true,"id":"31c90c493bf05852","title":"商品列表"},{"parent":"a42d702e0ee9","children":[{"parent":"76628b9be31b73be","note":"商品相册图片、名称、副标题、价格、销量、库存、浏览量","children":[],"id":"d872c72a1dc426b1","title":"商品信息"},{"parent":"76628b9be31b73be","note":"对应商品SKU属性","children":[],"id":"1cf72f8a190ade90","title":"购买类型"},{"parent":"76628b9be31b73be","note":"对应后台商品参数","children":[],"id":"ae359f289b98ce59","title":"商品参数"},{"parent":"76628b9be31b73be","note":"当前商品可用优惠券","children":[],"id":"10b2645b29d024c9","title":"优惠券"},{"parent":"76628b9be31b73be","note":"当前商品优惠促销","children":[],"id":"00379bbe8835d448","title":"促销活动"},{"parent":"76628b9be31b73be","children":[],"id":"119f628802e49bc5","title":"服务"},{"parent":"76628b9be31b73be","note":"图片、名称、首字母","children":[],"id":"ef11951399728cdb","title":"品牌信息"},{"parent":"76628b9be31b73be","note":"富文本展示详情","children":[],"id":"ff50586bb4b941db","title":"图文详情"},{"parent":"76628b9be31b73be","children":[{"parent":"289f074cbc434a5a","children":[],"id":"50eef6dd3036e365","title":"首页"},{"parent":"289f074cbc434a5a","children":[],"id":"ac7f421c8669af01","title":"购物车"},{"parent":"289f074cbc434a5a","children":[],"id":"09ecbaf3dc66e6bd","title":"收藏"},{"parent":"289f074cbc434a5a","children":[],"id":"3785b6a08250a08b","title":"立即购买"},{"parent":"289f074cbc434a5a","children":[],"id":"b8eef396c751402e","title":"加入购物车"}],"id":"289f074cbc434a5a","title":"操作"}],"collapsed":true,"id":"76628b9be31b73be","title":"商品详情"}],"collapsed":false,"id":"a42d702e0ee9","title":"商品"},{"parent":"root","lineStyle":{"randomLineColor":"#0FBAB0"},"children":[{"parent":"d0697f697cfe","note":"商品图片、名称、规格、价格、数量","children":[],"id":"afa515b5f473036f","title":"数据列表"},{"parent":"d0697f697cfe","children":[],"id":"ce4e9b0baea515bf","title":"修改数量"},{"parent":"d0697f697cfe","children":[],"id":"9d64a3747a5a4dbb","title":"清空"},{"parent":"d0697f697cfe","children":[],"id":"8779753e63722334","title":"计算价格"},{"parent":"d0697f697cfe","children":[],"id":"15fed91c2bce2f7a","title":"去结算"}],"collapsed":false,"id":"d0697f697cfe","title":"购物车"},{"parent":"root","lineStyle":{"randomLineColor":"#DD489D"},"children":[{"parent":"fb065ff81be1","note":"从购物车中的去结算功能创建","children":[{"parent":"ca04b711e7949548","children":[],"id":"2f2dccd584a60ad4","title":"选择收货地址"},{"parent":"ca04b711e7949548","children":[],"id":"cafd7b8dc18e5d0a","title":"商品信息"},{"parent":"ca04b711e7949548","children":[],"id":"aac14770026fdf5c","title":"选择优惠券"},{"parent":"ca04b711e7949548","children":[],"id":"6131425fc61d3668","title":"积分抵扣"},{"parent":"ca04b711e7949548","note":"最终价格=商品合计+运费-活动优惠-优惠券-积分抵扣","children":[],"id":"83d59dcc9997c7c8","title":"价格计算"},{"parent":"ca04b711e7949548","children":[],"id":"a9211cfedc3109bc","title":"备注"}],"collapsed":true,"id":"ca04b711e7949548","title":"创建订单"},{"parent":"fb065ff81be1","children":[{"parent":"11fe4c9727debc8f","children":[],"id":"373000cec66405d8","title":"支付金额"},{"parent":"11fe4c9727debc8f","children":[],"id":"df24adab6221244a","title":"支付方式"},{"parent":"11fe4c9727debc8f","children":[],"id":"3f2edb4c5007d3b9","title":"确认支付"}],"collapsed":true,"id":"11fe4c9727debc8f","title":"订单支付"},{"parent":"fb065ff81be1","note":"创建日期、订单状态、商品信息、商品数量、价格","children":[],"id":"92a1c16202e9f0d5","title":"订单列表"},{"parent":"fb065ff81be1","children":[{"parent":"ae74ea4a5868936d","note":"会员下单后未付款状态","children":[{"parent":"e05261e1c823b51f","children":[],"id":"1d762ed3603fb5c6","title":"取消订单"},{"parent":"e05261e1c823b51f","children":[],"id":"83766b00b43fc138","title":"立即付款"}],"collapsed":true,"id":"e05261e1c823b51f","title":"待付款"},{"parent":"ae74ea4a5868936d","note":"会员付款后,后台以执行发货操作状态","children":[{"parent":"6e2cbaae4d6a3f59","children":[],"id":"98ed3cb660c4e10b","title":"查看物流"},{"parent":"6e2cbaae4d6a3f59","children":[],"id":"b0e179cf55d5761c","title":"确认收货"}],"collapsed":true,"id":"6e2cbaae4d6a3f59","title":"待收货"},{"parent":"ae74ea4a5868936d","note":"会员确认收货后的状态","children":[{"parent":"6bfda684d01cb0f0","children":[],"id":"071cd90a7aa7a8fb","title":"删除"},{"parent":"6bfda684d01cb0f0","children":[],"id":"84e48c0894bd2b7b","title":"评价商品"}],"id":"6bfda684d01cb0f0","title":"已完成"},{"parent":"ae74ea4a5868936d","note":"会员主动取消或订单超时自动取消","children":[{"parent":"80aa20d5928f9bbd","children":[],"id":"6b566d423f377756","title":"删除"}],"id":"80aa20d5928f9bbd","title":"已取消"}],"collapsed":true,"id":"ae74ea4a5868936d","title":"操作"},{"parent":"fb065ff81be1","children":[{"parent":"9000a6310c88009d","note":"订单状态、收货人信息、商品信息、价格信息、订单信息","children":[],"id":"d62bce8b55d78507","title":"显示信息"},{"parent":"9000a6310c88009d","note":"同订单操作","children":[],"id":"90d27cfe885775be","title":"操作"},{"parent":"9000a6310c88009d","note":"订单完成后可申请售后","children":[],"id":"632a21481391ada1","title":"申请售后"}],"collapsed":true,"id":"9000a6310c88009d","title":"订单详情"}],"collapsed":false,"id":"fb065ff81be1","title":"订单"},{"parent":"root","lineStyle":{"randomLineColor":"#3D5EC2"},"children":[{"parent":"3667506bcbfd","note":"头像、昵称、积分、成长值、优惠券数量","children":[],"id":"5d90a0c0a702d882","title":"会员信息"},{"parent":"3667506bcbfd","children":[{"parent":"bf2cf63ba26fb5ad","note":"名称、有效期、面值、类型","children":[],"id":"2c500f9d25ff3e51","title":"数据列表"},{"parent":"bf2cf63ba26fb5ad","note":"未使用、已使用、已过期","children":[],"id":"b4fdb51f26c1a41b","title":"优惠券状态"}],"collapsed":true,"id":"bf2cf63ba26fb5ad","title":"优惠券"},{"parent":"3667506bcbfd","children":[{"parent":"a0267688cbae9ec0","children":[],"id":"37a223bdd73eb3ea","title":"全部订单"},{"parent":"a0267688cbae9ec0","children":[],"id":"fed835b16b137e97","title":"待付款"},{"parent":"a0267688cbae9ec0","children":[],"id":"3b367f84d51347aa","title":"待收货"},{"parent":"a0267688cbae9ec0","children":[],"id":"04150d3e5153ce52","title":"退款/售后"}],"collapsed":true,"id":"a0267688cbae9ec0","title":"订单功能"},{"parent":"3667506bcbfd","children":[{"parent":"062bb5a6671bf97e","note":"地址、姓名、手机号","children":[],"id":"a9958c3f8e401d5a","title":"数据列表"},{"parent":"062bb5a6671bf97e","note":"姓名、手机号、邮政编码、所在区域、详细地址、设为默认","children":[],"id":"1592f39d4d06e3b2","title":"添加"},{"parent":"062bb5a6671bf97e","children":[{"parent":"751e583216317c3b","children":[],"id":"25fe335a7e0e0c84","title":"编辑"},{"parent":"751e583216317c3b","children":[],"id":"134e058cf79508d2","title":"删除"}],"id":"751e583216317c3b","title":"操作"}],"collapsed":true,"id":"062bb5a6671bf97e","title":"地址管理"},{"parent":"3667506bcbfd","note":"商品浏览记录","children":[{"parent":"a45a5ad8ef617b35","note":"图片、名称、副标题、价格、浏览时间","children":[],"id":"00483209f028560f","title":"数据列表"},{"parent":"a45a5ad8ef617b35","children":[],"id":"557b86dd53b224af","title":"清空"}],"collapsed":true,"id":"a45a5ad8ef617b35","title":"我的足迹"},{"parent":"3667506bcbfd","note":"关注的品牌","children":[{"parent":"e34b41b47e132443","note":"图片、名称","children":[],"id":"d531e0e51b1d8dec","title":"数据列表"},{"parent":"e34b41b47e132443","children":[],"id":"e99cc7d31e3ac24a","title":"清空"}],"collapsed":true,"id":"e34b41b47e132443","title":"我的关注"},{"parent":"3667506bcbfd","note":"收藏的商品","children":[{"parent":"ed018e9dbdd38b57","note":"图片、名称、副标题、价格","children":[],"id":"9e4222c443f50033","title":"数据列表"},{"parent":"ed018e9dbdd38b57","children":[],"id":"f93a5ed18eaa86da","title":"清空"}],"collapsed":true,"id":"ed018e9dbdd38b57","title":"我的收藏"},{"parent":"3667506bcbfd","children":[],"id":"1738b184bfa4e83d","title":"我的评价"},{"parent":"3667506bcbfd","children":[{"parent":"f20d65c05cf2cbb5","children":[],"id":"319f7a22b8a9ce0f","title":"个人资料"},{"parent":"f20d65c05cf2cbb5","children":[],"id":"24e455087b466678","title":"收货地址"},{"parent":"f20d65c05cf2cbb5","children":[],"id":"7ad27808644bcee9","title":"实名认证"},{"parent":"f20d65c05cf2cbb5","children":[],"id":"10c0510057542792","title":"消息推送"},{"parent":"f20d65c05cf2cbb5","children":[],"id":"81aa86bdc6413543","title":"清除缓存"},{"parent":"f20d65c05cf2cbb5","children":[],"id":"2053022f05ad0ed2","title":"关于"},{"parent":"f20d65c05cf2cbb5","children":[],"id":"755460577b262df1","title":"检查更新"},{"parent":"f20d65c05cf2cbb5","children":[],"id":"55077fb110be218a","title":"退出登录"}],"collapsed":true,"id":"f20d65c05cf2cbb5","title":"设置"}],"id":"3667506bcbfd","title":"我的"}],"root":true,"theme":"delicate_caihong","id":"root","title":"移动商城功能","version":489,"structure":"mind_free"}},"meta":{"exportTime":"2022-11-11 16:38:21","member":"5a210b2ee4b04f355d337104","diagramInfo":{"creator":"5a210b2ee4b04f355d337104","created":"2022-11-11 15:03:54","modified":"2022-11-11 16:36:49","title":"移动商城功能","category":"mind_free"},"id":"636df3da6376897f2b17dcee","type":"ProcessOn Schema File","version":"1.0"}} ================================================ FILE: document/pos/oms.pos ================================================ {"diagram":{"image":{"x":0,"width":200,"y":0,"pngdata":"iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAsUlEQVR4nO3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8GXHmAAFMgHIEAAAAAElFTkSuQmCC","height":200},"elements":{"leftChildren":[],"children":[{"parent":"root","lineStyle":{"randomLineColor":"#FD5155"},"children":[{"parent":"4dcde504af651f71e1d7","note":"关键字、收货人、提交时间、顶替状态、订单分类、订单来源
","children":[],"collapsed":false,"id":"fde4bfb4e88c67ad8428","title":"筛选搜索"},{"parent":"4dcde504af651f71e1d7","note":"编号、订单编号、提交时间、用户账号、订单金额、支付方式、订单来源、订单状态","children":[],"collapsed":false,"id":"c05c9c5a7bd74b1a5df2","title":"数据列表"},{"parent":"4dcde504af651f71e1d7","children":[{"parent":"0b56a27aacfcf9e91429","children":[],"id":"6a1a5efa50bb1ef7","title":"查看订单"},{"parent":"0b56a27aacfcf9e91429","children":[],"id":"150f177484de884c3ab8","title":"订单关闭"},{"parent":"0b56a27aacfcf9e91429","children":[],"id":"99c1cc50d8297c2a98d6","title":"订单发货"},{"parent":"0b56a27aacfcf9e91429","children":[],"id":"2981f6b3025222a8c841","title":"订单跟踪"},{"parent":"0b56a27aacfcf9e91429","children":[],"id":"20c190cbe89762be15a3","title":"删除订单"}],"collapsed":true,"id":"0b56a27aacfcf9e91429","title":"操作"},{"parent":"4dcde504af651f71e1d7","children":[{"parent":"6bb87975d955845bde69","children":[],"id":"4aca17ee5d455b621b79","title":"订单状态"},{"parent":"6bb87975d955845bde69","children":[{"parent":"105e887f8df84ebf2d4d","note":"收货人姓名、手机号码、邮政编码、所在区域、详细地址
","children":[],"collapsed":false,"id":"9f82b9bdf00ec9689ef9","title":"修改收货人信息"},{"parent":"105e887f8df84ebf2d4d","note":"运费、折扣金额","children":[],"collapsed":false,"id":"38f2b97e619f795829bc","title":"修改费用信息"},{"parent":"105e887f8df84ebf2d4d","children":[],"id":"1edfe0c33b5e5bf1c33e","title":"发送站内信"},{"parent":"105e887f8df84ebf2d4d","children":[],"id":"a1a00b251a06108ffd1d","title":"关闭订单"},{"parent":"105e887f8df84ebf2d4d","children":[],"id":"6047f31b701bb59cbedb","title":"备注订单"}],"collapsed":true,"id":"105e887f8df84ebf2d4d","title":"修改订单"},{"parent":"6bb87975d955845bde69","children":[],"id":"87c30eccd19c19b2fa21","title":"基本信息"},{"parent":"6bb87975d955845bde69","children":[],"id":"0fd2e05525ef81ccc71b","title":"收货人信息"},{"parent":"6bb87975d955845bde69","children":[],"id":"16bad996586dfb702684","title":"商品信息"},{"parent":"6bb87975d955845bde69","children":[],"id":"b7a1eb937d5a1dd829a5","title":"费用信息"},{"parent":"6bb87975d955845bde69","children":[],"id":"33ed8f34f0dcc1fd884e","title":"操作信息"}],"collapsed":true,"id":"6bb87975d955845bde69","title":"查看订单"},{"parent":"4dcde504af651f71e1d7","children":[{"parent":"ad4f7b3904a3a28913c0","children":[],"id":"ff914c59e0c91f5f","title":"批量发货"},{"parent":"ad4f7b3904a3a28913c0","children":[],"id":"ef2970a00683951a","title":"关闭订单"},{"parent":"ad4f7b3904a3a28913c0","children":[],"id":"9a0958bcf8cee447","title":"删除订单"}],"collapsed":true,"id":"ad4f7b3904a3a28913c0","title":"批量操作"}],"collapsed":false,"id":"4dcde504af651f71e1d7","title":"订单列表"},{"parent":"root","lineStyle":{"randomLineColor":"#7549C5"},"children":[{"parent":"6c3a4a0c039217397d8b","children":[],"id":"462128b57dd56195ce9c","title":"秒杀订单自动关闭时间"},{"parent":"6c3a4a0c039217397d8b","children":[],"id":"62967ff650b3f3d7b7ce","title":"正常订单自动关闭时间"},{"parent":"6c3a4a0c039217397d8b","children":[],"id":"c8d3f6fe2e16b1f9f8b0","title":"发货超过时间自动完成"},{"parent":"6c3a4a0c039217397d8b","children":[],"id":"313a5d6ebc9763801649","title":"订单完成多少天自动结束,不能申请售后"},{"parent":"6c3a4a0c039217397d8b","children":[],"id":"46b11b98a6e1122437eb","title":"订单完成超过多少天自动5星好评"}],"collapsed":false,"id":"6c3a4a0c039217397d8b","title":"订单设置"},{"parent":"root","lineStyle":{"randomLineColor":"#80BA4C"},"children":[{"parent":"a20f045e6427362f64f9","note":"服务单号、处理状态、申请时间、操作人员、处理时间","children":[],"id":"cce77724d1c597545635","title":"筛选搜索"},{"parent":"a20f045e6427362f64f9","note":"服务单号、申请时间、用户账号、退款金额、申请状态、处理时间","children":[],"id":"4dd4d82ac01825481bbb","title":"数据列表"},{"parent":"a20f045e6427362f64f9","children":[{"parent":"af645e85dfaab5269abc","children":[],"id":"f041b7db8f60966eb590","title":"退货商品"},{"parent":"af645e85dfaab5269abc","children":[{"parent":"7dd3937fa8c74d5b98b3","children":[],"id":"fb9fa92d8bcb4e85cace","title":"申请信息"},{"parent":"7dd3937fa8c74d5b98b3","children":[],"id":"cc01cd801b7518e46c6d","title":"退货信息"},{"parent":"7dd3937fa8c74d5b98b3","children":[],"id":"aac18616f56b8cc59070","title":"处理备注"},{"parent":"7dd3937fa8c74d5b98b3","note":"待处理、退货中、已完成、已拒绝","children":[],"id":"0a57c06092e8fe42","title":"处理状态"}],"id":"7dd3937fa8c74d5b98b3","title":"服务单信息"},{"parent":"af645e85dfaab5269abc","children":[{"parent":"1ef8f832bcdfc56ee072","children":[],"id":"a674fb6aaca4f93e87d3","title":"确认退货"},{"parent":"1ef8f832bcdfc56ee072","children":[],"id":"2b7c411c7e2b0614d12a","title":"确认收货"},{"parent":"1ef8f832bcdfc56ee072","children":[],"id":"e7edcdd4dd2207a4241f","title":"拒绝退货"}],"id":"1ef8f832bcdfc56ee072","title":"操作"}],"collapsed":true,"id":"af645e85dfaab5269abc","title":"查看详情"}],"collapsed":false,"id":"a20f045e6427362f64f9","title":"退货申请处理"},{"parent":"root","lineStyle":{"randomLineColor":"#0F80C4"},"children":[{"parent":"62069253fd52e628ed10","note":"编号、原因类型、排序、是否可用、添加时间","children":[],"id":"3ce7706ba9d874208588","title":"数据列表"},{"parent":"62069253fd52e628ed10","children":[],"id":"b5a558064d36c48d583d","title":"添加"},{"parent":"62069253fd52e628ed10","children":[{"parent":"a9dab4eefa926d49d008","children":[],"id":"77c1129eafd5b997","title":"编辑"},{"parent":"a9dab4eefa926d49d008","children":[],"id":"2c7b162acfb710a0","title":"删除"}],"id":"a9dab4eefa926d49d008","title":"操作"}],"collapsed":true,"id":"62069253fd52e628ed10","title":"退货原因设置"}],"root":true,"theme":"delicate_caihong","id":"root","title":"订单模块","version":289,"structure":"mind_free"}},"meta":{"exportTime":"2022-11-11 16:37:52","member":"5a210b2ee4b04f355d337104","diagramInfo":{"creator":"5a210b2ee4b04f355d337104","created":"2022-11-10 16:48:02","modified":"2022-11-11 10:37:32","title":"订单模块","category":"mind_free"},"id":"636cbac26376897f2b155b9b","type":"ProcessOn Schema File","version":"1.0"}} ================================================ FILE: document/pos/pms.pos ================================================ {"diagram":{"image":{"x":0,"width":200,"y":0,"pngdata":"iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAsUlEQVR4nO3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8GXHmAAFMgHIEAAAAAElFTkSuQmCC","height":200},"elements":{"leftChildren":[],"children":[{"parent":"root","lineStyle":{"randomLineColor":"#BE49C4"},"children":[{"parent":"b0193c3521eaf8a10ed6","note":"名称、货号、分类、品牌、上架状态、审核状态","children":[],"collapsed":false,"id":"afd97cc98a82ffb89757","title":"筛选搜索"},{"parent":"b0193c3521eaf8a10ed6","note":"编号、商品图片、商品名称、价格\\货号、标签、排序、SKU库存、销量、审核状态","children":[],"collapsed":false,"id":"12612e018e9a0526d448","title":"数据列表"},{"parent":"b0193c3521eaf8a10ed6","children":[{"parent":"9e3d797bcab8e3f942bd","children":[],"id":"c1e1fc1bc48de3aa","title":"查看"},{"parent":"9e3d797bcab8e3f942bd","children":[],"id":"c4f7563e8e6919d1","title":"编辑"},{"parent":"9e3d797bcab8e3f942bd","children":[],"id":"a213bd6c064b5cda","title":"日志"},{"parent":"9e3d797bcab8e3f942bd","children":[],"id":"47bdb6d8dffb2ee9","title":"删除"}],"collapsed":true,"id":"9e3d797bcab8e3f942bd","title":"操作"},{"parent":"b0193c3521eaf8a10ed6","children":[{"parent":"16914295265bdffc1064","children":[],"id":"5b6717e914b56339","title":"上下架"},{"parent":"16914295265bdffc1064","children":[],"id":"a1b7055145994b96","title":"推荐"},{"parent":"16914295265bdffc1064","children":[],"id":"1917ac40ba9149eb","title":"新品"},{"parent":"16914295265bdffc1064","children":[],"id":"213632c8ddfe63fa","title":"转移分类"}],"collapsed":true,"id":"16914295265bdffc1064","title":"批量操作"}],"collapsed":false,"id":"b0193c3521eaf8a10ed6","title":"商品列表"},{"parent":"root","lineStyle":{"randomLineColor":"#02ACF4"},"children":[{"parent":"4578b2205b9273061cb6","children":[{"parent":"150b7fae65c85ffc65c1","children":[],"id":"01e1bfb6ac3f2a34665f","title":"商品分类"},{"parent":"150b7fae65c85ffc65c1","children":[],"id":"8548078d1c762a336c8f","title":"商品名称"},{"parent":"150b7fae65c85ffc65c1","children":[],"id":"54c42179cfe1777184e8","title":"副标题"},{"parent":"150b7fae65c85ffc65c1","children":[],"id":"c2b450a1cb3f6360613b","title":"商品品牌"},{"parent":"150b7fae65c85ffc65c1","children":[],"id":"dd50b3150f3cf2c98159","title":"商品介绍"},{"parent":"150b7fae65c85ffc65c1","children":[],"id":"bad04fb4c667270ef0bc","title":"商品货号"},{"parent":"150b7fae65c85ffc65c1","children":[],"id":"628556f625622f778633","title":"商品价格"},{"parent":"150b7fae65c85ffc65c1","children":[],"id":"bde367e24e1ecec5","title":"商品库存"}],"collapsed":true,"id":"150b7fae65c85ffc65c1","title":"填写商品信息"},{"parent":"4578b2205b9273061cb6","children":[{"parent":"eb970a0ca5d901effeaa","children":[],"id":"ba7a238e5b6b0f3d","title":"积分"},{"parent":"eb970a0ca5d901effeaa","children":[],"id":"add191749e24973d","title":"成长值"},{"parent":"eb970a0ca5d901effeaa","children":[],"id":"12d896a0ff03f042","title":"上下架状态"},{"parent":"eb970a0ca5d901effeaa","children":[{"parent":"be5f56e1a733a494","children":[],"id":"d8e05c5688ffb4e4","title":"新品"},{"parent":"be5f56e1a733a494","children":[],"id":"a6e8e9c8dd6a1840","title":"推荐"}],"collapsed":true,"id":"be5f56e1a733a494","title":"商品标签"},{"parent":"eb970a0ca5d901effeaa","children":[],"id":"4790d2c6ef88240c","title":"服务保证"},{"parent":"eb970a0ca5d901effeaa","children":[],"id":"058db134cf6a8b86","title":"商品详细页内容"},{"parent":"eb970a0ca5d901effeaa","children":[{"parent":"c71531fc19b4628c","children":[],"id":"053ba4d08bbcb9e8","title":"特惠促销"},{"parent":"c71531fc19b4628c","children":[],"id":"a401f6ab1582e34d","title":"会员价格"},{"parent":"c71531fc19b4628c","children":[],"id":"877d93b933c03a3a","title":"阶梯价格"},{"parent":"c71531fc19b4628c","children":[],"id":"a42342a809f6614e","title":"满减价格"}],"collapsed":true,"id":"c71531fc19b4628c","title":"优惠方式"}],"collapsed":true,"id":"eb970a0ca5d901effeaa","title":"填写商品促销"},{"parent":"4578b2205b9273061cb6","children":[{"parent":"f39ae019cc793b23a550","children":[],"id":"80e8ec4a8d3f6f765ce8","title":"属性类型"},{"parent":"f39ae019cc793b23a550","children":[{"parent":"7338aeb3f9d92cc8","children":[],"id":"657ba1546025c843","title":"SKU属性"},{"parent":"7338aeb3f9d92cc8","children":[],"id":"94ddd46d7a557420","title":"价格"},{"parent":"7338aeb3f9d92cc8","children":[],"id":"75c053323a443450","title":"库存"}],"id":"7338aeb3f9d92cc8","title":"商品规格"},{"parent":"f39ae019cc793b23a550","children":[],"id":"d10f746e017ce12f4af7","title":"属性图片"},{"parent":"f39ae019cc793b23a550","children":[],"id":"51f591c54dd6b2f45cf3","title":"商品参数"},{"parent":"f39ae019cc793b23a550","note":"可上传5张,第一张为主图","children":[],"id":"b78ee701cb4d30b8046e","title":"商品相册"},{"parent":"f39ae019cc793b23a550","note":"使用富文本编辑器,直接批量上传切图","children":[{"parent":"0e0d32d1de9c0d89f090","children":[],"id":"c5ca9a1fc3808321","title":"电脑端详情"},{"parent":"0e0d32d1de9c0d89f090","children":[],"id":"91a414ef18e602d8","title":"移动端详情"}],"id":"0e0d32d1de9c0d89f090","title":"商品详情"}],"collapsed":true,"id":"f39ae019cc793b23a550","title":"填写商品属性"},{"parent":"4578b2205b9273061cb6","children":[{"parent":"410f282a6a011559cd7e","children":[],"id":"5e1055a2c14e6ac1333b","title":"关联专题"},{"parent":"410f282a6a011559cd7e","children":[],"id":"c0aa38689d4f6dd98e89","title":"关联优选"}],"collapsed":true,"id":"410f282a6a011559cd7e","title":"选择商品关联"}],"collapsed":false,"id":"4578b2205b9273061cb6","title":"添加商品"},{"parent":"root","lineStyle":{"randomLineColor":"#7549C5"},"children":[{"parent":"7a6d301f9278f2630c2a","note":"编号、分类名、级别、数量、单位、导航栏、是否显示、排序","children":[],"collapsed":false,"id":"38ae08dc614fc8af1ea5","title":"数据列表"},{"parent":"7a6d301f9278f2630c2a","children":[{"parent":"e4c0b0ee759251371957","children":[],"id":"2290b6f08e405c8a","title":"查看下级"},{"parent":"e4c0b0ee759251371957","children":[],"id":"54f789fb49bb8b5c","title":"转移商品"}],"collapsed":true,"id":"e4c0b0ee759251371957","title":"设置"},{"parent":"7a6d301f9278f2630c2a","children":[{"parent":"99c5cec73311b9484c09","children":[],"id":"1d9a161ad7407b18","title":"编辑"},{"parent":"99c5cec73311b9484c09","children":[],"id":"934b181044de07b1","title":"删除"}],"collapsed":true,"id":"99c5cec73311b9484c09","title":"操作"},{"parent":"7a6d301f9278f2630c2a","note":"分类名称、上级分类、数量单位、排序、是否显示、是否显示在导航栏、分类图标、筛选属性、关键字、分类描述","children":[],"collapsed":false,"id":"8442f155f8cce6ee1fd3","title":"添加分类"}],"collapsed":false,"id":"7a6d301f9278f2630c2a","title":"商品分类"},{"parent":"root","lineStyle":{"randomLineColor":"#FD5155"},"children":[{"parent":"39f28c9e5a207c982746","children":[{"parent":"e88242482ef0584009bd","note":"编号、名称、属性数量、参数数量","children":[],"collapsed":false,"id":"9a8cae1b0cb9eecab2d9","title":"数据列表"},{"parent":"e88242482ef0584009bd","children":[{"parent":"5a1a503374d30b7377e0","note":"编号、属性名称、商品类型、属性是否可选、属性录入方式、可选值列表、排序、操作","children":[],"id":"d53c5691019e3c4a1d2c","title":"属性或属性列表"},{"parent":"5a1a503374d30b7377e0","children":[{"parent":"4c9c4b6411a35464eb2b","children":[],"id":"420dcd0e046932e4efbd","title":"属性名称"},{"parent":"4c9c4b6411a35464eb2b","children":[],"id":"60cc4e6c1d2b731ecce5","title":"商品类型"},{"parent":"4c9c4b6411a35464eb2b","children":[],"id":"eeec949bd8e1d5bd705d","title":"分类筛选样式"},{"parent":"4c9c4b6411a35464eb2b","children":[],"id":"8e1dbcb9a5a927d63cc8","title":"能否进行检索"},{"parent":"4c9c4b6411a35464eb2b","children":[],"id":"222a6dc9d98482784a28","title":"相同属性值的商品是否关联"},{"parent":"4c9c4b6411a35464eb2b","children":[],"id":"a62a03df100eb0261d76","title":"属性是否可选"},{"parent":"4c9c4b6411a35464eb2b","children":[],"id":"60f0394a180cfb7d8d38","title":"该属性值的录入方式"},{"parent":"4c9c4b6411a35464eb2b","children":[],"id":"735a214b97ab1edffdda","title":"属性值可选值列表"},{"parent":"4c9c4b6411a35464eb2b","children":[],"id":"0af81046f778a255766f","title":"是否支持手动新增"},{"parent":"4c9c4b6411a35464eb2b","children":[],"id":"4d397f72061fd8b994a4","title":"属性排序"}],"collapsed":true,"id":"4c9c4b6411a35464eb2b","title":"添加属性或参数"}],"collapsed":false,"id":"5a1a503374d30b7377e0","title":"设置"},{"parent":"e88242482ef0584009bd","children":[{"parent":"172f19e2a225095b","children":[],"id":"606c1e69b5bd85a3","title":"编辑"},{"parent":"172f19e2a225095b","children":[],"id":"fe364906958220e9","title":"删除"}],"id":"172f19e2a225095b","title":"操作"},{"parent":"e88242482ef0584009bd","children":[{"parent":"6eefb44138a35973b7c7","children":[],"id":"269fe5284562ba30","title":"编辑"},{"parent":"6eefb44138a35973b7c7","children":[],"id":"18cb5d523283607e","title":"删除"}],"collapsed":true,"id":"6eefb44138a35973b7c7","title":"操作"}],"collapsed":true,"id":"e88242482ef0584009bd","title":"商品类型"},{"parent":"39f28c9e5a207c982746","children":[],"id":"d7b41c3bc685d007","title":"添加类型"}],"collapsed":false,"id":"39f28c9e5a207c982746","title":"商品类型"},{"parent":"root","lineStyle":{"randomLineColor":"#0F80C4"},"children":[{"parent":"7d93cc9472646cd2f5eb","note":"关键字","children":[],"id":"805f7bafc84c6c2dbafd","title":"筛选搜索"},{"parent":"7d93cc9472646cd2f5eb","note":"编号、品牌名称、品牌首字母、排序、品牌制造商、是否显示、商品数量及评价数量","children":[],"id":"3dfb4349b642c35a850b","title":"数据列表"},{"parent":"7d93cc9472646cd2f5eb","children":[{"parent":"6c942115a541b0753b68","children":[],"id":"f19a25d8cb05949c","title":"显示"},{"parent":"6c942115a541b0753b68","children":[],"id":"5e62d6928cee3dfe","title":"隐藏"}],"collapsed":true,"id":"6c942115a541b0753b68","title":"批量操作"},{"parent":"7d93cc9472646cd2f5eb","children":[{"parent":"7698f312546878c2891f","children":[],"id":"7ee6e839884c9312","title":"品牌名称"},{"parent":"7698f312546878c2891f","children":[],"id":"58bf03255dc50aa1","title":"品牌首字母"},{"parent":"7698f312546878c2891f","children":[],"id":"02e832a254208c19","title":"品牌LOGO"},{"parent":"7698f312546878c2891f","children":[],"id":"9a5fd84ed7a1a2a6","title":"品牌专区大图"},{"parent":"7698f312546878c2891f","children":[],"id":"2365ee6d54460ce5","title":"品牌故事"},{"parent":"7698f312546878c2891f","children":[],"id":"824cde85b57ede2a","title":"排序"},{"parent":"7698f312546878c2891f","children":[],"id":"23839f67608c5e67","title":"是否显示"},{"parent":"7698f312546878c2891f","children":[],"id":"2e941dacd17c024a","title":"是否为品牌制造商"}],"collapsed":true,"id":"7698f312546878c2891f","title":"添加品牌"}],"collapsed":false,"id":"7d93cc9472646cd2f5eb","title":"品牌管理"}],"root":true,"theme":"delicate_caihong","id":"root","title":"商品模块","version":422,"structure":"mind_free"}},"meta":{"exportTime":"2022-11-11 16:38:12","member":"5a210b2ee4b04f355d337104","diagramInfo":{"creator":"5a210b2ee4b04f355d337104","created":"2022-11-10 16:07:31","modified":"2022-11-11 10:37:08","title":"商品模块","category":"mind_free"},"id":"636cb1431e085317c6a36b06","type":"ProcessOn Schema File","version":"1.0"}} ================================================ FILE: document/pos/sms.pos ================================================ {"diagram":{"image":{"x":0,"width":200,"y":0,"pngdata":"iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAsUlEQVR4nO3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8GXHmAAFMgHIEAAAAAElFTkSuQmCC","height":200},"elements":{"leftChildren":[],"children":[{"parent":"root","lineStyle":{"randomLineColor":"#FCB52A"},"children":[{"parent":"545764a25a8548f0ddf3","note":"关键字","children":[],"id":"9a3ca43a68aa95e2","title":"筛选搜索"},{"parent":"545764a25a8548f0ddf3","note":"编号、活动标题、活动状态、开始时间、结束时间、上下线状态","children":[],"collapsed":false,"id":"a0e352814d1082aafd1f","title":"数据列表"},{"parent":"545764a25a8548f0ddf3","note":"活动标题、开始时间、结束时间、上下线状态","children":[],"id":"8b248c2d5fae2bcdf4d8","title":"添加活动"},{"parent":"545764a25a8548f0ddf3","children":[{"parent":"6db653dfba561795","children":[],"id":"b71e19827bba9f4f","title":"编辑"},{"parent":"6db653dfba561795","children":[],"id":"ae8dafe8f0a89fc0","title":"删除"}],"collapsed":true,"id":"6db653dfba561795","title":"操作"},{"parent":"545764a25a8548f0ddf3","children":[{"parent":"6db45d3fe49ce8b0860d","children":[{"parent":"f777bf175fe6a34e0760","children":[],"id":"fd3d69627249b6e05f79","title":"显示商品列表"}],"collapsed":false,"id":"f777bf175fe6a34e0760","title":"显示时间段列表"},{"parent":"6db45d3fe49ce8b0860d","children":[{"parent":"d8517e5dc277361b","note":"编号、商品名称、货号、商品价格、剩余数量,设置秒杀价格、秒杀数量、限购数量、排序","children":[],"id":"2323b83d9a361b94","title":"数据列表"},{"parent":"d8517e5dc277361b","note":"直接展示上架商品信息","children":[],"id":"ccdb30253f6b63d9","title":"添加商品"}],"id":"d8517e5dc277361b","title":"选择商品"}],"collapsed":true,"id":"6db45d3fe49ce8b0860d","title":"设置商品"},{"parent":"545764a25a8548f0ddf3","children":[{"parent":"fb60edaba84f13c6bc2d","note":"编号、名称、开始时间、结束时间、启用状态","children":[],"id":"c03b022a719a0a770462","title":"数据列表"},{"parent":"fb60edaba84f13c6bc2d","note":"名称、开始时间、结束时间、启用状态","children":[],"id":"0aabec9051d8f19e","title":"添加时间段"},{"parent":"fb60edaba84f13c6bc2d","children":[{"parent":"d20e8ec6263ebadeef80","children":[],"id":"b86daff650a125dc","title":"编辑"},{"parent":"d20e8ec6263ebadeef80","children":[],"id":"3159b3d5db632aa1","title":"删除"}],"collapsed":true,"id":"d20e8ec6263ebadeef80","title":"操作"}],"collapsed":true,"id":"fb60edaba84f13c6bc2d","title":"秒杀时间段列表"}],"collapsed":false,"id":"545764a25a8548f0ddf3","title":"秒杀活动列表"},{"parent":"root","lineStyle":{"randomLineColor":"#FD5155"},"children":[{"parent":"42b2258bf954dabd6df8","note":"优惠券名称、优惠券类型","children":[],"id":"867a06d3c21fe70e","title":"筛选搜索"},{"parent":"42b2258bf954dabd6df8","note":"编号、名称、类型、可使用商品、使用门槛、面值、适用平台、有效期、状态","children":[],"id":"c061afe595360693","title":"数据列表"},{"parent":"42b2258bf954dabd6df8","note":"名称、类型、可使用商品、使用门槛、面值、适用平台、有效期、状态、总发行量、每人限领、领取时间、备注","children":[],"id":"3565eac051844296","title":"添加"},{"parent":"42b2258bf954dabd6df8","children":[{"parent":"c648f7624727f8fc","children":[],"id":"ae53acc6fc8b556e","title":"编辑"},{"parent":"c648f7624727f8fc","children":[],"id":"3847c6afc720c970","title":"删除"}],"collapsed":true,"id":"c648f7624727f8fc","title":"操作"},{"parent":"42b2258bf954dabd6df8","children":[{"parent":"8c1359eba32f936f","children":[],"id":"bc0a934e179877cc","title":"优惠券信息"},{"parent":"8c1359eba32f936f","note":"使用状态、订单编号","children":[],"id":"51466c9d2e5b4460","title":"筛选搜索"},{"parent":"8c1359eba32f936f","note":"优惠码、领取会员、领取方式、领取时间、当前状态、使用时间、订单编号
","children":[],"id":"78f03b618041d6cb","title":"优惠券领取、使用历史"}],"collapsed":true,"id":"8c1359eba32f936f","title":"查看"}],"collapsed":false,"id":"42b2258bf954dabd6df8","title":"优惠券列表"},{"parent":"root","lineStyle":{"randomLineColor":"#7549C5"},"children":[{"parent":"dc46ba4e39fdab24","note":"品牌名称、推荐状态","children":[],"id":"58f25242fff694cf","title":"筛选搜索"},{"parent":"dc46ba4e39fdab24","note":"编号、品牌名称、是否推荐、排序、状态","children":[],"id":"f46070a601e81e48","title":"数据列表"},{"parent":"dc46ba4e39fdab24","children":[{"parent":"def2896b07bb19fc","children":[],"id":"9b1c6dbe6f6bf443","title":"设置排序"},{"parent":"def2896b07bb19fc","children":[],"id":"8ee429ee489a4e6d","title":"删除"}],"collapsed":true,"id":"def2896b07bb19fc","title":"操作"},{"parent":"dc46ba4e39fdab24","note":"从品牌列表选择品牌并添加","children":[],"id":"1c8b467481694967","title":"选择品牌"}],"id":"dc46ba4e39fdab24","title":"品牌推荐"},{"parent":"root","lineStyle":{"randomLineColor":"#F88A35"},"children":[{"parent":"22ebf929115001a4","note":"商品名称、推荐状态","children":[],"id":"49a40f8396367eb9","title":"筛选搜索"},{"parent":"22ebf929115001a4","note":"编号、商品名称、是否推荐、排序、状态","children":[],"id":"a487e128c49dc7dc","title":"数据列表"},{"parent":"22ebf929115001a4","children":[{"parent":"e6cb76fe705b439c","children":[],"id":"42ad60941e2ae867","title":"设置排序"},{"parent":"e6cb76fe705b439c","children":[],"id":"23f9ba432308714b","title":"删除"}],"collapsed":true,"id":"e6cb76fe705b439c","title":"操作"},{"parent":"22ebf929115001a4","note":"从商品列表选择商品添加","children":[],"id":"f5ca69c68f34d53f","title":"选择商品"}],"id":"22ebf929115001a4","title":"新品推荐"},{"parent":"root","lineStyle":{"randomLineColor":"#F88A35"},"children":[{"parent":"bc48cdc5b44c","note":"商品名称、推荐状态","children":[],"id":"8c6293e3bd90","title":"筛选搜索"},{"parent":"bc48cdc5b44c","note":"编号、商品名称、是否推荐、排序、状态","children":[],"id":"df2bb98bb7d3","title":"数据列表"},{"parent":"bc48cdc5b44c","children":[{"parent":"ab3036201d87","children":[],"id":"0463fc3a452e","title":"设置排序"},{"parent":"ab3036201d87","children":[],"id":"fc793528c4f9","title":"删除"}],"collapsed":true,"id":"ab3036201d87","title":"操作"},{"parent":"bc48cdc5b44c","note":"从商品列表选择商品添加","children":[],"id":"2debbb1fc80d","title":"选择商品"}],"id":"bc48cdc5b44c","title":"人气推荐"},{"parent":"root","lineStyle":{"randomLineColor":"#F88A35"},"children":[{"parent":"71d9a7e1542c","note":"专题名称、推荐状态","children":[],"id":"18bf1418a142","title":"筛选搜索"},{"parent":"71d9a7e1542c","note":"编号、专题名称、是否推荐、排序、状态","children":[],"id":"84b73f86d56d","title":"数据列表"},{"parent":"71d9a7e1542c","children":[{"parent":"c8374dd2c84f","children":[],"id":"cbabf9b4bda6","title":"设置排序"},{"parent":"c8374dd2c84f","children":[],"id":"335f63d58965","title":"删除"}],"collapsed":true,"id":"c8374dd2c84f","title":"操作"},{"parent":"71d9a7e1542c","note":"从专题列表选择商品添加","children":[],"id":"8ee248bc24ca","title":"选择专题"}],"id":"71d9a7e1542c","title":"专题推荐"},{"parent":"root","lineStyle":{"randomLineColor":"#DD489D"},"children":[{"parent":"e7481c76661d765a","note":"广告名称、广告位置、到期时间","children":[],"id":"4ee2072e0faa15db","title":"筛选搜索"},{"parent":"e7481c76661d765a","note":"编号、广告名称、广告位置、广告图片、时间、上下线状态、点击次数、生成订单
","children":[],"id":"c67fd79aa7efe4d2","title":"数据列表"},{"parent":"e7481c76661d765a","children":[{"parent":"1f992e078b526d5d","children":[],"id":"c8b980dc234876c5","title":"编辑"},{"parent":"1f992e078b526d5d","children":[],"id":"55f98013908eba09","title":"删除"}],"collapsed":true,"id":"1f992e078b526d5d","title":"操作"},{"parent":"e7481c76661d765a","note":"广告名称、广告位置、广告图片、开始时间、到期时间、上下线状态、排序、广告链接、广告备注","children":[],"id":"1daeff8cf3eed6b7","title":"添加广告"}],"id":"e7481c76661d765a","title":"广告列表"}],"root":true,"theme":"delicate_caihong","id":"root","title":"营销模块","version":282,"structure":"mind_free"}},"meta":{"exportTime":"2022-11-11 16:38:30","member":"5a210b2ee4b04f355d337104","diagramInfo":{"creator":"5a210b2ee4b04f355d337104","created":"2022-11-11 10:39:06","modified":"2022-11-11 14:47:44","title":"营销模块","category":"mind_free"},"id":"636db5ca1e085317c6a53f00","type":"ProcessOn Schema File","version":"1.0"}} ================================================ FILE: document/pos/ums.pos ================================================ {"diagram":{"image":{"x":0,"width":200,"y":0,"pngdata":"iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAsUlEQVR4nO3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8GXHmAAFMgHIEAAAAAElFTkSuQmCC","height":200},"elements":{"leftChildren":[],"children":[{"parent":"root","lineStyle":{"randomLineColor":"#BE49C4"},"children":[{"parent":"25ad9c354241","note":"账号、姓名","children":[],"id":"a534c5c1431d","title":"筛选搜索"},{"parent":"25ad9c354241","note":"账号、姓名、邮箱、添加时间、最后登录、是否启用","children":[],"id":"515a998fac21","title":"数据列表"},{"parent":"25ad9c354241","note":"账号、姓名、邮箱、密码、备注、是否启用","children":[],"id":"a642fabedf3e6922","title":"添加"},{"parent":"25ad9c354241","children":[{"parent":"bf1e014fe073d036","children":[],"id":"4afff98845184f42","title":"编辑"},{"parent":"bf1e014fe073d036","children":[],"id":"e2c445685b95cb5f","title":"删除"}],"collapsed":true,"id":"bf1e014fe073d036","title":"操作"},{"parent":"25ad9c354241","note":"显示可选角色分配、支持多选","children":[],"id":"a1e34965a8c6fbf5","title":"分配角色"}],"id":"25ad9c354241","title":"用户列表"},{"parent":"root","lineStyle":{"randomLineColor":"#F88A35"},"children":[{"parent":"952f1880cc08","note":"角色名称","children":[],"id":"b517aa49a95c","title":"筛选搜索"},{"parent":"952f1880cc08","note":"编号、角色名称、描述、用户数、添加时间、是否启用","children":[],"id":"22842dfed78c","title":"数据列表"},{"parent":"952f1880cc08","note":"角色名称、描述、是否启用","children":[],"id":"5bb3fe6364cc1e40","title":"添加"},{"parent":"952f1880cc08","children":[{"parent":"9b2645159961ff82","children":[],"id":"83862e5d17271f78","title":"编辑"},{"parent":"9b2645159961ff82","children":[],"id":"1272ef7243e92251","title":"删除"}],"collapsed":true,"id":"9b2645159961ff82","title":"操作"},{"parent":"952f1880cc08","note":"树形结构显示菜单,可多选","children":[],"id":"eb1e75861d5cae27","title":"分配菜单"},{"parent":"952f1880cc08","note":"按类别显示权限,可多选","children":[],"id":"3b7aba1f8357e6a1","title":"分配资源"}],"id":"952f1880cc08","title":"角色列表"},{"parent":"root","lineStyle":{"randomLineColor":"#0FBAB0"},"children":[{"parent":"3b3191eb67b3","note":"编号、菜单名称、菜单级别、前端名称、前端图标、是否显示、排序、设置","children":[],"id":"42fc29e1f387","title":"数据列表"},{"parent":"3b3191eb67b3","note":"菜单名称、上级菜单、前端名称、前端图标、是否显示、排序","children":[],"id":"00626f4222e6","title":"添加"},{"parent":"3b3191eb67b3","children":[{"parent":"3a776c35d2f021e3","children":[],"id":"f3b3af838cfef8d4","title":"编辑"},{"parent":"3a776c35d2f021e3","children":[],"id":"6d6b093b4438d8f8","title":"删除"}],"collapsed":true,"id":"3a776c35d2f021e3","title":"操作"}],"id":"3b3191eb67b3","title":"菜单列表"},{"parent":"root","lineStyle":{"randomLineColor":"#7549C5"},"children":[{"parent":"b4546a3318d6","note":"资源名称、资源路径、资源分类","children":[],"id":"412b02a45895aefd","title":"筛选搜索"},{"parent":"b4546a3318d6","note":"编号、资源名称、资源路径、描述、添加时间","children":[],"id":"0623f40ddcecbc96","title":"数据列表"},{"parent":"b4546a3318d6","children":[{"parent":"5b3fa9a2f9cc0cd5","note":"编号、名称、创建时间、排序","children":[],"id":"1b293388acc42812","title":"数据列表"},{"parent":"5b3fa9a2f9cc0cd5","note":"名称、排序","children":[],"id":"be7ac6bcba2ac473","title":"添加"},{"parent":"5b3fa9a2f9cc0cd5","children":[{"parent":"75d57415ae3b9703","children":[],"id":"7249e10514bc74a0","title":"编辑"},{"parent":"75d57415ae3b9703","children":[],"id":"75832b8fb6058bb4","title":"删除"}],"id":"75d57415ae3b9703","title":"操作"}],"collapsed":true,"id":"5b3fa9a2f9cc0cd5","title":"资源分类"},{"parent":"b4546a3318d6","note":"资源名称、资源路径、资源分类、描述","children":[],"id":"52df9a20039f228b","title":"添加"},{"parent":"b4546a3318d6","children":[{"parent":"dfe91e487b4ce974","children":[],"id":"9fa0b4dea66f1650","title":"编辑"},{"parent":"dfe91e487b4ce974","children":[],"id":"2480ed5cd92998ae","title":"删除"}],"collapsed":true,"id":"dfe91e487b4ce974","title":"操作"}],"id":"b4546a3318d6","title":"资源列表"}],"root":true,"theme":"delicate_caihong","id":"root","title":"权限模块","version":125,"structure":"mind_right","summaries":[]}},"meta":{"exportTime":"2022-11-11 16:38:03","member":"5a210b2ee4b04f355d337104","diagramInfo":{"creator":"5a210b2ee4b04f355d337104","created":"2022-11-11 14:49:00","modified":"2022-11-11 15:01:34","title":"权限模块","category":"mind_free"},"id":"636df05c5653bb3a847979ce","type":"ProcessOn Schema File","version":"1.0"}} ================================================ FILE: document/sql/mall_tiny.sql ================================================ /* Navicat Premium Data Transfer Source Server : localhost Source Server Type : MySQL Source Server Version : 50719 Source Host : localhost:3306 Source Schema : mall_tiny Target Server Type : MySQL Target Server Version : 50719 File Encoding : 65001 Date: 08/12/2022 16:02:33 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for pms_brand -- ---------------------------- DROP TABLE IF EXISTS `pms_brand`; CREATE TABLE `pms_brand` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `first_letter` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '首字母', `sort` int(11) NULL DEFAULT NULL, `factory_status` int(1) NULL DEFAULT NULL COMMENT '是否为品牌制造商:0->不是;1->是', `show_status` int(1) NULL DEFAULT NULL, `product_count` int(11) NULL DEFAULT NULL COMMENT '产品数量', `product_comment_count` int(11) NULL DEFAULT NULL COMMENT '产品评论数量', `logo` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '品牌logo', `big_pic` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '专区大图', `brand_story` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '品牌故事', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 60 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '品牌表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of pms_brand -- ---------------------------- INSERT INTO `pms_brand` VALUES (1, '万和', 'W', 0, 1, 1, 100, 100, 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20200607/5b07ca8aN4e127d2f.jpg', 'http://img13.360buyimg.com/cms/jfs/t1/121860/35/2430/187800/5ec4e294E22f3ffcc/1e233b65b94ba192.jpg', '万和成立于1993年8月,总部位于广东顺德国家级高新技术开发区内,是国内生产规模最大的燃气具专业制造企业,也是中国燃气具发展战略的首倡者和推动者、中国五金制品协会燃气用具分会第三届理事长单位。'); INSERT INTO `pms_brand` VALUES (2, '三星', 'S', 100, 1, 1, 100, 100, 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20200607/57201b47N7bf15715.jpg', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221108/sanxing_banner_01.png', '三星集团(英文:SAMSUNG、韩文:삼성)是韩国最大的跨国企业集团,三星集团包括众多的国际下属企业,旗下子公司有:三星电子、三星物产、三星人寿保险等,业务涉及电子、金融、机械、化学等众多领域。'); INSERT INTO `pms_brand` VALUES (3, '华为', 'H', 100, 1, 1, 100, 100, 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20200607/5abf6f26N31658aa2.jpg', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221108/huawei_banner_01.png', '荣耀品牌成立于2013年,是华为旗下手机双品牌之一。荣耀以“创新、品质、服务”为核心战略,为全球年轻人提供潮酷的全场景智能化体验,打造年轻人向往的先锋文化和潮流生活方式'); INSERT INTO `pms_brand` VALUES (4, '格力', 'G', 30, 1, 1, 100, 100, 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/timg (3).jpg', NULL, 'Victoria\'s Secret的故事'); INSERT INTO `pms_brand` VALUES (5, '方太', 'F', 20, 1, 1, 100, 100, 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/timg (4).jpg', NULL, 'Victoria\'s Secret的故事'); INSERT INTO `pms_brand` VALUES (6, '小米', 'M', 500, 1, 1, 100, 100, 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20200607/5565f5a2N0b8169ae.jpg', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221108/xiaomi_banner_01.png', '小米公司正式成立于2010年4月,是一家专注于高端智能手机、互联网电视自主研发的创新型科技企业。主要由前谷歌、微软、摩托、金山等知名公司的顶尖人才组建。'); INSERT INTO `pms_brand` VALUES (21, 'OPPO', 'O', 0, 1, 1, 88, 500, 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/timg(6).jpg', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221108/oppo_banner_01.png', 'OPPO于2008年推出第一款“笑脸手机”,由此开启探索和引领至美科技之旅。今天,OPPO凭借以Find和R系列手机为核心的智能终端产品,以及OPPO+等互联网服务,让全球消费者尽享至美科技。'); INSERT INTO `pms_brand` VALUES (49, '七匹狼', 'S', 200, 1, 1, 77, 400, 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190525/qipilang.png', NULL, 'BOOB的故事'); INSERT INTO `pms_brand` VALUES (50, '海澜之家', 'H', 200, 1, 1, 66, 300, 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20200607/5a5c69b9N5d6c5696.jpg', 'http://img10.360buyimg.com/cms/jfs/t1/133148/4/1605/470028/5edaf5ccEd7a687a9/e0a007631361ff75.jpg', '“海澜之家”(英文缩写:HLA)是海澜之家股份有限公司旗下的服装品牌,总部位于中国江苏省无锡市江阴市,主要采用连锁零售的模式,销售男性服装、配饰与相关产品。'); INSERT INTO `pms_brand` VALUES (51, '苹果', 'A', 200, 1, 1, 55, 200, 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20200607/49b30bb0377030d1.jpg', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221108/apple_banner_01.png', '苹果公司(Apple Inc. )是美国的一家高科技公司。 由史蒂夫·乔布斯、斯蒂夫·沃兹尼亚克和罗·韦恩(Ron Wayne)等人于1976年4月1日创立,并命名为美国苹果电脑公司(Apple Computer Inc. ),2007年1月9日更名为苹果公司,总部位于加利福尼亚州的...'); INSERT INTO `pms_brand` VALUES (58, 'NIKE', 'N', 0, 1, 0, 33, 100, 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/timg (51).jpg', '', 'NIKE的故事'); INSERT INTO `pms_brand` VALUES (59, '测试品牌', 'C', 0, 0, 0, NULL, NULL, 'http://localhost:9000/mall/20220609/Snipaste_2022-06-08_14-35-53.png', 'http://localhost:9000/mall/20220609/biji_05.jpg', '12345'); -- ---------------------------- -- Table structure for pms_product -- ---------------------------- DROP TABLE IF EXISTS `pms_product`; CREATE TABLE `pms_product` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `brand_id` bigint(20) NULL DEFAULT NULL, `product_category_id` bigint(20) NULL DEFAULT NULL, `feight_template_id` bigint(20) NULL DEFAULT NULL, `product_attribute_category_id` bigint(20) NULL DEFAULT NULL, `name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `pic` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `product_sn` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '货号', `delete_status` int(1) NULL DEFAULT NULL COMMENT '删除状态:0->未删除;1->已删除', `publish_status` int(1) NULL DEFAULT NULL COMMENT '上架状态:0->下架;1->上架', `new_status` int(1) NULL DEFAULT NULL COMMENT '新品状态:0->不是新品;1->新品', `recommand_status` int(1) NULL DEFAULT NULL COMMENT '推荐状态;0->不推荐;1->推荐', `verify_status` int(1) NULL DEFAULT NULL COMMENT '审核状态:0->未审核;1->审核通过', `sort` int(11) NULL DEFAULT NULL COMMENT '排序', `sale` int(11) NULL DEFAULT NULL COMMENT '销量', `price` decimal(10, 2) NULL DEFAULT NULL, `promotion_price` decimal(10, 2) NULL DEFAULT NULL COMMENT '促销价格', `gift_growth` int(11) NULL DEFAULT 0 COMMENT '赠送的成长值', `gift_point` int(11) NULL DEFAULT 0 COMMENT '赠送的积分', `use_point_limit` int(11) NULL DEFAULT NULL COMMENT '限制使用的积分数', `sub_title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '副标题', `description` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '商品描述', `original_price` decimal(10, 2) NULL DEFAULT NULL COMMENT '市场价', `stock` int(11) NULL DEFAULT NULL COMMENT '库存', `low_stock` int(11) NULL DEFAULT NULL COMMENT '库存预警值', `unit` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '单位', `weight` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品重量,默认为克', `preview_status` int(1) NULL DEFAULT NULL COMMENT '是否为预告商品:0->不是;1->是', `service_ids` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '以逗号分割的产品服务:1->无忧退货;2->快速退款;3->免费包邮', `keywords` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `note` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `album_pics` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '画册图片,连产品图片限制为5张,以逗号分割', `detail_title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `detail_desc` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL, `detail_html` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '产品详情网页内容', `detail_mobile_html` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '移动端网页详情', `promotion_start_time` datetime NULL DEFAULT NULL COMMENT '促销开始时间', `promotion_end_time` datetime NULL DEFAULT NULL COMMENT '促销结束时间', `promotion_per_limit` int(11) NULL DEFAULT NULL COMMENT '活动限购数量', `promotion_type` int(1) NULL DEFAULT NULL COMMENT '促销类型:0->没有促销使用原价;1->使用促销价;2->使用会员价;3->使用阶梯价格;4->使用满减价格;5->限时购', `brand_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '品牌名称', `product_category_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品分类名称', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 46 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '商品信息' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of pms_product -- ---------------------------- INSERT INTO `pms_product` VALUES (1, 49, 7, 0, 0, '银色星芒刺绣网纱底裤', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86577', 1, 1, 1, 1, 1, 100, 0, 100.00, NULL, 0, 100, NULL, '111', '111', 120.00, 100, 20, '件', 1000.00, 0, NULL, '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', NULL, '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', NULL, NULL, NULL, 0, '七匹狼', '外套'); INSERT INTO `pms_product` VALUES (2, 49, 7, 0, 0, '银色星芒刺绣网纱底裤2', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86578', 1, 1, 1, 1, 1, 1, 0, 100.00, NULL, 0, 100, NULL, '111', '111', 120.00, 100, 20, '件', 1000.00, 0, NULL, '银色星芒刺绣网纱底裤2', '银色星芒刺绣网纱底裤', NULL, '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', '

银色星芒刺绣网纱底裤

', '

银色星芒刺绣网纱底裤

', NULL, NULL, NULL, 0, '七匹狼', '外套'); INSERT INTO `pms_product` VALUES (3, 1, 7, 0, 0, '银色星芒刺绣网纱底裤3', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86579', 1, 1, 1, 1, 1, 1, 0, 100.00, NULL, 0, 100, NULL, '111', '111', 120.00, 100, 20, '件', 1000.00, 0, NULL, '银色星芒刺绣网纱底裤3', '银色星芒刺绣网纱底裤', NULL, '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', NULL, NULL, NULL, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (4, 1, 7, 0, 0, '银色星芒刺绣网纱底裤4', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86580', 1, 1, 1, 1, 1, 1, 0, 100.00, NULL, 0, 100, NULL, '111', '111', 120.00, 100, 20, '件', 1000.00, 0, NULL, '银色星芒刺绣网纱底裤4', '银色星芒刺绣网纱底裤', NULL, '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', NULL, NULL, NULL, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (5, 1, 7, 0, 0, '银色星芒刺绣网纱底裤5', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86581', 1, 0, 1, 1, 1, 1, 0, 100.00, NULL, 0, 100, NULL, '111', '111', 120.00, 100, 20, '件', 1000.00, 0, NULL, '银色星芒刺绣网纱底裤5', '银色星芒刺绣网纱底裤', NULL, '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', NULL, NULL, NULL, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (6, 1, 7, 0, 0, '银色星芒刺绣网纱底裤6', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86582', 1, 1, 1, 1, 1, 1, 0, 100.00, NULL, 0, 100, NULL, '111', '111', 120.00, 100, 20, '件', 1000.00, 0, NULL, '银色星芒刺绣网纱底裤6', '银色星芒刺绣网纱底裤', NULL, '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', '银色星芒刺绣网纱底裤', NULL, NULL, NULL, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (7, 1, 7, 0, 1, '女式超柔软拉毛运动开衫', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86577', 1, 0, 0, 0, 0, 0, 0, 249.00, 0.00, 0, 100, 0, '匠心剪裁,垂感质地', '匠心剪裁,垂感质地', 299.00, 100, 0, '件', 0.00, 0, 'string', '女式超柔软拉毛运动开衫', 'string', 'string', 'string', 'string', 'string', 'string', '2018-04-26 10:41:03', '2018-04-26 10:41:03', 0, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (8, 1, 7, 0, 1, '女式超柔软拉毛运动开衫1', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86577', 1, 0, 0, 0, 0, 0, 0, 249.00, 0.00, 0, 100, 0, '匠心剪裁,垂感质地', '匠心剪裁,垂感质地', 299.00, 100, 0, '件', 0.00, 0, 'string', '女式超柔软拉毛运动开衫', 'string', 'string', 'string', 'string', 'string', 'string', '2018-04-26 10:41:03', '2018-04-26 10:41:03', 0, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (9, 1, 7, 0, 1, '女式超柔软拉毛运动开衫1', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86577', 1, 0, 0, 0, 0, 0, 0, 249.00, 0.00, 0, 100, 0, '匠心剪裁,垂感质地', '匠心剪裁,垂感质地', 299.00, 100, 0, '件', 0.00, 0, 'string', '女式超柔软拉毛运动开衫', 'string', 'string', 'string', 'string', 'string', 'string', '2018-04-26 10:41:03', '2018-04-26 10:41:03', 0, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (10, 1, 7, 0, 1, '女式超柔软拉毛运动开衫1', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86577', 1, 0, 0, 0, 0, 0, 0, 249.00, 0.00, 0, 100, 0, '匠心剪裁,垂感质地', '匠心剪裁,垂感质地', 299.00, 100, 0, '件', 0.00, 0, 'string', '女式超柔软拉毛运动开衫', 'string', 'string', 'string', 'string', 'string', 'string', '2018-04-26 10:41:03', '2018-04-26 10:41:03', 0, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (11, 1, 7, 0, 1, '女式超柔软拉毛运动开衫1', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86577', 1, 1, 0, 1, 0, 0, 0, 249.00, 0.00, 0, 100, 0, '匠心剪裁,垂感质地', '匠心剪裁,垂感质地', 299.00, 100, 0, '件', 0.00, 0, 'string', '女式超柔软拉毛运动开衫', 'string', 'string', 'string', 'string', 'string', 'string', '2018-04-26 10:41:03', '2018-04-26 10:41:03', 0, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (12, 1, 7, 0, 1, '女式超柔软拉毛运动开衫2', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86577', 1, 1, 0, 1, 0, 0, 0, 249.00, 0.00, 0, 100, 0, '匠心剪裁,垂感质地', '匠心剪裁,垂感质地', 299.00, 100, 0, '件', 0.00, 0, 'string', '女式超柔软拉毛运动开衫', 'string', 'string', 'string', 'string', 'string', 'string', '2018-04-26 10:41:03', '2018-04-26 10:41:03', 0, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (13, 1, 7, 0, 1, '女式超柔软拉毛运动开衫3', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86577', 1, 1, 0, 1, 0, 0, 0, 249.00, 0.00, 0, 100, 0, '匠心剪裁,垂感质地', '匠心剪裁,垂感质地', 299.00, 100, 0, '件', 0.00, 0, 'string', '女式超柔软拉毛运动开衫', 'string', 'string', 'string', 'string', 'string', 'string', '2018-04-26 10:41:03', '2018-04-26 10:41:03', 0, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (14, 1, 7, 0, 1, '女式超柔软拉毛运动开衫3', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86577', 1, 0, 0, 1, 0, 0, 0, 249.00, 0.00, 0, 100, 0, '匠心剪裁,垂感质地', '匠心剪裁,垂感质地', 299.00, 100, 0, '件', 0.00, 0, 'string', '女式超柔软拉毛运动开衫', 'string', 'string', 'string', 'string', 'string', 'string', '2018-04-26 10:41:03', '2018-04-26 10:41:03', 0, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (18, 1, 7, 0, 1, '女式超柔软拉毛运动开衫3', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180522/web.png', 'No86577', 1, 0, 0, 1, 0, 0, 0, 249.00, 0.00, 0, 100, 0, '匠心剪裁,垂感质地', '匠心剪裁,垂感质地', 299.00, 100, 0, '件', 0.00, 0, 'string', '女式超柔软拉毛运动开衫', 'string', 'string', 'string', 'string', 'string', 'string', '2018-04-26 10:41:03', '2018-04-26 10:41:03', 0, 0, '万和', '外套'); INSERT INTO `pms_product` VALUES (22, 6, 7, 0, 1, 'test', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180604/1522738681.jpg', '', 1, 1, 0, 0, 0, 0, 0, 0.00, NULL, 0, 0, 0, 'test', '', 0.00, 100, 0, '', 0.00, 1, '1,2', '', '', '', '', '', '', '', NULL, NULL, 0, 0, '小米', '外套'); INSERT INTO `pms_product` VALUES (23, 6, 19, 0, 1, '毛衫测试', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180604/1522738681.jpg', 'NO.1098', 1, 1, 1, 1, 0, 0, 0, 99.00, NULL, 99, 99, 1000, '毛衫测试11', 'xxx', 109.00, 100, 0, '件', 1000.00, 1, '1,2,3', '毛衫测试', '毛衫测试', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180604/1522738681.jpg,http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180604/1522738681.jpg', '毛衫测试', '毛衫测试', '

', '', NULL, NULL, 0, 2, '小米', '手机数码'); INSERT INTO `pms_product` VALUES (24, 6, 7, 0, NULL, 'xxx', '', '', 1, 0, 0, 0, 0, 0, 0, 0.00, NULL, 0, 0, 0, 'xxx', '', 0.00, 100, 0, '', 0.00, 0, '', '', '', '', '', '', '', '', NULL, NULL, 0, 0, '小米', '外套'); INSERT INTO `pms_product` VALUES (26, 3, 19, 0, 3, '华为 HUAWEI P20 ', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/5ac1bf58Ndefaac16.jpg', '6946605', 0, 1, 1, 1, 0, 100, 100, 3788.00, NULL, 3788, 3788, 0, 'AI智慧全面屏 6GB +64GB 亮黑色 全网通版 移动联通电信4G手机 双卡双待手机 双卡双待', '', 4288.00, 1000, 0, '件', 0.00, 1, '2,3,1', '', '', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/5ab46a3cN616bdc41.jpg,http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/5ac1bf5fN2522b9dc.jpg', '', '', '

', '

\"\"

\n

\"\"

', NULL, NULL, 0, 4, '华为', '手机通讯'); INSERT INTO `pms_product` VALUES (27, 6, 19, 0, 3, '小米8 全面屏游戏智能手机 6GB+64GB 黑色 全网通4G 双卡双待', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/xiaomi.jpg', '7437788', 0, 1, 1, 1, 0, 0, 99, 2699.00, NULL, 2699, 2699, 0, '骁龙845处理器,红外人脸解锁,AI变焦双摄,AI语音助手小米6X低至1299,点击抢购', '小米8 全面屏游戏智能手机 6GB+64GB 黑色 全网通4G 双卡双待', 2699.00, 100, 0, '', 0.00, 0, '1', '', '', '', '', '', '

', '

', NULL, NULL, 0, 3, '小米', '手机通讯'); INSERT INTO `pms_product` VALUES (28, 6, 19, 0, 3, '小米 红米5A 全网通版 3GB+32GB 香槟金 移动联通电信4G手机 双卡双待', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5a9d248cN071f4959.jpg', '7437789', 0, 1, 1, 1, 0, 0, 98, 649.00, NULL, 649, 649, 0, '8天超长待机,137g轻巧机身,高通骁龙处理器小米6X低至1299,点击抢购', '', 649.00, 100, 0, '', 0.00, 0, '', '', '', '', '', '', '', '
\n

 

\n
', NULL, NULL, 0, 4, '小米', '手机通讯'); INSERT INTO `pms_product` VALUES (29, 51, 19, 0, 3, 'Apple iPhone 8 Plus 64GB 红色特别版 移动联通电信4G手机', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5acc5248N6a5f81cd.jpg', '7437799', 0, 1, 1, 1, 0, 0, 97, 5499.00, 4799.00, 5499, 5499, 0, '【限时限量抢购】Apple产品年中狂欢节,好物尽享,美在智慧!速来 >> 勾选[保障服务][原厂保2年],获得AppleCare+全方位服务计划,原厂延保售后无忧。', '', 5499.00, 100, 0, '', 0.00, 0, '1,2,3', '', '', '', '', '', '', '
', '2020-05-04 15:12:54', '2020-05-30 00:00:00', 0, 1, '苹果', '手机通讯'); INSERT INTO `pms_product` VALUES (30, 50, 8, 0, 1, 'HLA海澜之家简约动物印花短袖T恤', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5ad83a4fN6ff67ecd.jpg!cc_350x449.jpg', 'HNTBJ2E042A', 0, 1, 1, 1, 0, 0, 0, 98.00, NULL, 0, 0, 0, '2018夏季新品微弹舒适新款短T男生 6月6日-6月20日,满300减30,参与互动赢百元礼券,立即分享赢大奖', '', 98.00, 100, 0, '', 0.00, 0, '', '', '', '', '', '', '', '', NULL, NULL, 0, 0, '海澜之家', 'T恤'); INSERT INTO `pms_product` VALUES (31, 50, 8, 0, 1, 'HLA海澜之家蓝灰花纹圆领针织布短袖T恤', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5ac98b64N70acd82f.jpg!cc_350x449.jpg', 'HNTBJ2E080A', 0, 1, 0, 0, 0, 0, 0, 98.00, NULL, 0, 0, 0, '2018夏季新品短袖T恤男HNTBJ2E080A 蓝灰花纹80 175/92A/L80A 蓝灰花纹80 175/92A/L', '', 98.00, 100, 0, '', 0.00, 0, '', '', '', '', '', '', '', '', NULL, NULL, 0, 0, '海澜之家', 'T恤'); INSERT INTO `pms_product` VALUES (32, 50, 8, 0, 1, 'HLA海澜之家短袖T恤男基础款', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5a51eb88Na4797877.jpg', 'HNTBJ2E153A', 0, 1, 0, 0, 0, 0, 0, 68.00, NULL, 0, 0, 0, 'HLA海澜之家短袖T恤男基础款简约圆领HNTBJ2E153A藏青(F3)175/92A(50)', '', 68.00, 100, 0, '', 0.00, 0, '1,2', '', '', '', '', '', '', '', NULL, NULL, 0, 0, '海澜之家', 'T恤'); INSERT INTO `pms_product` VALUES (33, 6, 35, 0, 12, '小米(MI)小米电视4A ', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5b02804dN66004d73.jpg', '4609652', 0, 1, 0, 0, 0, 0, 0, 2499.00, NULL, 0, 0, 0, '小米(MI)小米电视4A 55英寸 L55M5-AZ/L55M5-AD 2GB+8GB HDR 4K超高清 人工智能网络液晶平板电视', '', 2499.00, 100, 0, '', 0.00, 0, '', '', '', '', '', '', '', '', NULL, NULL, 0, 0, '小米', '电视'); INSERT INTO `pms_product` VALUES (34, 6, 35, 0, 12, '小米(MI)小米电视4A 65英寸', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5b028530N51eee7d4.jpg', '4609660', 0, 1, 0, 0, 0, 0, 0, 3999.00, NULL, 0, 0, 0, ' L65M5-AZ/L65M5-AD 2GB+8GB HDR 4K超高清 人工智能网络液晶平板电视', '', 3999.00, 100, 0, '', 0.00, 0, '1,2', '', '', '', '', '', '', '', NULL, NULL, 0, 0, '小米', '电视'); INSERT INTO `pms_product` VALUES (35, 58, 29, 0, 11, '耐克NIKE 男子 休闲鞋 ROSHE RUN 运动鞋 511881-010黑色41码', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5b235bb9Nf606460b.jpg', '6799342', 0, 1, 0, 0, 0, 0, 0, 369.00, NULL, 0, 0, 0, '耐克NIKE 男子 休闲鞋 ROSHE RUN 运动鞋 511881-010黑色41码', '', 369.00, 100, 0, '', 0.00, 0, '', '', '', '', '', '', '', '', NULL, NULL, 0, 0, 'NIKE', '男鞋'); INSERT INTO `pms_product` VALUES (36, 58, 29, 0, 11, '耐克NIKE 男子 气垫 休闲鞋 AIR MAX 90 ESSENTIAL 运动鞋 AJ1285-101白色41码', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5b19403eN9f0b3cb8.jpg', '6799345', 0, 1, 1, 1, 0, 0, 0, 499.00, NULL, 0, 0, 0, '耐克NIKE 男子 气垫 休闲鞋 AIR MAX 90 ESSENTIAL 运动鞋 AJ1285-101白色41码', '', 499.00, 100, 0, '', 0.00, 0, '', '', '', '', '', '', '', '', NULL, NULL, 0, 0, 'NIKE', '男鞋'); INSERT INTO `pms_product` VALUES (37, 51, 19, 0, 3, 'Apple iPhone 14 (A2884) 128GB 支持移动联通电信5G 双卡双待手机', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221028/iphone14_001.jpg', '100038005189', 0, 1, 0, 0, 0, 200, 0, 5999.00, NULL, 0, 0, 0, '【11.11大爱超大爱】指定iPhone14产品限时限量领券立减601元!!!部分iPhone产品现货抢购确认收货即送原厂手机壳10元优惠券!!!猛戳 ', '', 5999.00, 1000, 0, '', 208.00, 0, '1,2,3', '', '', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221028/iphone14_002.jpg,http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221028/iphone14_003.jpg,http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221028/iphone14_004.jpg', '', '', '', '
', NULL, NULL, 0, 0, '苹果', '手机通讯'); INSERT INTO `pms_product` VALUES (38, 51, 53, 0, 3, 'Apple iPad 10.9英寸平板电脑 2022年款', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221028/ipad_001.jpg', '100044025833', 0, 1, 0, 0, 0, 0, 0, 3599.00, NULL, 0, 0, 0, '【11.11大爱超大爱】iPad9代限量抢购,价格优惠,更享以旧换新至高补贴325元!!快来抢购吧!! ', '', 3599.00, 1000, 0, '', 0.00, 0, '1,2,3', '', '', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221028/ipad_002.jpg', '', '', '', '
', NULL, NULL, 0, 0, '苹果', '平板电脑'); INSERT INTO `pms_product` VALUES (39, 6, 54, 0, 13, '小米 Xiaomi Book Pro 14 2022 锐龙版 2.8K超清大师屏 高端轻薄笔记本电脑', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221028/xiaomi_computer_001.jpg', '100023207945', 0, 1, 0, 1, 0, 0, 0, 5599.00, NULL, 0, 0, 0, '【双十一大促来袭】指定型号至高优惠1000,以旧换新至高补贴1000元,晒单赢好礼', '', 5599.00, 500, 0, '', 0.00, 0, '', '', '', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221028/xiaomi_computer_002.jpg', '', '', '', '
\n
\n
 
\n
 
\n
\n
 
\n
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
 
\n
', NULL, NULL, 0, 0, '小米', '笔记本'); INSERT INTO `pms_product` VALUES (40, 6, 19, 0, 3, '小米12 Pro 天玑版 天玑9000+处理器 5000万疾速影像 2K超视感屏 120Hz高刷 67W快充', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221104/xiaomi_12_pro_01.jpg', '100027789721', 0, 1, 0, 1, 0, 0, 0, 2999.00, NULL, 0, 0, 0, '天玑9000+处理器、5160mAh大电量、2KAmoled超视感屏【点击购买小米11Ultra,戳】 ', '', 2999.00, 500, 0, '', 0.00, 0, '', '', '', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221104/xiaomi_12_pro_02.jpg', '', '', '', '

', NULL, NULL, 0, 0, '小米', '手机通讯'); INSERT INTO `pms_product` VALUES (41, 6, 19, 0, 3, 'Redmi K50 天玑8100 2K柔性直屏 OIS光学防抖 67W快充 5500mAh大电量', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221104/redmi_k50_01.jpg', '100035246702', 0, 1, 0, 0, 0, 0, 0, 2099.00, NULL, 0, 0, 0, '【品质好物】天玑8100,2K直屏,5500mAh大电量【Note12Pro火热抢购中】 ', '', 2099.00, 1000, 0, '', 0.00, 0, '', '', '', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221104/redmi_k50_02.jpg', '', '', '', '

', NULL, NULL, 0, 0, '小米', '手机通讯'); INSERT INTO `pms_product` VALUES (42, 3, 19, 0, 3, 'HUAWEI Mate 50 直屏旗舰 超光变XMAGE影像 北斗卫星消息', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221104/huawei_mate50_01.jpg', '100035295081', 0, 1, 0, 0, 0, 0, 0, 4999.00, NULL, 0, 0, 0, '【华为Mate50新品上市】内置66W华为充电套装,超光变XMAGE影像,北斗卫星消息,鸿蒙操作系统3.0!立即抢购!华为新品可持续计划,猛戳》 ', '', 4999.00, 1000, 0, '', 0.00, 0, '', '', '', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221104/huawei_mate50_02.jpg', '', '', '', '

', NULL, NULL, 0, 0, '华为', '手机通讯'); INSERT INTO `pms_product` VALUES (43, 1, 39, 0, 14, '万和(Vanward)燃气热水器天然气家用四重防冻直流变频节能全新升级增压水伺服恒温高抗风', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221104/wanhe_13L_01.png', '10044060351752', 0, 1, 0, 0, 0, 0, 0, 1799.00, NULL, 0, 0, 0, '【家电11.11享低价,抢到手价不高于1199】【发布种草秀享好礼!同价11.11买贵补差】爆款超一级能效零冷水】 ', '', 1799.00, 1000, 0, '', 0.00, 0, '', '', '', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221104/wanhe_16L_01.jpg', '', '', '', '

', NULL, NULL, 0, 0, '万和', '厨卫大电'); INSERT INTO `pms_product` VALUES (44, 2, 55, 0, 15, '三星(SAMSUNG)500GB SSD固态硬盘 M.2接口(NVMe协议)', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221108/sanxing_ssd_02.jpg', '100018768480', 0, 1, 0, 0, 0, 0, 0, 369.00, NULL, 0, 0, 0, '【满血无缓存!进店抽百元E卡,部分型号白条三期免息】兼具速度与可靠性!读速高达3500MB/s,全功率模式!点击 ', '', 369.00, 1000, 0, '', 0.00, 0, '', '', '', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221108/sanxing_ssd_01.jpg', '', '', '', '

', NULL, NULL, 0, 0, '三星', '硬盘'); INSERT INTO `pms_product` VALUES (45, 21, 19, 0, 3, 'OPPO Reno8 8GB+128GB 鸢尾紫 新配色上市 80W超级闪充 5000万水光人像三摄', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221108/oppo_r8_01.jpg', '10052147850350', 0, 1, 0, 0, 0, 0, 0, 2299.00, 999.00, 0, 0, 0, '【11.11提前购机享价保,好货不用等,系统申请一键价保补差!】【Reno8Pro爆款优惠】 ', '', 2299.00, 1000, 0, '', 0.00, 0, '', '', '', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20221108/oppo_r8_02.jpg', '', '', '', '

', '2022-11-09 16:15:50', '2022-11-25 00:00:00', 0, 4, 'OPPO', '手机通讯'); -- ---------------------------- -- Table structure for pms_product_attribute -- ---------------------------- DROP TABLE IF EXISTS `pms_product_attribute`; CREATE TABLE `pms_product_attribute` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `product_attribute_category_id` bigint(20) NULL DEFAULT NULL, `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `select_type` int(1) NULL DEFAULT NULL COMMENT '属性选择类型:0->唯一;1->单选;2->多选', `input_type` int(1) NULL DEFAULT NULL COMMENT '属性录入方式:0->手工录入;1->从列表中选取', `input_list` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '可选值列表,以逗号隔开', `sort` int(11) NULL DEFAULT NULL COMMENT '排序字段:最高的可以单独上传图片', `filter_type` int(1) NULL DEFAULT NULL COMMENT '分类筛选样式:1->普通;1->颜色', `search_type` int(1) NULL DEFAULT NULL COMMENT '检索类型;0->不需要进行检索;1->关键字检索;2->范围检索', `related_status` int(1) NULL DEFAULT NULL COMMENT '相同属性产品是否关联;0->不关联;1->关联', `hand_add_status` int(1) NULL DEFAULT NULL COMMENT '是否支持手动新增;0->不支持;1->支持', `type` int(1) NULL DEFAULT NULL COMMENT '属性的类型;0->规格;1->参数', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 74 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '商品属性参数表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of pms_product_attribute -- ---------------------------- INSERT INTO `pms_product_attribute` VALUES (1, 1, '尺寸', 2, 1, 'M,X,XL,2XL,3XL,4XL', 0, 0, 0, 0, 0, 0); INSERT INTO `pms_product_attribute` VALUES (7, 1, '颜色', 2, 1, '黑色,红色,白色,粉色', 100, 0, 0, 0, 1, 0); INSERT INTO `pms_product_attribute` VALUES (13, 0, '上市年份', 1, 1, '2013年,2014年,2015年,2016年,2017年', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (14, 0, '上市年份1', 1, 1, '2013年,2014年,2015年,2016年,2017年', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (15, 0, '上市年份2', 1, 1, '2013年,2014年,2015年,2016年,2017年', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (16, 0, '上市年份3', 1, 1, '2013年,2014年,2015年,2016年,2017年', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (17, 0, '上市年份4', 1, 1, '2013年,2014年,2015年,2016年,2017年', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (18, 0, '上市年份5', 1, 1, '2013年,2014年,2015年,2016年,2017年', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (19, 0, '适用对象', 1, 1, '青年女性,中年女性', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (20, 0, '适用对象1', 2, 1, '青年女性,中年女性', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (21, 0, '适用对象3', 2, 1, '青年女性,中年女性', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (24, 1, '商品编号', 1, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (25, 1, '适用季节', 1, 1, '春季,夏季,秋季,冬季', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (32, 2, '适用人群', 0, 1, '老年,青年,中年', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (33, 2, '风格', 0, 1, '嘻哈风格,基础大众,商务正装', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (35, 2, '颜色', 0, 0, '', 100, 0, 0, 0, 1, 0); INSERT INTO `pms_product_attribute` VALUES (37, 1, '适用人群', 1, 1, '儿童,青年,中年,老年', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (38, 1, '上市时间', 1, 1, '2017年秋,2017年冬,2018年春,2018年夏', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (39, 1, '袖长', 1, 1, '短袖,长袖,中袖', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (40, 2, '尺码', 0, 1, '29,30,31,32,33,34', 0, 0, 0, 0, 0, 0); INSERT INTO `pms_product_attribute` VALUES (41, 2, '适用场景', 0, 1, '居家,运动,正装', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (42, 2, '上市时间', 0, 1, '2018年春,2018年夏', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (43, 3, '颜色', 0, 0, '', 100, 0, 0, 0, 1, 0); INSERT INTO `pms_product_attribute` VALUES (44, 3, '容量', 0, 1, '16G,32G,64G,128G,256G,512G', 0, 0, 0, 0, 0, 0); INSERT INTO `pms_product_attribute` VALUES (45, 3, '屏幕尺寸', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (46, 3, '网络', 0, 1, '3G,4G,5G,WLAN', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (47, 3, '系统', 0, 1, 'Android,IOS', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (48, 3, '电池容量', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (49, 11, '颜色', 0, 1, '红色,蓝色,绿色', 0, 1, 0, 0, 0, 0); INSERT INTO `pms_product_attribute` VALUES (50, 11, '尺寸', 0, 1, '38,39,40', 0, 0, 0, 0, 0, 0); INSERT INTO `pms_product_attribute` VALUES (51, 11, '风格', 0, 1, '夏季,秋季', 0, 0, 0, 0, 0, 0); INSERT INTO `pms_product_attribute` VALUES (52, 12, '尺寸', 0, 1, '50英寸,65英寸,70英寸', 0, 0, 0, 0, 0, 0); INSERT INTO `pms_product_attribute` VALUES (53, 12, '内存', 0, 1, '8G,16G,32G', 0, 0, 0, 0, 0, 0); INSERT INTO `pms_product_attribute` VALUES (54, 12, '商品编号', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (55, 12, '商品毛重', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (56, 12, '商品产地', 0, 1, '中国大陆,其他', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (57, 12, '电视类型', 0, 1, '大屏,教育电视,4K超清', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (58, 13, '颜色', 0, 0, '', 0, 0, 0, 0, 1, 0); INSERT INTO `pms_product_attribute` VALUES (59, 13, '版本', 0, 1, 'R7 16G 512,R5 16G 512,I5 16G 512,I7 16G 512', 0, 0, 0, 0, 0, 0); INSERT INTO `pms_product_attribute` VALUES (60, 13, '屏幕尺寸', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (61, 13, '屏幕分辨率', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (62, 13, 'CPU型号', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (63, 14, '系列', 0, 0, '', 0, 0, 0, 0, 1, 0); INSERT INTO `pms_product_attribute` VALUES (64, 14, '上市时间', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (65, 14, '毛重', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (66, 14, '额定功率', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (67, 15, '颜色', 0, 0, '', 0, 0, 0, 0, 1, 0); INSERT INTO `pms_product_attribute` VALUES (68, 15, '版本', 0, 1, '512GB,1TB', 0, 0, 0, 0, 0, 0); INSERT INTO `pms_product_attribute` VALUES (69, 15, '系列', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (70, 15, '型号', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (71, 15, '闪存类型', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (72, 15, '顺序读速', 0, 0, '', 0, 0, 0, 0, 0, 1); INSERT INTO `pms_product_attribute` VALUES (73, 15, '顺序写入', 0, 0, '', 0, 0, 0, 0, 0, 1); -- ---------------------------- -- Table structure for pms_product_attribute_value -- ---------------------------- DROP TABLE IF EXISTS `pms_product_attribute_value`; CREATE TABLE `pms_product_attribute_value` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `product_id` bigint(20) NULL DEFAULT NULL, `product_attribute_id` bigint(20) NULL DEFAULT NULL, `value` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手动添加规格或参数的值,参数单值,规格有多个时以逗号隔开', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 477 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储产品参数信息的表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of pms_product_attribute_value -- ---------------------------- INSERT INTO `pms_product_attribute_value` VALUES (1, 9, 1, 'X'); INSERT INTO `pms_product_attribute_value` VALUES (2, 10, 1, 'X'); INSERT INTO `pms_product_attribute_value` VALUES (3, 11, 1, 'X'); INSERT INTO `pms_product_attribute_value` VALUES (4, 12, 1, 'X'); INSERT INTO `pms_product_attribute_value` VALUES (5, 13, 1, 'X'); INSERT INTO `pms_product_attribute_value` VALUES (6, 14, 1, 'X'); INSERT INTO `pms_product_attribute_value` VALUES (7, 18, 1, 'X'); INSERT INTO `pms_product_attribute_value` VALUES (8, 7, 1, 'X'); INSERT INTO `pms_product_attribute_value` VALUES (9, 7, 1, 'XL'); INSERT INTO `pms_product_attribute_value` VALUES (10, 7, 1, 'XXL'); INSERT INTO `pms_product_attribute_value` VALUES (11, 22, 7, 'x,xx'); INSERT INTO `pms_product_attribute_value` VALUES (12, 22, 24, 'no110'); INSERT INTO `pms_product_attribute_value` VALUES (13, 22, 25, '春季'); INSERT INTO `pms_product_attribute_value` VALUES (14, 22, 37, '青年'); INSERT INTO `pms_product_attribute_value` VALUES (15, 22, 38, '2018年春'); INSERT INTO `pms_product_attribute_value` VALUES (16, 22, 39, '长袖'); INSERT INTO `pms_product_attribute_value` VALUES (124, 23, 7, '米白色,浅黄色'); INSERT INTO `pms_product_attribute_value` VALUES (125, 23, 24, 'no1098'); INSERT INTO `pms_product_attribute_value` VALUES (126, 23, 25, '春季'); INSERT INTO `pms_product_attribute_value` VALUES (127, 23, 37, '青年'); INSERT INTO `pms_product_attribute_value` VALUES (128, 23, 38, '2018年春'); INSERT INTO `pms_product_attribute_value` VALUES (129, 23, 39, '长袖'); INSERT INTO `pms_product_attribute_value` VALUES (130, 1, 13, NULL); INSERT INTO `pms_product_attribute_value` VALUES (131, 1, 14, NULL); INSERT INTO `pms_product_attribute_value` VALUES (132, 1, 15, NULL); INSERT INTO `pms_product_attribute_value` VALUES (133, 1, 16, NULL); INSERT INTO `pms_product_attribute_value` VALUES (134, 1, 17, NULL); INSERT INTO `pms_product_attribute_value` VALUES (135, 1, 18, NULL); INSERT INTO `pms_product_attribute_value` VALUES (136, 1, 19, NULL); INSERT INTO `pms_product_attribute_value` VALUES (137, 1, 20, NULL); INSERT INTO `pms_product_attribute_value` VALUES (138, 1, 21, NULL); INSERT INTO `pms_product_attribute_value` VALUES (139, 2, 13, NULL); INSERT INTO `pms_product_attribute_value` VALUES (140, 2, 14, NULL); INSERT INTO `pms_product_attribute_value` VALUES (141, 2, 15, NULL); INSERT INTO `pms_product_attribute_value` VALUES (142, 2, 16, NULL); INSERT INTO `pms_product_attribute_value` VALUES (143, 2, 17, NULL); INSERT INTO `pms_product_attribute_value` VALUES (144, 2, 18, NULL); INSERT INTO `pms_product_attribute_value` VALUES (145, 2, 19, NULL); INSERT INTO `pms_product_attribute_value` VALUES (146, 2, 20, NULL); INSERT INTO `pms_product_attribute_value` VALUES (147, 2, 21, NULL); INSERT INTO `pms_product_attribute_value` VALUES (243, 30, 7, '蓝色,白色'); INSERT INTO `pms_product_attribute_value` VALUES (244, 30, 24, 'HNTBJ2E042A'); INSERT INTO `pms_product_attribute_value` VALUES (245, 30, 25, '夏季'); INSERT INTO `pms_product_attribute_value` VALUES (246, 30, 37, '青年'); INSERT INTO `pms_product_attribute_value` VALUES (247, 30, 38, '2018年夏'); INSERT INTO `pms_product_attribute_value` VALUES (248, 30, 39, '短袖'); INSERT INTO `pms_product_attribute_value` VALUES (249, 31, 7, '浅灰色,深灰色'); INSERT INTO `pms_product_attribute_value` VALUES (250, 31, 24, 'HNTBJ2E080A'); INSERT INTO `pms_product_attribute_value` VALUES (251, 31, 25, '夏季'); INSERT INTO `pms_product_attribute_value` VALUES (252, 31, 37, '青年'); INSERT INTO `pms_product_attribute_value` VALUES (253, 31, 38, '2018年夏'); INSERT INTO `pms_product_attribute_value` VALUES (254, 31, 39, '短袖'); INSERT INTO `pms_product_attribute_value` VALUES (255, 32, 7, '黑色,白色'); INSERT INTO `pms_product_attribute_value` VALUES (256, 32, 24, 'HNTBJ2E153A'); INSERT INTO `pms_product_attribute_value` VALUES (257, 32, 25, '夏季'); INSERT INTO `pms_product_attribute_value` VALUES (258, 32, 37, '青年'); INSERT INTO `pms_product_attribute_value` VALUES (259, 32, 38, '2018年夏'); INSERT INTO `pms_product_attribute_value` VALUES (260, 32, 39, '短袖'); INSERT INTO `pms_product_attribute_value` VALUES (265, 33, 54, '4609652'); INSERT INTO `pms_product_attribute_value` VALUES (266, 33, 55, '28.6kg'); INSERT INTO `pms_product_attribute_value` VALUES (267, 33, 56, '中国大陆'); INSERT INTO `pms_product_attribute_value` VALUES (268, 33, 57, '大屏'); INSERT INTO `pms_product_attribute_value` VALUES (269, 34, 54, '4609660'); INSERT INTO `pms_product_attribute_value` VALUES (270, 34, 55, '30.8kg'); INSERT INTO `pms_product_attribute_value` VALUES (271, 34, 56, '中国大陆'); INSERT INTO `pms_product_attribute_value` VALUES (272, 34, 57, '4K超清'); INSERT INTO `pms_product_attribute_value` VALUES (273, 26, 43, '金色,银色'); INSERT INTO `pms_product_attribute_value` VALUES (274, 26, 45, '5.0'); INSERT INTO `pms_product_attribute_value` VALUES (275, 26, 46, '4G'); INSERT INTO `pms_product_attribute_value` VALUES (276, 26, 47, 'Android'); INSERT INTO `pms_product_attribute_value` VALUES (277, 26, 48, '3000'); INSERT INTO `pms_product_attribute_value` VALUES (288, 27, 43, '黑色,蓝色'); INSERT INTO `pms_product_attribute_value` VALUES (289, 27, 45, '5.8'); INSERT INTO `pms_product_attribute_value` VALUES (290, 27, 46, '4G'); INSERT INTO `pms_product_attribute_value` VALUES (291, 27, 47, 'Android'); INSERT INTO `pms_product_attribute_value` VALUES (292, 27, 48, '3000ml'); INSERT INTO `pms_product_attribute_value` VALUES (303, 28, 43, '金色,银色'); INSERT INTO `pms_product_attribute_value` VALUES (304, 28, 45, '5.0'); INSERT INTO `pms_product_attribute_value` VALUES (305, 28, 46, '4G'); INSERT INTO `pms_product_attribute_value` VALUES (306, 28, 47, 'Android'); INSERT INTO `pms_product_attribute_value` VALUES (307, 28, 48, '2800ml'); INSERT INTO `pms_product_attribute_value` VALUES (308, 29, 43, '金色,银色'); INSERT INTO `pms_product_attribute_value` VALUES (309, 29, 45, '4.7'); INSERT INTO `pms_product_attribute_value` VALUES (310, 29, 46, '4G'); INSERT INTO `pms_product_attribute_value` VALUES (311, 29, 47, 'IOS'); INSERT INTO `pms_product_attribute_value` VALUES (312, 29, 48, '1960ml'); INSERT INTO `pms_product_attribute_value` VALUES (338, 37, 43, '午夜色,星光色,紫色,蓝色'); INSERT INTO `pms_product_attribute_value` VALUES (339, 37, 45, '6.1英寸'); INSERT INTO `pms_product_attribute_value` VALUES (340, 37, 46, '5G'); INSERT INTO `pms_product_attribute_value` VALUES (341, 37, 47, 'IOS'); INSERT INTO `pms_product_attribute_value` VALUES (342, 37, 48, '3000毫安'); INSERT INTO `pms_product_attribute_value` VALUES (438, 40, 43, '黑色,蓝色'); INSERT INTO `pms_product_attribute_value` VALUES (439, 40, 45, '6.73英寸'); INSERT INTO `pms_product_attribute_value` VALUES (440, 40, 46, '5G'); INSERT INTO `pms_product_attribute_value` VALUES (441, 40, 47, 'Android'); INSERT INTO `pms_product_attribute_value` VALUES (442, 40, 48, '5160mAh'); INSERT INTO `pms_product_attribute_value` VALUES (443, 38, 43, '银色,蓝色'); INSERT INTO `pms_product_attribute_value` VALUES (444, 38, 45, '10.9英寸'); INSERT INTO `pms_product_attribute_value` VALUES (445, 38, 46, 'WLAN'); INSERT INTO `pms_product_attribute_value` VALUES (446, 38, 47, 'IOS'); INSERT INTO `pms_product_attribute_value` VALUES (447, 38, 48, '6000毫安'); INSERT INTO `pms_product_attribute_value` VALUES (448, 39, 58, '新小米Pro 14英寸 2.8K屏,新Redmi Pro 15英寸 3.2K屏'); INSERT INTO `pms_product_attribute_value` VALUES (449, 39, 60, '15.6英寸'); INSERT INTO `pms_product_attribute_value` VALUES (450, 39, 61, '3200*2000'); INSERT INTO `pms_product_attribute_value` VALUES (451, 39, 62, 'R5 6600H'); INSERT INTO `pms_product_attribute_value` VALUES (452, 41, 43, '墨羽,银迹'); INSERT INTO `pms_product_attribute_value` VALUES (453, 41, 45, '6.67英寸'); INSERT INTO `pms_product_attribute_value` VALUES (454, 41, 46, '5G'); INSERT INTO `pms_product_attribute_value` VALUES (455, 41, 47, 'Android'); INSERT INTO `pms_product_attribute_value` VALUES (456, 41, 48, '5500mAh'); INSERT INTO `pms_product_attribute_value` VALUES (457, 42, 43, '曜金黑,冰霜银'); INSERT INTO `pms_product_attribute_value` VALUES (458, 42, 45, '6.7英寸'); INSERT INTO `pms_product_attribute_value` VALUES (459, 42, 46, '5G'); INSERT INTO `pms_product_attribute_value` VALUES (460, 42, 47, 'Android'); INSERT INTO `pms_product_attribute_value` VALUES (461, 42, 48, '4460mAh'); INSERT INTO `pms_product_attribute_value` VALUES (462, 43, 63, 'JSQ25-565W13【13升】【恒温旗舰款】,JSQ30-565W16【16升】【恒温旗舰款】'); INSERT INTO `pms_product_attribute_value` VALUES (463, 43, 64, '2021-05'); INSERT INTO `pms_product_attribute_value` VALUES (464, 43, 65, '15.5kg'); INSERT INTO `pms_product_attribute_value` VALUES (465, 43, 66, '30w'); INSERT INTO `pms_product_attribute_value` VALUES (466, 44, 67, '新品980|NVMe PCIe3.0*4,980 PRO|NVMe PCIe 4.0'); INSERT INTO `pms_product_attribute_value` VALUES (467, 44, 69, '980'); INSERT INTO `pms_product_attribute_value` VALUES (468, 44, 70, 'MZ-V8V500BW'); INSERT INTO `pms_product_attribute_value` VALUES (469, 44, 71, 'TLC'); INSERT INTO `pms_product_attribute_value` VALUES (470, 44, 72, '3100MB/s'); INSERT INTO `pms_product_attribute_value` VALUES (471, 44, 73, '2600MB/s'); INSERT INTO `pms_product_attribute_value` VALUES (472, 45, 43, '鸢尾紫,晴空蓝'); INSERT INTO `pms_product_attribute_value` VALUES (473, 45, 45, '6.43英寸'); INSERT INTO `pms_product_attribute_value` VALUES (474, 45, 46, '5G'); INSERT INTO `pms_product_attribute_value` VALUES (475, 45, 47, 'Android'); INSERT INTO `pms_product_attribute_value` VALUES (476, 45, 48, '4500mAh'); -- ---------------------------- -- Table structure for ums_admin -- ---------------------------- DROP TABLE IF EXISTS `ums_admin`; CREATE TABLE `ums_admin` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `icon` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '头像', `email` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱', `nick_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '昵称', `note` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注信息', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `login_time` datetime NULL DEFAULT NULL COMMENT '最后登录时间', `status` int(1) NULL DEFAULT 1 COMMENT '帐号启用状态:0->禁用;1->启用', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '后台用户表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ums_admin -- ---------------------------- INSERT INTO `ums_admin` VALUES (1, 'test', '$2a$10$NZ5o7r2E.ayT2ZoxgjlI.eJ6OEYqjH7INR/F.mXDbjZJi9HF0YCVG', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/timg.jpg', 'test@qq.com', '测试账号', NULL, '2018-09-29 13:55:30', '2018-09-29 13:55:39', 1); INSERT INTO `ums_admin` VALUES (3, 'admin', '$2a$10$.E1FokumK5GIXWgKlg.Hc.i/0/2.qdAwYFL1zc5QHdyzpXOr38RZO', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/timg.jpg', 'admin@163.com', '系统管理员', '系统管理员', '2018-10-08 13:32:47', '2019-04-20 12:45:16', 1); INSERT INTO `ums_admin` VALUES (4, 'macro', '$2a$10$Bx4jZPR7GhEpIQfefDQtVeS58GfT5n6mxs/b4nLLK65eMFa16topa', 'string', 'macro@qq.com', 'macro', 'macro专用', '2019-10-06 15:53:51', '2020-02-03 14:55:55', 1); INSERT INTO `ums_admin` VALUES (6, 'productAdmin', '$2a$10$6/.J.p.6Bhn7ic4GfoB5D.pGd7xSiD1a9M6ht6yO0fxzlKJPjRAGm', NULL, 'product@qq.com', '商品管理员', '只有商品权限', '2020-02-07 16:15:08', NULL, 1); INSERT INTO `ums_admin` VALUES (7, 'orderAdmin', '$2a$10$UqEhA9UZXjHHA3B.L9wNG.6aerrBjC6WHTtbv1FdvYPUI.7lkL6E.', NULL, 'order@qq.com', '订单管理员', '只有订单管理权限', '2020-02-07 16:15:50', NULL, 1); INSERT INTO `ums_admin` VALUES (10, 'ceshi', '$2a$10$RaaNo9CC0RSms8mc/gJpCuOWndDT4pHH0u5XgZdAAYFs1Uq4sOPRi', NULL, 'ceshi@qq.com', 'ceshi', NULL, '2020-03-13 16:23:30', NULL, 1); -- ---------------------------- -- Table structure for ums_admin_login_log -- ---------------------------- DROP TABLE IF EXISTS `ums_admin_login_log`; CREATE TABLE `ums_admin_login_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `admin_id` bigint(20) NULL DEFAULT NULL, `create_time` datetime NULL DEFAULT NULL, `ip` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `address` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `user_agent` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '浏览器登录类型', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 310 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '后台用户登录日志表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ums_admin_login_log -- ---------------------------- INSERT INTO `ums_admin_login_log` VALUES (285, 3, '2020-08-24 14:05:21', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (286, 10, '2020-08-24 14:05:39', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (287, 3, '2020-09-08 21:28:40', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (288, 3, '2021-03-28 10:38:25', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (289, 3, '2021-03-28 10:40:47', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (290, 3, '2022-04-29 09:40:54', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (291, 3, '2022-04-29 10:39:16', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (292, 3, '2022-05-06 10:45:59', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (293, 6, '2022-05-06 10:59:57', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (294, 3, '2022-05-07 16:56:22', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (295, 3, '2022-05-10 09:49:07', '127.0.0.1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (296, 3, '2022-05-10 09:52:17', '127.0.0.1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (297, 3, '2022-05-10 09:52:37', '127.0.0.1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (298, 3, '2022-05-10 10:05:09', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (299, 3, '2022-05-19 14:39:59', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (300, 3, '2022-05-19 14:46:25', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (301, 3, '2022-05-19 14:46:27', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (302, 3, '2022-05-20 16:22:54', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (303, 3, '2022-05-20 16:28:22', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (304, 3, '2022-05-25 10:39:07', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (305, 1, '2022-07-27 15:55:59', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (306, 1, '2022-07-27 15:57:02', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (307, 1, '2022-07-27 15:59:32', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (308, 1, '2022-07-28 10:44:23', '0:0:0:0:0:0:0:1', NULL, NULL); INSERT INTO `ums_admin_login_log` VALUES (309, 3, '2022-08-11 10:09:50', '0:0:0:0:0:0:0:1', NULL, NULL); -- ---------------------------- -- Table structure for ums_admin_role_relation -- ---------------------------- DROP TABLE IF EXISTS `ums_admin_role_relation`; CREATE TABLE `ums_admin_role_relation` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `admin_id` bigint(20) NULL DEFAULT NULL, `role_id` bigint(20) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 40 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '后台用户和角色关系表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ums_admin_role_relation -- ---------------------------- INSERT INTO `ums_admin_role_relation` VALUES (26, 3, 5); INSERT INTO `ums_admin_role_relation` VALUES (27, 6, 1); INSERT INTO `ums_admin_role_relation` VALUES (28, 7, 2); INSERT INTO `ums_admin_role_relation` VALUES (29, 1, 5); INSERT INTO `ums_admin_role_relation` VALUES (30, 4, 5); INSERT INTO `ums_admin_role_relation` VALUES (31, 8, 5); INSERT INTO `ums_admin_role_relation` VALUES (34, 12, 6); INSERT INTO `ums_admin_role_relation` VALUES (38, 13, 5); INSERT INTO `ums_admin_role_relation` VALUES (39, 10, 8); -- ---------------------------- -- Table structure for ums_menu -- ---------------------------- DROP TABLE IF EXISTS `ums_menu`; CREATE TABLE `ums_menu` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `parent_id` bigint(20) NULL DEFAULT NULL COMMENT '父级ID', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `title` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '菜单名称', `level` int(4) NULL DEFAULT NULL COMMENT '菜单级数', `sort` int(4) NULL DEFAULT NULL COMMENT '菜单排序', `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '前端名称', `icon` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '前端图标', `hidden` int(1) NULL DEFAULT NULL COMMENT '前端隐藏', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 26 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '后台菜单表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ums_menu -- ---------------------------- INSERT INTO `ums_menu` VALUES (1, 0, '2020-02-02 14:50:36', '商品', 0, 0, 'pms', 'product', 1); INSERT INTO `ums_menu` VALUES (2, 1, '2020-02-02 14:51:50', '商品列表', 1, 0, 'product', 'product-list', 0); INSERT INTO `ums_menu` VALUES (3, 1, '2020-02-02 14:52:44', '添加商品', 1, 0, 'addProduct', 'product-add', 0); INSERT INTO `ums_menu` VALUES (4, 1, '2020-02-02 14:53:51', '商品分类', 1, 0, 'productCate', 'product-cate', 0); INSERT INTO `ums_menu` VALUES (5, 1, '2020-02-02 14:54:51', '商品类型', 1, 0, 'productAttr', 'product-attr', 0); INSERT INTO `ums_menu` VALUES (6, 1, '2020-02-02 14:56:29', '品牌管理', 1, 0, 'brand', 'product-brand', 0); INSERT INTO `ums_menu` VALUES (7, 0, '2020-02-02 16:54:07', '订单', 0, 0, 'oms', 'order', 1); INSERT INTO `ums_menu` VALUES (8, 7, '2020-02-02 16:55:18', '订单列表', 1, 0, 'order', 'product-list', 0); INSERT INTO `ums_menu` VALUES (9, 7, '2020-02-02 16:56:46', '订单设置', 1, 0, 'orderSetting', 'order-setting', 0); INSERT INTO `ums_menu` VALUES (10, 7, '2020-02-02 16:57:39', '退货申请处理', 1, 0, 'returnApply', 'order-return', 0); INSERT INTO `ums_menu` VALUES (11, 7, '2020-02-02 16:59:40', '退货原因设置', 1, 0, 'returnReason', 'order-return-reason', 0); INSERT INTO `ums_menu` VALUES (12, 0, '2020-02-04 16:18:00', '营销', 0, 0, 'sms', 'sms', 1); INSERT INTO `ums_menu` VALUES (13, 12, '2020-02-04 16:19:22', '秒杀活动列表', 1, 0, 'flash', 'sms-flash', 0); INSERT INTO `ums_menu` VALUES (14, 12, '2020-02-04 16:20:16', '优惠券列表', 1, 0, 'coupon', 'sms-coupon', 0); INSERT INTO `ums_menu` VALUES (16, 12, '2020-02-07 16:22:38', '品牌推荐', 1, 0, 'homeBrand', 'product-brand', 0); INSERT INTO `ums_menu` VALUES (17, 12, '2020-02-07 16:23:14', '新品推荐', 1, 0, 'homeNew', 'sms-new', 0); INSERT INTO `ums_menu` VALUES (18, 12, '2020-02-07 16:26:38', '人气推荐', 1, 0, 'homeHot', 'sms-hot', 0); INSERT INTO `ums_menu` VALUES (19, 12, '2020-02-07 16:28:16', '专题推荐', 1, 0, 'homeSubject', 'sms-subject', 0); INSERT INTO `ums_menu` VALUES (20, 12, '2020-02-07 16:28:42', '广告列表', 1, 0, 'homeAdvertise', 'sms-ad', 0); INSERT INTO `ums_menu` VALUES (21, 0, '2020-02-07 16:29:13', '权限', 0, 0, 'ums', 'ums', 0); INSERT INTO `ums_menu` VALUES (22, 21, '2020-02-07 16:29:51', '用户列表', 1, 0, 'admin', 'ums-admin', 0); INSERT INTO `ums_menu` VALUES (23, 21, '2020-02-07 16:30:13', '角色列表', 1, 0, 'role', 'ums-role', 0); INSERT INTO `ums_menu` VALUES (24, 21, '2020-02-07 16:30:53', '菜单列表', 1, 0, 'menu', 'ums-menu', 0); INSERT INTO `ums_menu` VALUES (25, 21, '2020-02-07 16:31:13', '资源列表', 1, 0, 'resource', 'ums-resource', 0); -- ---------------------------- -- Table structure for ums_resource -- ---------------------------- DROP TABLE IF EXISTS `ums_resource`; CREATE TABLE `ums_resource` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '资源名称', `url` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '资源URL', `description` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述', `category_id` bigint(20) NULL DEFAULT NULL COMMENT '资源分类ID', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 30 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '后台资源表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ums_resource -- ---------------------------- INSERT INTO `ums_resource` VALUES (1, '2020-02-04 17:04:55', '商品品牌管理', '/brand/**', NULL, 1); INSERT INTO `ums_resource` VALUES (2, '2020-02-04 17:05:35', '商品属性分类管理', '/productAttribute/**', NULL, 1); INSERT INTO `ums_resource` VALUES (3, '2020-02-04 17:06:13', '商品属性管理', '/productAttribute/**', NULL, 1); INSERT INTO `ums_resource` VALUES (4, '2020-02-04 17:07:15', '商品分类管理', '/productCategory/**', NULL, 1); INSERT INTO `ums_resource` VALUES (5, '2020-02-04 17:09:16', '商品管理', '/product/**', NULL, 1); INSERT INTO `ums_resource` VALUES (6, '2020-02-04 17:09:53', '商品库存管理', '/sku/**', NULL, 1); INSERT INTO `ums_resource` VALUES (8, '2020-02-05 14:43:37', '订单管理', '/order/**', '', 2); INSERT INTO `ums_resource` VALUES (9, '2020-02-05 14:44:22', ' 订单退货申请管理', '/returnApply/**', '', 2); INSERT INTO `ums_resource` VALUES (10, '2020-02-05 14:45:08', '退货原因管理', '/returnReason/**', '', 2); INSERT INTO `ums_resource` VALUES (11, '2020-02-05 14:45:43', '订单设置管理', '/orderSetting/**', '', 2); INSERT INTO `ums_resource` VALUES (12, '2020-02-05 14:46:23', '收货地址管理', '/companyAddress/**', '', 2); INSERT INTO `ums_resource` VALUES (13, '2020-02-07 16:37:22', '优惠券管理', '/coupon/**', '', 3); INSERT INTO `ums_resource` VALUES (14, '2020-02-07 16:37:59', '优惠券领取记录管理', '/couponHistory/**', '', 3); INSERT INTO `ums_resource` VALUES (15, '2020-02-07 16:38:28', '限时购活动管理', '/flash/**', '', 3); INSERT INTO `ums_resource` VALUES (16, '2020-02-07 16:38:59', '限时购商品关系管理', '/flashProductRelation/**', '', 3); INSERT INTO `ums_resource` VALUES (17, '2020-02-07 16:39:22', '限时购场次管理', '/flashSession/**', '', 3); INSERT INTO `ums_resource` VALUES (18, '2020-02-07 16:40:07', '首页轮播广告管理', '/home/advertise/**', '', 3); INSERT INTO `ums_resource` VALUES (19, '2020-02-07 16:40:34', '首页品牌管理', '/home/brand/**', '', 3); INSERT INTO `ums_resource` VALUES (20, '2020-02-07 16:41:06', '首页新品管理', '/home/newProduct/**', '', 3); INSERT INTO `ums_resource` VALUES (21, '2020-02-07 16:42:16', '首页人气推荐管理', '/home/recommendProduct/**', '', 3); INSERT INTO `ums_resource` VALUES (22, '2020-02-07 16:42:48', '首页专题推荐管理', '/home/recommendSubject/**', '', 3); INSERT INTO `ums_resource` VALUES (23, '2020-02-07 16:44:56', ' 商品优选管理', '/prefrenceArea/**', '', 5); INSERT INTO `ums_resource` VALUES (24, '2020-02-07 16:45:39', '商品专题管理', '/subject/**', '', 5); INSERT INTO `ums_resource` VALUES (25, '2020-02-07 16:47:34', '后台用户管理', '/admin/**', '', 4); INSERT INTO `ums_resource` VALUES (26, '2020-02-07 16:48:24', '后台用户角色管理', '/role/**', '', 4); INSERT INTO `ums_resource` VALUES (27, '2020-02-07 16:48:48', '后台菜单管理', '/menu/**', '', 4); INSERT INTO `ums_resource` VALUES (28, '2020-02-07 16:49:18', '后台资源分类管理', '/resourceCategory/**', '', 4); INSERT INTO `ums_resource` VALUES (29, '2020-02-07 16:49:45', '后台资源管理', '/resource/**', '', 4); -- ---------------------------- -- Table structure for ums_resource_category -- ---------------------------- DROP TABLE IF EXISTS `ums_resource_category`; CREATE TABLE `ums_resource_category` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '分类名称', `sort` int(4) NULL DEFAULT NULL COMMENT '排序', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '资源分类表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ums_resource_category -- ---------------------------- INSERT INTO `ums_resource_category` VALUES (1, '2020-02-05 10:21:44', '商品模块', 0); INSERT INTO `ums_resource_category` VALUES (2, '2020-02-05 10:22:34', '订单模块', 0); INSERT INTO `ums_resource_category` VALUES (3, '2020-02-05 10:22:48', '营销模块', 0); INSERT INTO `ums_resource_category` VALUES (4, '2020-02-05 10:23:04', '权限模块', 0); INSERT INTO `ums_resource_category` VALUES (5, '2020-02-07 16:34:27', '内容模块', 0); INSERT INTO `ums_resource_category` VALUES (6, '2020-02-07 16:35:49', '其他模块', 0); -- ---------------------------- -- Table structure for ums_role -- ---------------------------- DROP TABLE IF EXISTS `ums_role`; CREATE TABLE `ums_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '名称', `description` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述', `admin_count` int(11) NULL DEFAULT NULL COMMENT '后台用户数量', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `status` int(1) NULL DEFAULT 1 COMMENT '启用状态:0->禁用;1->启用', `sort` int(11) NULL DEFAULT 0, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '后台用户角色表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ums_role -- ---------------------------- INSERT INTO `ums_role` VALUES (1, '商品管理员', '只能查看及操作商品', 0, '2020-02-03 16:50:37', 1, 0); INSERT INTO `ums_role` VALUES (2, '订单管理员', '只能查看及操作订单', 0, '2018-09-30 15:53:45', 1, 0); INSERT INTO `ums_role` VALUES (5, '超级管理员', '拥有所有查看和操作功能', 0, '2020-02-02 15:11:05', 1, 0); INSERT INTO `ums_role` VALUES (8, '权限管理员', '用于权限模块所有操作功能', 0, '2020-08-24 10:57:35', 1, 0); -- ---------------------------- -- Table structure for ums_role_menu_relation -- ---------------------------- DROP TABLE IF EXISTS `ums_role_menu_relation`; CREATE TABLE `ums_role_menu_relation` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `role_id` bigint(20) NULL DEFAULT NULL COMMENT '角色ID', `menu_id` bigint(20) NULL DEFAULT NULL COMMENT '菜单ID', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 111 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '后台角色菜单关系表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ums_role_menu_relation -- ---------------------------- INSERT INTO `ums_role_menu_relation` VALUES (33, 1, 1); INSERT INTO `ums_role_menu_relation` VALUES (34, 1, 2); INSERT INTO `ums_role_menu_relation` VALUES (35, 1, 3); INSERT INTO `ums_role_menu_relation` VALUES (36, 1, 4); INSERT INTO `ums_role_menu_relation` VALUES (37, 1, 5); INSERT INTO `ums_role_menu_relation` VALUES (38, 1, 6); INSERT INTO `ums_role_menu_relation` VALUES (53, 2, 7); INSERT INTO `ums_role_menu_relation` VALUES (54, 2, 8); INSERT INTO `ums_role_menu_relation` VALUES (55, 2, 9); INSERT INTO `ums_role_menu_relation` VALUES (56, 2, 10); INSERT INTO `ums_role_menu_relation` VALUES (57, 2, 11); INSERT INTO `ums_role_menu_relation` VALUES (72, 5, 1); INSERT INTO `ums_role_menu_relation` VALUES (73, 5, 2); INSERT INTO `ums_role_menu_relation` VALUES (74, 5, 3); INSERT INTO `ums_role_menu_relation` VALUES (75, 5, 4); INSERT INTO `ums_role_menu_relation` VALUES (76, 5, 5); INSERT INTO `ums_role_menu_relation` VALUES (77, 5, 6); INSERT INTO `ums_role_menu_relation` VALUES (78, 5, 7); INSERT INTO `ums_role_menu_relation` VALUES (79, 5, 8); INSERT INTO `ums_role_menu_relation` VALUES (80, 5, 9); INSERT INTO `ums_role_menu_relation` VALUES (81, 5, 10); INSERT INTO `ums_role_menu_relation` VALUES (82, 5, 11); INSERT INTO `ums_role_menu_relation` VALUES (83, 5, 12); INSERT INTO `ums_role_menu_relation` VALUES (84, 5, 13); INSERT INTO `ums_role_menu_relation` VALUES (85, 5, 14); INSERT INTO `ums_role_menu_relation` VALUES (86, 5, 16); INSERT INTO `ums_role_menu_relation` VALUES (87, 5, 17); INSERT INTO `ums_role_menu_relation` VALUES (88, 5, 18); INSERT INTO `ums_role_menu_relation` VALUES (89, 5, 19); INSERT INTO `ums_role_menu_relation` VALUES (90, 5, 20); INSERT INTO `ums_role_menu_relation` VALUES (91, 5, 21); INSERT INTO `ums_role_menu_relation` VALUES (92, 5, 22); INSERT INTO `ums_role_menu_relation` VALUES (93, 5, 23); INSERT INTO `ums_role_menu_relation` VALUES (94, 5, 24); INSERT INTO `ums_role_menu_relation` VALUES (95, 5, 25); INSERT INTO `ums_role_menu_relation` VALUES (96, 6, 21); INSERT INTO `ums_role_menu_relation` VALUES (97, 6, 22); INSERT INTO `ums_role_menu_relation` VALUES (98, 6, 23); INSERT INTO `ums_role_menu_relation` VALUES (99, 6, 24); INSERT INTO `ums_role_menu_relation` VALUES (100, 6, 25); INSERT INTO `ums_role_menu_relation` VALUES (101, 7, 21); INSERT INTO `ums_role_menu_relation` VALUES (102, 7, 22); INSERT INTO `ums_role_menu_relation` VALUES (103, 7, 23); INSERT INTO `ums_role_menu_relation` VALUES (104, 7, 24); INSERT INTO `ums_role_menu_relation` VALUES (105, 7, 25); INSERT INTO `ums_role_menu_relation` VALUES (106, 8, 21); INSERT INTO `ums_role_menu_relation` VALUES (107, 8, 22); INSERT INTO `ums_role_menu_relation` VALUES (108, 8, 23); INSERT INTO `ums_role_menu_relation` VALUES (109, 8, 24); INSERT INTO `ums_role_menu_relation` VALUES (110, 8, 25); -- ---------------------------- -- Table structure for ums_role_resource_relation -- ---------------------------- DROP TABLE IF EXISTS `ums_role_resource_relation`; CREATE TABLE `ums_role_resource_relation` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `role_id` bigint(20) NULL DEFAULT NULL COMMENT '角色ID', `resource_id` bigint(20) NULL DEFAULT NULL COMMENT '资源ID', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 216 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '后台角色资源关系表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ums_role_resource_relation -- ---------------------------- INSERT INTO `ums_role_resource_relation` VALUES (103, 2, 8); INSERT INTO `ums_role_resource_relation` VALUES (104, 2, 9); INSERT INTO `ums_role_resource_relation` VALUES (105, 2, 10); INSERT INTO `ums_role_resource_relation` VALUES (106, 2, 11); INSERT INTO `ums_role_resource_relation` VALUES (107, 2, 12); INSERT INTO `ums_role_resource_relation` VALUES (142, 5, 1); INSERT INTO `ums_role_resource_relation` VALUES (143, 5, 2); INSERT INTO `ums_role_resource_relation` VALUES (144, 5, 3); INSERT INTO `ums_role_resource_relation` VALUES (145, 5, 4); INSERT INTO `ums_role_resource_relation` VALUES (146, 5, 5); INSERT INTO `ums_role_resource_relation` VALUES (147, 5, 6); INSERT INTO `ums_role_resource_relation` VALUES (148, 5, 8); INSERT INTO `ums_role_resource_relation` VALUES (149, 5, 9); INSERT INTO `ums_role_resource_relation` VALUES (150, 5, 10); INSERT INTO `ums_role_resource_relation` VALUES (151, 5, 11); INSERT INTO `ums_role_resource_relation` VALUES (152, 5, 12); INSERT INTO `ums_role_resource_relation` VALUES (153, 5, 13); INSERT INTO `ums_role_resource_relation` VALUES (154, 5, 14); INSERT INTO `ums_role_resource_relation` VALUES (155, 5, 15); INSERT INTO `ums_role_resource_relation` VALUES (156, 5, 16); INSERT INTO `ums_role_resource_relation` VALUES (157, 5, 17); INSERT INTO `ums_role_resource_relation` VALUES (158, 5, 18); INSERT INTO `ums_role_resource_relation` VALUES (159, 5, 19); INSERT INTO `ums_role_resource_relation` VALUES (160, 5, 20); INSERT INTO `ums_role_resource_relation` VALUES (161, 5, 21); INSERT INTO `ums_role_resource_relation` VALUES (162, 5, 22); INSERT INTO `ums_role_resource_relation` VALUES (163, 5, 23); INSERT INTO `ums_role_resource_relation` VALUES (164, 5, 24); INSERT INTO `ums_role_resource_relation` VALUES (165, 5, 25); INSERT INTO `ums_role_resource_relation` VALUES (166, 5, 26); INSERT INTO `ums_role_resource_relation` VALUES (167, 5, 27); INSERT INTO `ums_role_resource_relation` VALUES (168, 5, 28); INSERT INTO `ums_role_resource_relation` VALUES (169, 5, 29); INSERT INTO `ums_role_resource_relation` VALUES (170, 1, 1); INSERT INTO `ums_role_resource_relation` VALUES (171, 1, 2); INSERT INTO `ums_role_resource_relation` VALUES (172, 1, 3); INSERT INTO `ums_role_resource_relation` VALUES (173, 1, 4); INSERT INTO `ums_role_resource_relation` VALUES (174, 1, 5); INSERT INTO `ums_role_resource_relation` VALUES (175, 1, 6); INSERT INTO `ums_role_resource_relation` VALUES (176, 1, 23); INSERT INTO `ums_role_resource_relation` VALUES (177, 1, 24); INSERT INTO `ums_role_resource_relation` VALUES (178, 6, 25); INSERT INTO `ums_role_resource_relation` VALUES (179, 6, 26); INSERT INTO `ums_role_resource_relation` VALUES (180, 6, 27); INSERT INTO `ums_role_resource_relation` VALUES (181, 6, 28); INSERT INTO `ums_role_resource_relation` VALUES (182, 6, 29); INSERT INTO `ums_role_resource_relation` VALUES (205, 7, 25); INSERT INTO `ums_role_resource_relation` VALUES (206, 7, 26); INSERT INTO `ums_role_resource_relation` VALUES (207, 7, 27); INSERT INTO `ums_role_resource_relation` VALUES (208, 7, 28); INSERT INTO `ums_role_resource_relation` VALUES (209, 7, 29); INSERT INTO `ums_role_resource_relation` VALUES (210, 7, 31); INSERT INTO `ums_role_resource_relation` VALUES (211, 8, 25); INSERT INTO `ums_role_resource_relation` VALUES (212, 8, 26); INSERT INTO `ums_role_resource_relation` VALUES (213, 8, 27); INSERT INTO `ums_role_resource_relation` VALUES (214, 8, 28); INSERT INTO `ums_role_resource_relation` VALUES (215, 8, 29); SET FOREIGN_KEY_CHECKS = 1; ================================================ FILE: mall-tiny/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny/pom.xml ================================================ 4.0.0 mall-tiny 1.0-SNAPSHOT mall-tiny Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-security io.jsonwebtoken jjwt ${jjwt.version} org.springframework.boot spring-boot-starter-data-elasticsearch org.springframework.boot spring-boot-starter-data-mongodb org.springframework.boot spring-boot-starter-amqp io.minio minio ${minio.version} org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import org.springframework.data.domain.Page; import java.util.List; /** * @auther macrozheng * @description 通用分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } /** * 将SpringData分页后的list转为分页信息 */ public static CommonPage restPage(Page pageInfo) { CommonPage result = new CommonPage(); result.setTotalPage(pageInfo.getTotalPages()); result.setPageNum(pageInfo.getNumber()); result.setPageSize(pageInfo.getSize()); result.setTotal(pageInfo.getTotalElements()); result.setList(pageInfo.getContent()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回结果封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description API返回码接口 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description API返回码封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/common/utils/JwtTokenUtil.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; /** * @auther macrozheng * @description JwtToken生成的工具类 * JWT token的格式:header.payload.signature * header的格式(算法、token的类型): * {"alg": "HS512","typ": "JWT"} * payload的格式(用户名、创建时间、生成时间): * {"sub":"wang","created":1489079981393,"exp":1489684781} * signature的生成算法: * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) * @date 2018/4/26 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/component/CancelOrderReceiver.java ================================================ package com.macro.mall.tiny.component; import com.macro.mall.tiny.service.OmsPortalOrderService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @auther macrozheng * @description 取消订单消息的接收者 * @date 2018/9/14 * @github https://github.com/macrozheng */ @Component @RabbitListener(queues = "mall.order.cancel") public class CancelOrderReceiver { private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderReceiver.class); @Autowired private OmsPortalOrderService portalOrderService; @RabbitHandler public void handle(Long orderId){ LOGGER.info("receive delay message orderId:{}",orderId); portalOrderService.cancelOrder(orderId); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/component/CancelOrderSender.java ================================================ package com.macro.mall.tiny.component; import com.macro.mall.tiny.dto.QueueEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.AmqpException; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessagePostProcessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @auther macrozheng * @description 取消订单消息的发送者 * @date 2018/9/14 * @github https://github.com/macrozheng */ @Component public class CancelOrderSender { private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderSender.class); @Autowired private AmqpTemplate amqpTemplate; public void sendMessage(Long orderId,final long delayTimes){ //给延迟队列发送消息 amqpTemplate.convertAndSend(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange(), QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey(), orderId, new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { //给消息设置延迟毫秒值 message.getMessageProperties().setExpiration(String.valueOf(delayTimes)); return message; } }); LOGGER.info("send delay message orderId:{}",orderId); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/component/JwtAuthenticationTokenFilter.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; /** * @auther macrozheng * @description JWT登录授权过滤器 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 自定义未登录或者token失效时的返回结果 * @date 2018/5/14 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 自定义无权限访问的返回结果 * @date 2018/4/26 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/config/GlobalCorsConfig.java ================================================ package com.macro.mall.tiny.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; /** * @auther macrozheng * @description 全局跨域配置 * @date 2019/7/27 * @github https://github.com/macrozheng */ @Configuration public class GlobalCorsConfig { /** * 允许跨域调用的过滤器 */ @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); //允许所有域名进行跨域调用 config.addAllowedOriginPattern("*"); //允许跨越发送cookie config.setAllowCredentials(true); //放行全部原始头信息 config.addAllowedHeader("*"); //允许所有请求方法跨域调用 config.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/config/IgnoreUrlsConfig.java ================================================ package com.macro.mall.tiny.config; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; /** * @auther macrozheng * @description 白名单资源路径配置 * @date 2018/11/5 * @github https://github.com/macrozheng */ @Getter @Setter @Configuration @ConfigurationProperties(prefix = "secure.ignored") public class IgnoreUrlsConfig { private List urls = new ArrayList<>(); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/config/MallSecurityConfig.java ================================================ package com.macro.mall.tiny.config; import com.macro.mall.tiny.domain.AdminUserDetails; 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.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; /** * @auther macrozheng * @description Mall安全自定义配置,用于配置如何获取用户信息 * @date 2022/5/20 * @github https://github.com/macrozheng */ @Configuration public class MallSecurityConfig { @Autowired private UmsAdminService adminService; @Bean public UserDetailsService userDetailsService() { //获取登录用户信息 return username -> { AdminUserDetails admin = adminService.getAdminByUsername(username); if (admin != null) { return admin; } throw new UsernameNotFoundException("用户名或密码错误"); }; } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * @auther macrozheng * @description MyBatis相关配置 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @EnableTransactionManagement @MapperScan({"com.macro.mall.tiny.mbg.mapper","com.macro.mall.tiny.dao"}) public class MyBatisConfig { } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/config/RabbitMqConfig.java ================================================ package com.macro.mall.tiny.config; import com.macro.mall.tiny.dto.QueueEnum; import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description 消息队列相关配置 * @date 2018/9/14 * @github https://github.com/macrozheng */ @Configuration public class RabbitMqConfig { /** * 订单消息实际消费队列所绑定的交换机 */ @Bean DirectExchange orderDirect() { return ExchangeBuilder .directExchange(QueueEnum.QUEUE_ORDER_CANCEL.getExchange()) .durable(true) .build(); } /** * 订单延迟队列所绑定的交换机 */ @Bean DirectExchange orderTtlDirect() { return ExchangeBuilder .directExchange(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange()) .durable(true) .build(); } /** * 订单实际消费队列 */ @Bean public Queue orderQueue() { return new Queue(QueueEnum.QUEUE_ORDER_CANCEL.getName()); } /** * 订单延迟队列(死信队列) */ @Bean public Queue orderTtlQueue() { return QueueBuilder .durable(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getName()) .withArgument("x-dead-letter-exchange", QueueEnum.QUEUE_ORDER_CANCEL.getExchange())//到期后转发的交换机 .withArgument("x-dead-letter-routing-key", QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey())//到期后转发的路由键 .build(); } /** * 将订单队列绑定到交换机 */ @Bean Binding orderBinding(DirectExchange orderDirect,Queue orderQueue){ return BindingBuilder .bind(orderQueue) .to(orderDirect) .with(QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey()); } /** * 将订单延迟队列绑定到交换机 */ @Bean Binding orderTtlBinding(DirectExchange orderTtlDirect,Queue orderTtlQueue){ return BindingBuilder .bind(orderTtlQueue) .to(orderTtlDirect) .with(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey()); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/config/RedisConfig.java ================================================ package com.macro.mall.tiny.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /** * @auther macrozheng * @description Redis相关配置 * @date 2020/3/2 * @github https://github.com/macrozheng */ @EnableCaching @Configuration public class RedisConfig { /** * redis数据库自定义key */ public static final String REDIS_KEY_DATABASE="mall"; @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisSerializer serializer = redisSerializer(); RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(serializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(serializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public RedisSerializer redisSerializer() { //创建JSON序列化器 Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //必须设置,否则无法将JSON转化为对象,会转化成Map类型 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(objectMapper); return serializer; } @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); //设置Redis缓存有效期为1天 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1)); return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/config/SecurityConfig.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.domain.AdminUserDetails; 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.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 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.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * @auther macrozheng * @description SpringSecurity相关配置 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) public class SecurityConfig { @Autowired private RestfulAccessDeniedHandler restfulAccessDeniedHandler; @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Bean SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity .authorizeRequests(); //不需要保护的资源路径允许访问 for (String url : ignoreUrlsConfig.getUrls()) { registry.antMatchers(url).permitAll(); } //允许跨域请求的OPTIONS请求 registry.antMatchers(HttpMethod.OPTIONS) .permitAll(); httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf .disable() .sessionManagement()// 基于token,所以不需要session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .anyRequest()// 除上面外的所有请求全部需要鉴权认证 .authenticated(); // 禁用缓存 httpSecurity.headers().cacheControl(); // 添加JWT filter httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); //添加自定义未授权和未登录结果返回 httpSecurity.exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler) .authenticationEntryPoint(restAuthenticationEntryPoint); return httpSecurity.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){ return new JwtAuthenticationTokenFilter(); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger文档的相关配置(带认证) * @date 2022/11/22 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .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(new Contact("macro", null, null)) .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; } @Bean public BeanPostProcessor generateBeanPostProcessor(){ return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/controller/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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 搜索商品管理Controller * @date 2018/6/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "EsProductController") @Tag(name = "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 delete(@PathVariable Long id) { esProductService.delete(id); return CommonResult.success(null); } @ApiOperation(value = "根据id批量删除商品") @RequestMapping(value = "/delete/batch", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("ids") List ids) { esProductService.delete(ids); return CommonResult.success(null); } @ApiOperation(value = "根据id创建商品") @RequestMapping(value = "/create/{id}", method = RequestMethod.POST) @ResponseBody public CommonResult create(@PathVariable Long id) { EsProduct esProduct = esProductService.create(id); if (esProduct != null) { return CommonResult.success(esProduct); } else { return CommonResult.failed(); } } @ApiOperation(value = "简单搜索") @RequestMapping(value = "/search/simple", method = RequestMethod.GET) @ResponseBody public CommonResult> search(@RequestParam(required = false) String keyword, @RequestParam(required = false, defaultValue = "0") Integer pageNum, @RequestParam(required = false, defaultValue = "5") Integer pageSize) { Page esProductPage = esProductService.search(keyword, pageNum, pageSize); return CommonResult.success(CommonPage.restPage(esProductPage)); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/controller/MemberReadHistoryController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import com.macro.mall.tiny.service.MemberReadHistoryService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 会员商品浏览记录管理Controller * @date 2018/8/3 * @github https://github.com/macrozheng */ @Controller @Api(tags = "MemberReadHistoryController") @Tag(name = "MemberReadHistoryController", description = "会员商品浏览记录管理") @RequestMapping("/member/readHistory") public class MemberReadHistoryController { @Autowired private MemberReadHistoryService memberReadHistoryService; @ApiOperation("创建浏览记录") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody public CommonResult create(@RequestBody MemberReadHistory memberReadHistory) { int count = memberReadHistoryService.create(memberReadHistory); if (count > 0) { return CommonResult.success(count); } else { return CommonResult.failed(); } } @ApiOperation("删除浏览记录") @RequestMapping(value = "/delete", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("ids") List ids) { int count = memberReadHistoryService.delete(ids); if (count > 0) { return CommonResult.success(count); } else { return CommonResult.failed(); } } @ApiOperation("展示浏览记录") @RequestMapping(value = "/list", method = RequestMethod.GET) @ResponseBody public CommonResult> list(Long memberId) { List memberReadHistoryList = memberReadHistoryService.list(memberId); return CommonResult.success(memberReadHistoryList); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/controller/MinioController.java ================================================ package com.macro.mall.tiny.controller; import cn.hutool.core.collection.CollUtil; import cn.hutool.json.JSONUtil; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.dto.BucketPolicyConfigDto; import com.macro.mall.tiny.dto.MinioUploadDto; import io.minio.*; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.text.SimpleDateFormat; import java.util.Date; /** * @auther macrozheng * @description MinIO对象存储管理Controller * @date 2019/12/25 * @github https://github.com/macrozheng */ @Controller @Api(tags = "MinioController") @Tag(name = "MinioController", description = "MinIO对象存储管理") @RequestMapping("/minio") public class MinioController { private static final Logger LOGGER = LoggerFactory.getLogger(MinioController.class); @Value("${minio.endpoint}") private String ENDPOINT; @Value("${minio.bucketName}") private String BUCKET_NAME; @Value("${minio.accessKey}") private String ACCESS_KEY; @Value("${minio.secretKey}") private String SECRET_KEY; @ApiOperation("文件上传") @RequestMapping(value = "/upload", method = RequestMethod.POST) @ResponseBody public CommonResult upload(@RequestPart("file") MultipartFile file) { try { //创建一个MinIO的Java客户端 MinioClient minioClient =MinioClient.builder() .endpoint(ENDPOINT) .credentials(ACCESS_KEY,SECRET_KEY) .build(); boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET_NAME).build()); if (isExist) { LOGGER.info("存储桶已经存在!"); } else { //创建存储桶并设置只读权限 minioClient.makeBucket(MakeBucketArgs.builder().bucket(BUCKET_NAME).build()); BucketPolicyConfigDto bucketPolicyConfigDto = createBucketPolicyConfigDto(BUCKET_NAME); SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder() .bucket(BUCKET_NAME) .config(JSONUtil.toJsonStr(bucketPolicyConfigDto)) .build(); minioClient.setBucketPolicy(setBucketPolicyArgs); } String filename = file.getOriginalFilename(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); // 设置存储对象名称 String objectName = sdf.format(new Date()) + "/" + filename; // 使用putObject上传一个文件到存储桶中 PutObjectArgs putObjectArgs = PutObjectArgs.builder() .bucket(BUCKET_NAME) .object(objectName) .contentType(file.getContentType()) .stream(file.getInputStream(), file.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE).build(); minioClient.putObject(putObjectArgs); LOGGER.info("文件上传成功!"); MinioUploadDto minioUploadDto = new MinioUploadDto(); minioUploadDto.setName(filename); minioUploadDto.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName); return CommonResult.success(minioUploadDto); } catch (Exception e) { e.printStackTrace(); LOGGER.info("上传发生错误: {}!", e.getMessage()); } return CommonResult.failed(); } /** * 创建存储桶的访问策略,设置为只读权限 */ private BucketPolicyConfigDto createBucketPolicyConfigDto(String bucketName) { BucketPolicyConfigDto.Statement statement = BucketPolicyConfigDto.Statement.builder() .Effect("Allow") .Principal("*") .Action("s3:GetObject") .Resource("arn:aws:s3:::"+bucketName+"/*.**").build(); return BucketPolicyConfigDto.builder() .Version("2012-10-17") .Statement(CollUtil.toList(statement)) .build(); } @ApiOperation("文件删除") @RequestMapping(value = "/delete", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("objectName") String objectName) { try { MinioClient minioClient = MinioClient.builder() .endpoint(ENDPOINT) .credentials(ACCESS_KEY,SECRET_KEY) .build(); minioClient.removeObject(RemoveObjectArgs.builder().bucket(BUCKET_NAME).object(objectName).build()); return CommonResult.success(null); } catch (Exception e) { e.printStackTrace(); } return CommonResult.failed(); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/controller/OmsPortalOrderController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.dto.OrderParam; import com.macro.mall.tiny.service.OmsPortalOrderService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; /** * @auther macrozheng * @description 订单管理Controller * @date 2018/8/30 * @github https://github.com/macrozheng */ @Controller @Api(tags = "OmsPortalOrderController") @Tag(name = "OmsPortalOrderController", description = "订单管理") @RequestMapping("/order") public class OmsPortalOrderController { @Autowired private OmsPortalOrderService portalOrderService; @ApiOperation("根据购物车信息生成订单") @RequestMapping(value = "/generateOrder", method = RequestMethod.POST) @ResponseBody public Object generateOrder(@RequestBody OrderParam orderParam) { return portalOrderService.generateOrder(orderParam); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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 @PreAuthorize("hasAuthority('brand:listAll')") public CommonResult> getBrandList() { return CommonResult.success(brandService.listAllBrand()); } @ApiOperation("添加品牌") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody @PreAuthorize("hasAuthority('brand:create')") 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 @PreAuthorize("hasAuthority('brand:update')") 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 @PreAuthorize("hasAuthority('brand:delete')") 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 @PreAuthorize("hasAuthority('brand:list')") 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)); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/controller/UmsAdminController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.domain.UmsResource; import com.macro.mall.tiny.service.UmsAdminService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; 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; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @auther macrozheng * @description 后台用户管理 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsAdminController") @Tag(name = "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 = "登录以后返回token") @RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public CommonResult login(@RequestParam String username, @RequestParam String password) { String token = adminService.login(username, password); if (token == null) { return CommonResult.validateFailed("用户名或密码错误"); } Map tokenMap = new HashMap<>(); tokenMap.put("token", token); tokenMap.put("tokenHead", tokenHead); return CommonResult.success(tokenMap); } @ApiOperation(value = "登录以后返回token") @RequestMapping(value = "/resourceList", method = RequestMethod.POST) @ResponseBody public CommonResult> resourceList() { List resourceList = adminService.getResourceList(); return CommonResult.success(resourceList); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/controller/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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 会员登录注册管理Controller * @date 2018/8/3 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsMemberController") @Tag(name = "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); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/dao/EsProductDao.java ================================================ package com.macro.mall.tiny.dao; import com.macro.mall.tiny.nosql.elasticsearch.document.EsProduct; import org.apache.ibatis.annotations.Param; import java.util.List; /** * @auther macrozheng * @description 搜索系统中的商品管理自定义Dao * @date 2018/6/19 * @github https://github.com/macrozheng */ public interface EsProductDao { List getAllEsProductList(@Param("id") Long id); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/domain/AdminUserDetails.java ================================================ package com.macro.mall.tiny.domain; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; 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; /** * @auther macrozheng * @description SpringSecurity用户信息封装类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode(callSuper = false) @Builder public class AdminUserDetails implements UserDetails { private String username; private String password; private List authorityList; @Override public Collection getAuthorities() { return this.authorityList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/domain/UmsResource.java ================================================ package com.macro.mall.tiny.domain; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.Date; /** *

* 后台资源表 *

* * @author macro * @since 2020-08-21 */ @Data @EqualsAndHashCode(callSuper = false) @ApiModel(value="UmsResource对象", description="后台资源表") @Builder public class UmsResource{ private Long id; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "资源名称") private String name; @ApiModelProperty(value = "资源URL") private String url; @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "资源分类ID") private Long categoryId; } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/dto/BucketPolicyConfigDto.java ================================================ package com.macro.mall.tiny.dto; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.List; /** * @auther macrozheng * @description Minio Bucket访问策略配置 * @date 2020/8/11 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode @Builder public class BucketPolicyConfigDto { private String Version; private List Statement; @Data @EqualsAndHashCode @Builder public static class Statement { private String Effect; private String Principal; private String Action; private String Resource; } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/dto/MinioUploadDto.java ================================================ package com.macro.mall.tiny.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; /** * @auther macrozheng * @description 文件上传返回结果 * @date 2019/12/25 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode public class MinioUploadDto { @ApiModelProperty("文件访问URL") private String url; @ApiModelProperty("文件名称") private String name; } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/dto/OrderParam.java ================================================ package com.macro.mall.tiny.dto; import lombok.Data; /** * @auther macrozheng * @description 生成订单时传入的参数 * @date 2018/8/30 * @github https://github.com/macrozheng */ @Data public class OrderParam { //收货地址id private Long memberReceiveAddressId; //优惠券id private Long couponId; //使用的积分数 private Integer useIntegration; //支付方式 private Integer payType; } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/dto/QueueEnum.java ================================================ package com.macro.mall.tiny.dto; import lombok.Getter; /** * @auther macrozheng * @description 消息队列枚举配置 * @date 2018/9/14 * @github https://github.com/macrozheng */ @Getter public enum QueueEnum { /** * 消息通知队列 */ QUEUE_ORDER_CANCEL("mall.order.direct", "mall.order.cancel", "mall.order.cancel"), /** * 消息通知ttl队列 */ QUEUE_TTL_ORDER_CANCEL("mall.order.direct.ttl", "mall.order.cancel.ttl", "mall.order.cancel.ttl"); /** * 交换机名称 */ private String exchange; /** * 队列名称 */ private String name; /** * 路由键 */ private String routeKey; QueueEnum(String exchange, String name, String routeKey) { this.exchange = exchange; this.name = name; this.routeKey = routeKey; } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description MBG代码生成工具 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { long countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand row); int insertSelective(PmsBrand row); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExample(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand row); int updateByPrimaryKeyWithBLOBs(PmsBrand row); int updateByPrimaryKey(PmsBrand row); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { private Long id; private String name; @ApiModelProperty(value = "首字母") private String firstLetter; private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/document/EsProduct.java ================================================ package com.macro.mall.tiny.nosql.elasticsearch.document; import lombok.Data; import lombok.EqualsAndHashCode; 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 org.springframework.data.elasticsearch.annotations.Setting; import java.io.Serializable; import java.math.BigDecimal; import java.util.List; /** * @auther macrozheng * @description 搜索商品的信息 * @date 2018/6/19 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode @Document(indexName = "pms") @Setting(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; } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/document/EsProductAttributeValue.java ================================================ package com.macro.mall.tiny.nosql.elasticsearch.document; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; import java.io.Serializable; /** * @auther macrozheng * @description 搜索商品的属性信息 * @date 2018/6/27 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode public class EsProductAttributeValue implements Serializable { private static final long serialVersionUID = 1L; private Long id; private Long productAttributeId; //属性值 @Field(type = FieldType.Keyword) private String value; //属性参数:0->规格;1->参数 private Integer type; //属性名称 @Field(type=FieldType.Keyword) private String name; } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/repository/EsProductRepository.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; /** * @auther macrozheng * @description 商品ES操作类 * @date 2018/6/19 * @github https://github.com/macrozheng */ public interface EsProductRepository extends ElasticsearchRepository { /** * 搜索查询 * * @param name 商品名称 * @param subTitle 商品标题 * @param keywords 商品关键字 * @param page 分页信息 * @return */ Page findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/nosql/mongodb/document/MemberReadHistory.java ================================================ package com.macro.mall.tiny.nosql.mongodb.document; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; /** * @auther macrozheng * @description 用户商品浏览历史记录 * @date 2018/8/3 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode @Document public class MemberReadHistory { @Id private String id; @Indexed private Long memberId; private String memberNickname; private String memberIcon; @Indexed private Long productId; private String productName; private String productPic; private String productSubTitle; private String productPrice; private Date createTime; } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/nosql/mongodb/repository/MemberReadHistoryRepository.java ================================================ package com.macro.mall.tiny.nosql.mongodb.repository; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import org.springframework.data.mongodb.repository.MongoRepository; import java.util.List; /** * @auther macrozheng * @description 会员商品浏览历史Repository * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface MemberReadHistoryRepository extends MongoRepository { /** * 根据会员id按时间倒序获取浏览记录 * @param memberId 会员id */ List findByMemberIdOrderByCreateTimeDesc(Long memberId); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/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; /** * @auther macrozheng * @description 商品搜索管理Service * @date 2018/6/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/MemberReadHistoryService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import java.util.List; /** * @auther macrozheng * @description 会员浏览记录管理Service * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface MemberReadHistoryService { /** * 生成浏览记录 */ int create(MemberReadHistory memberReadHistory); /** * 批量删除浏览记录 */ int delete(List ids); /** * 获取用户浏览历史记录 */ List list(Long memberId); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/OmsPortalOrderService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.dto.OrderParam; import org.springframework.transaction.annotation.Transactional; /** * @auther macrozheng * @description 前台订单管理Service * @date 2018/8/30 * @github https://github.com/macrozheng */ public interface OmsPortalOrderService { /** * 根据提交信息生成订单 */ @Transactional CommonResult generateOrder(OrderParam orderParam); /** * 取消单个超时订单 */ @Transactional void cancelOrder(Long orderId); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/RedisService.java ================================================ package com.macro.mall.tiny.service; import java.util.List; import java.util.Map; import java.util.Set; /** * @auther macrozheng * @description redis操作Service * @date 2020/3/3 * @github https://github.com/macrozheng */ public interface RedisService { /** * 保存属性 */ void set(String key, Object value, long time); /** * 保存属性 */ void set(String key, Object value); /** * 获取属性 */ Object get(String key); /** * 删除属性 */ Boolean del(String key); /** * 批量删除属性 */ Long del(List keys); /** * 设置过期时间 */ Boolean expire(String key, long time); /** * 获取过期时间 */ Long getExpire(String key); /** * 判断是否有该属性 */ Boolean hasKey(String key); /** * 按delta递增 */ Long incr(String key, long delta); /** * 按delta递减 */ Long decr(String key, long delta); /** * 获取Hash结构中的属性 */ Object hGet(String key, String hashKey); /** * 向Hash结构中放入一个属性 */ Boolean hSet(String key, String hashKey, Object value, long time); /** * 向Hash结构中放入一个属性 */ void hSet(String key, String hashKey, Object value); /** * 直接获取整个Hash结构 */ Map hGetAll(String key); /** * 直接设置整个Hash结构 */ Boolean hSetAll(String key, Map map, long time); /** * 直接设置整个Hash结构 */ void hSetAll(String key, Map map); /** * 删除Hash结构中的属性 */ void hDel(String key, Object... hashKey); /** * 判断Hash结构中是否有该属性 */ Boolean hHasKey(String key, String hashKey); /** * Hash结构中属性递增 */ Long hIncr(String key, String hashKey, Long delta); /** * Hash结构中属性递减 */ Long hDecr(String key, String hashKey, Long delta); /** * 获取Set结构 */ Set sMembers(String key); /** * 向Set结构中添加属性 */ Long sAdd(String key, Object... values); /** * 向Set结构中添加属性 */ Long sAdd(String key, long time, Object... values); /** * 是否为Set中的属性 */ Boolean sIsMember(String key, Object value); /** * 获取Set结构的长度 */ Long sSize(String key); /** * 删除Set结构中的属性 */ Long sRemove(String key, Object... values); /** * 获取List结构中的属性 */ List lRange(String key, long start, long end); /** * 获取List结构的长度 */ Long lSize(String key); /** * 根据索引获取List中的属性 */ Object lIndex(String key, long index); /** * 向List结构中添加属性 */ Long lPush(String key, Object value); /** * 向List结构中添加属性 */ Long lPush(String key, Object value, long time); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Object... values); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Long time, Object... values); /** * 从List结构中移除属性 */ Long lRemove(String key, long count, Object value); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/UmsAdminService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.domain.UmsResource; import java.util.List; /** * @auther macrozheng * @description 后台用户管理Service * @date 2020/10/15 * @github https://github.com/macrozheng */ public interface UmsAdminService { /** * 根据用户名获取用户信息 */ AdminUserDetails getAdminByUsername(String username); /** * 获取所以权限列表 */ List getResourceList(); /** * 用户名密码登录 */ String login(String username, String password); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/UmsMemberService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.common.api.CommonResult; /** * @auther macrozheng * @description 会员管理Service * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface UmsMemberService { /** * 生成验证码 */ CommonResult generateAuthCode(String telephone); /** * 判断验证码和手机号码是否匹配 */ CommonResult verifyAuthCode(String telephone, String authCode); } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/impl/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; /** * @auther macrozheng * @description 搜索商品管理Service实现类 * @date 2018/6/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/impl/MemberReadHistoryServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import com.macro.mall.tiny.nosql.mongodb.repository.MemberReadHistoryRepository; import com.macro.mall.tiny.service.MemberReadHistoryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @auther macrozheng * @description 会员浏览记录管理Service实现类 * @date 2018/8/3 * @github https://github.com/macrozheng */ @Service public class MemberReadHistoryServiceImpl implements MemberReadHistoryService { @Autowired private MemberReadHistoryRepository memberReadHistoryRepository; @Override public int create(MemberReadHistory memberReadHistory) { memberReadHistory.setId(null); memberReadHistory.setCreateTime(new Date()); memberReadHistoryRepository.save(memberReadHistory); return 1; } @Override public int delete(List ids) { List deleteList = new ArrayList<>(); for(String id:ids){ MemberReadHistory memberReadHistory = new MemberReadHistory(); memberReadHistory.setId(id); deleteList.add(memberReadHistory); } memberReadHistoryRepository.deleteAll(deleteList); return ids.size(); } @Override public List list(Long memberId) { return memberReadHistoryRepository.findByMemberIdOrderByCreateTimeDesc(memberId); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/impl/OmsPortalOrderServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.component.CancelOrderSender; import com.macro.mall.tiny.dto.OrderParam; import com.macro.mall.tiny.service.OmsPortalOrderService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @auther macrozheng * @description 前台订单管理Service * @date 2018/8/30 * @github https://github.com/macrozheng */ @Service public class OmsPortalOrderServiceImpl implements OmsPortalOrderService { private static Logger LOGGER = LoggerFactory.getLogger(OmsPortalOrderServiceImpl.class); @Autowired private CancelOrderSender cancelOrderSender; @Override public CommonResult generateOrder(OrderParam orderParam) { //todo 执行一系类下单操作,具体参考mall项目 LOGGER.info("process generateOrder"); //下单完成后开启一个延迟消息,用于当用户没有付款时取消订单(orderId应该在下单后生成) sendDelayMessageCancelOrder(11L); return CommonResult.success(null, "下单成功"); } @Override public void cancelOrder(Long orderId) { //todo 执行一系类取消订单操作,具体参考mall项目 LOGGER.info("process cancelOrder orderId:{}",orderId); } private void sendDelayMessageCancelOrder(Long orderId) { //获取订单超时时间,假设为60分钟(测试用的30秒) long delayTimes = 30 * 1000; //发送延迟消息 cancelOrderSender.sendMessage(orderId, delayTimes); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/impl/RedisServiceImpl.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.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @auther macrozheng * @description redis操作实现类 * @date 2020/3/3 * @github https://github.com/macrozheng */ @Service public class RedisServiceImpl implements RedisService { @Autowired private RedisTemplate redisTemplate; @Override public void set(String key, Object value, long time) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } @Override public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); } @Override public Object get(String key) { return redisTemplate.opsForValue().get(key); } @Override public Boolean del(String key) { return redisTemplate.delete(key); } @Override public Long del(List keys) { return redisTemplate.delete(keys); } @Override public Boolean expire(String key, long time) { return redisTemplate.expire(key, time, TimeUnit.SECONDS); } @Override public Long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } @Override public Boolean hasKey(String key) { return redisTemplate.hasKey(key); } @Override public Long incr(String key, long delta) { return redisTemplate.opsForValue().increment(key, delta); } @Override public Long decr(String key, long delta) { return redisTemplate.opsForValue().increment(key, -delta); } @Override public Object hGet(String key, String hashKey) { return redisTemplate.opsForHash().get(key, hashKey); } @Override public Boolean hSet(String key, String hashKey, Object value, long time) { redisTemplate.opsForHash().put(key, hashKey, value); return expire(key, time); } @Override public void hSet(String key, String hashKey, Object value) { redisTemplate.opsForHash().put(key, hashKey, value); } @Override public Map hGetAll(String key) { return redisTemplate.opsForHash().entries(key); } @Override public Boolean hSetAll(String key, Map map, long time) { redisTemplate.opsForHash().putAll(key, map); return expire(key, time); } @Override public void hSetAll(String key, Map map) { redisTemplate.opsForHash().putAll(key, map); } @Override public void hDel(String key, Object... hashKey) { redisTemplate.opsForHash().delete(key, hashKey); } @Override public Boolean hHasKey(String key, String hashKey) { return redisTemplate.opsForHash().hasKey(key, hashKey); } @Override public Long hIncr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, delta); } @Override public Long hDecr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, -delta); } @Override public Set sMembers(String key) { return redisTemplate.opsForSet().members(key); } @Override public Long sAdd(String key, Object... values) { return redisTemplate.opsForSet().add(key, values); } @Override public Long sAdd(String key, long time, Object... values) { Long count = redisTemplate.opsForSet().add(key, values); expire(key, time); return count; } @Override public Boolean sIsMember(String key, Object value) { return redisTemplate.opsForSet().isMember(key, value); } @Override public Long sSize(String key) { return redisTemplate.opsForSet().size(key); } @Override public Long sRemove(String key, Object... values) { return redisTemplate.opsForSet().remove(key, values); } @Override public List lRange(String key, long start, long end) { return redisTemplate.opsForList().range(key, start, end); } @Override public Long lSize(String key) { return redisTemplate.opsForList().size(key); } @Override public Object lIndex(String key, long index) { return redisTemplate.opsForList().index(key, index); } @Override public Long lPush(String key, Object value) { return redisTemplate.opsForList().rightPush(key, value); } @Override public Long lPush(String key, Object value, long time) { Long index = redisTemplate.opsForList().rightPush(key, value); expire(key, time); return index; } @Override public Long lPushAll(String key, Object... values) { return redisTemplate.opsForList().rightPushAll(key, values); } @Override public Long lPushAll(String key, Long time, Object... values) { Long count = redisTemplate.opsForList().rightPushAll(key, values); expire(key, time); return count; } @Override public Long lRemove(String key, long count, Object value) { return redisTemplate.opsForList().remove(key, count, value); } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/impl/UmsAdminServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.collection.CollUtil; import com.macro.mall.tiny.common.utils.JwtTokenUtil; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.domain.UmsResource; import com.macro.mall.tiny.service.UmsAdminService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; 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.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description 后台用户管理Service实现类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Slf4j @Service public class UmsAdminServiceImpl implements UmsAdminService { /** * 存放默认用户信息 */ private List adminUserDetailsList = new ArrayList<>(); /** * 存放默认资源信息 */ private List resourceList = new ArrayList<>(); @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private PasswordEncoder passwordEncoder; @PostConstruct private void init(){ adminUserDetailsList.add(AdminUserDetails.builder() .username("admin") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("brand:create","brand:update","brand:delete","brand:list","brand:listAll")) .build()); adminUserDetailsList.add(AdminUserDetails.builder() .username("macro") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("brand:listAll")) .build()); resourceList.add(UmsResource.builder() .id(1L) .name("brand:create") .url("/brand/create") .build()); resourceList.add(UmsResource.builder() .id(2L) .name("brand:update") .url("/brand/update/**") .build()); resourceList.add(UmsResource.builder() .id(3L) .name("brand:delete") .url("/brand/delete/**") .build()); resourceList.add(UmsResource.builder() .id(4L) .name("brand:list") .url("/brand/list") .build()); resourceList.add(UmsResource.builder() .id(5L) .name("brand:listAll") .url("/brand/listAll") .build()); } @Override public AdminUserDetails getAdminByUsername(String username) { List findList = adminUserDetailsList.stream().filter(item -> item.getUsername().equals(username)).collect(Collectors.toList()); if(CollUtil.isNotEmpty(findList)){ return findList.get(0); } return null; } @Override public List getResourceList() { return resourceList; } @Override public String login(String username, String password) { String token = null; try { UserDetails userDetails = getAdminByUsername(username); if(userDetails==null){ return token; } 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) { log.warn("登录异常:{}", e.getMessage()); } return token; } } ================================================ FILE: mall-tiny/src/main/java/com/macro/mall/tiny/service/impl/UmsMemberServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.util.StrUtil; 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; /** * @auther macrozheng * @description 会员管理Service实现类 * @date 2018/8/3 * @github https://github.com/macrozheng */ @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 (StrUtil.isEmpty(authCode)) { return CommonResult.failed("请输入验证码"); } String realAuthCode = (String) redisService.get(REDIS_KEY_PREFIX_AUTH_CODE + telephone); boolean result = authCode.equals(realAuthCode); if (result) { return CommonResult.success(null, "验证码校验成功"); } else { return CommonResult.failed("验证码不正确"); } } } ================================================ FILE: mall-tiny/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root servlet: multipart: enabled: true #开 启文件上传 max-file-size: 10MB # 限制文件上传大小为10M mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER redis: host: localhost # Redis服务器地址 database: 0 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 password: # Redis服务器连接密码(默认为空) lettuce: pool: max-active: 8 # 连接池最大连接数 max-idle: 8 # 连接池最大空闲连接数 min-idle: 0 # 连接池最小空闲连接数 max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制 data: elasticsearch: repositories: enabled: true # 开启ES仓库配置,自动为仓库接口生成实现类 mongodb: host: localhost # MongoDB的连接地址 port: 27017 # MongoDB的连接端口号 database: mall-port # MongoDB的连接的数据库 elasticsearch: uris: http://localhost:9200 # ES的连接地址及端口号 rabbitmq: host: localhost port: 5672 virtual-host: /mall username: mall password: mall publisher-returns: true # 消息发送到队列确认 publisher-confirm-type: simple # 消息发送到交换器确认 mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml minio: endpoint: http://localhost:9000 # MinIO服务所在地址 bucketName: mall # 存储桶名称 accessKey: minioadmin # 访问的key secretKey: minioadmin # 访问的秘钥 # 自定义redis key redis: key: prefix: authCode: "portal:authCode:" expire: authCode: 120 # 验证码超期时间 # 自定义jwt key jwt: tokenHeader: Authorization # JWT存储的请求头 secret: mySecret # JWT加解密使用的密钥 expiration: 604800 # JWT的超期限时间(60*60*24) tokenHead: Bearer # JWT负载中拿到开头 secure: ignored: urls: # 安全路径白名单 - /swagger-ui/ - /swagger-resources/** - /**/v2/api-docs - /**/*.html - /**/*.js - /**/*.css - /**/*.png - /favicon.ico - /actuator/** - /druid/** - /admin/** - /esProduct/** - /member/readHistory/** - /order/** - /minio/** - /sso/** ================================================ FILE: mall-tiny/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny/src/main/resources/dao/EsProductDao.xml ================================================ ================================================ FILE: mall-tiny/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-01/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-01/pom.xml ================================================ 4.0.0 mall-tiny-01 1.0-SNAPSHOT mall-tiny-01 Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan({"com.macro.mall.tiny.mbg.mapper","com.macro.mall.tiny.dao"}) public class MyBatisConfig { } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @RequestMapping("/brand") public class PmsBrandController { @Autowired private PmsBrandService brandService; private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class); @RequestMapping(value = "/listAll", method = RequestMethod.GET) @ResponseBody public CommonResult> getBrandList() { return CommonResult.success(brandService.listAllBrand()); } @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; } @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; } @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("操作失败"); } } @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 = brandService.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(brandService.getBrand(id)); } } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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.Field; import org.mybatis.generator.internal.DefaultCommentGenerator; import org.mybatis.generator.internal.util.StringUtility; import java.util.Properties; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; /** * 设置用户配置的参数 */ @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); } } /** * 给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(" */"); } } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { long countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand row); int insertSelective(PmsBrand row); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExample(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand row); int updateByPrimaryKeyWithBLOBs(PmsBrand row); int updateByPrimaryKey(PmsBrand row); } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import java.io.Serializable; public class PmsBrand implements Serializable { /** * 主键ID */ private Long id; /** * 名称 */ private String name; /** * 首字母 */ private String firstLetter; /** * 排序 */ private Integer sort; /** * 是否为品牌制造商:0->不是;1->是 */ private Integer factoryStatus; /** * 是否显示 */ private Integer showStatus; /** * 产品数量 */ private Integer productCount; /** * 产品评论数量 */ private Integer productCommentCount; /** * 品牌logo */ private String logo; /** * 专区大图 */ private String bigPic; /** * 品牌故事 */ private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-01/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-01/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml ================================================ FILE: mall-tiny-01/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-01/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-01/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-01/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-02/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-02/pom.xml ================================================ 4.0.0 mall-tiny-02 1.0-SNAPSHOT mall-tiny-02 Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan("com.macro.mall.tiny.mbg.mapper") public class MyBatisConfig { } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger相关配置 * @date 2022/11/23 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("SwaggerUI演示") .description("mall-tiny") .contact(new Contact("macro", null, null)) .version("1.0") .build(); } @Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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)); } } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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 MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { long countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand row); int insertSelective(PmsBrand row); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExample(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand row); int updateByPrimaryKeyWithBLOBs(PmsBrand row); int updateByPrimaryKey(PmsBrand row); } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { @ApiModelProperty(value = "主键ID") private Long id; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "首字母") private String firstLetter; @ApiModelProperty(value = "排序") private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; @ApiModelProperty(value = "是否显示") private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-02/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-02/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml ================================================ FILE: mall-tiny-02/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-02/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-02/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-02/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-03/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-03/pom.xml ================================================ 4.0.0 mall-tiny-03 1.0-SNAPSHOT mall-tiny-03 Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan("com.macro.mall.tiny.mbg.mapper") public class MyBatisConfig { } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/config/RedisConfig.java ================================================ package com.macro.mall.tiny.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /** * @auther macrozheng * @description Redis配置类 * @date 2020/3/2 * @github https://github.com/macrozheng */ @EnableCaching @Configuration public class RedisConfig { /** * redis数据库自定义key */ public static final String REDIS_KEY_DATABASE="mall"; @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisSerializer serializer = redisSerializer(); RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(serializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(serializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public RedisSerializer redisSerializer() { //创建JSON序列化器 Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //必须设置,否则无法将JSON转化为对象,会转化成Map类型 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(objectMapper); return serializer; } @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); //设置Redis缓存有效期为1天 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1)); return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger相关配置 * @date 2022/11/23 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("SwaggerUI演示") .description("mall-tiny") .contact(new Contact("macro", null, null)) .version("1.0") .build(); } @Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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)); } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/controller/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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 会员登录注册管理Controller * @date 2018/8/3 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsMemberController") @Tag(name = "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); } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { int countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand record); int insertSelective(PmsBrand record); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExample(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand record); int updateByPrimaryKeyWithBLOBs(PmsBrand record); int updateByPrimaryKey(PmsBrand record); } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { private Long id; private String name; @ApiModelProperty(value = "首字母") private String firstLetter; private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/service/RedisService.java ================================================ package com.macro.mall.tiny.service; import java.util.List; import java.util.Map; import java.util.Set; /** * @auther macrozheng * @description redis操作Service * @date 2020/3/3 * @github https://github.com/macrozheng */ public interface RedisService { /** * 保存属性 */ void set(String key, Object value, long time); /** * 保存属性 */ void set(String key, Object value); /** * 获取属性 */ Object get(String key); /** * 删除属性 */ Boolean del(String key); /** * 批量删除属性 */ Long del(List keys); /** * 设置过期时间 */ Boolean expire(String key, long time); /** * 获取过期时间 */ Long getExpire(String key); /** * 判断是否有该属性 */ Boolean hasKey(String key); /** * 按delta递增 */ Long incr(String key, long delta); /** * 按delta递减 */ Long decr(String key, long delta); /** * 获取Hash结构中的属性 */ Object hGet(String key, String hashKey); /** * 向Hash结构中放入一个属性 */ Boolean hSet(String key, String hashKey, Object value, long time); /** * 向Hash结构中放入一个属性 */ void hSet(String key, String hashKey, Object value); /** * 直接获取整个Hash结构 */ Map hGetAll(String key); /** * 直接设置整个Hash结构 */ Boolean hSetAll(String key, Map map, long time); /** * 直接设置整个Hash结构 */ void hSetAll(String key, Map map); /** * 删除Hash结构中的属性 */ void hDel(String key, Object... hashKey); /** * 判断Hash结构中是否有该属性 */ Boolean hHasKey(String key, String hashKey); /** * Hash结构中属性递增 */ Long hIncr(String key, String hashKey, Long delta); /** * Hash结构中属性递减 */ Long hDecr(String key, String hashKey, Long delta); /** * 获取Set结构 */ Set sMembers(String key); /** * 向Set结构中添加属性 */ Long sAdd(String key, Object... values); /** * 向Set结构中添加属性 */ Long sAdd(String key, long time, Object... values); /** * 是否为Set中的属性 */ Boolean sIsMember(String key, Object value); /** * 获取Set结构的长度 */ Long sSize(String key); /** * 删除Set结构中的属性 */ Long sRemove(String key, Object... values); /** * 获取List结构中的属性 */ List lRange(String key, long start, long end); /** * 获取List结构的长度 */ Long lSize(String key); /** * 根据索引获取List中的属性 */ Object lIndex(String key, long index); /** * 向List结构中添加属性 */ Long lPush(String key, Object value); /** * 向List结构中添加属性 */ Long lPush(String key, Object value, long time); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Object... values); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Long time, Object... values); /** * 从List结构中移除属性 */ Long lRemove(String key, long count, Object value); } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/service/UmsMemberService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.common.api.CommonResult; /** * @auther macrozheng * @description 会员管理Service * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface UmsMemberService { /** * 生成验证码 */ CommonResult generateAuthCode(String telephone); /** * 判断验证码和手机号码是否匹配 */ CommonResult verifyAuthCode(String telephone, String authCode); } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/service/impl/RedisServiceImpl.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.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @auther macrozheng * @description redis操作实现类 * @date 2020/3/3 * @github https://github.com/macrozheng */ @Service public class RedisServiceImpl implements RedisService { @Autowired private RedisTemplate redisTemplate; @Override public void set(String key, Object value, long time) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } @Override public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); } @Override public Object get(String key) { return redisTemplate.opsForValue().get(key); } @Override public Boolean del(String key) { return redisTemplate.delete(key); } @Override public Long del(List keys) { return redisTemplate.delete(keys); } @Override public Boolean expire(String key, long time) { return redisTemplate.expire(key, time, TimeUnit.SECONDS); } @Override public Long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } @Override public Boolean hasKey(String key) { return redisTemplate.hasKey(key); } @Override public Long incr(String key, long delta) { return redisTemplate.opsForValue().increment(key, delta); } @Override public Long decr(String key, long delta) { return redisTemplate.opsForValue().increment(key, -delta); } @Override public Object hGet(String key, String hashKey) { return redisTemplate.opsForHash().get(key, hashKey); } @Override public Boolean hSet(String key, String hashKey, Object value, long time) { redisTemplate.opsForHash().put(key, hashKey, value); return expire(key, time); } @Override public void hSet(String key, String hashKey, Object value) { redisTemplate.opsForHash().put(key, hashKey, value); } @Override public Map hGetAll(String key) { return redisTemplate.opsForHash().entries(key); } @Override public Boolean hSetAll(String key, Map map, long time) { redisTemplate.opsForHash().putAll(key, map); return expire(key, time); } @Override public void hSetAll(String key, Map map) { redisTemplate.opsForHash().putAll(key, map); } @Override public void hDel(String key, Object... hashKey) { redisTemplate.opsForHash().delete(key, hashKey); } @Override public Boolean hHasKey(String key, String hashKey) { return redisTemplate.opsForHash().hasKey(key, hashKey); } @Override public Long hIncr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, delta); } @Override public Long hDecr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, -delta); } @Override public Set sMembers(String key) { return redisTemplate.opsForSet().members(key); } @Override public Long sAdd(String key, Object... values) { return redisTemplate.opsForSet().add(key, values); } @Override public Long sAdd(String key, long time, Object... values) { Long count = redisTemplate.opsForSet().add(key, values); expire(key, time); return count; } @Override public Boolean sIsMember(String key, Object value) { return redisTemplate.opsForSet().isMember(key, value); } @Override public Long sSize(String key) { return redisTemplate.opsForSet().size(key); } @Override public Long sRemove(String key, Object... values) { return redisTemplate.opsForSet().remove(key, values); } @Override public List lRange(String key, long start, long end) { return redisTemplate.opsForList().range(key, start, end); } @Override public Long lSize(String key) { return redisTemplate.opsForList().size(key); } @Override public Object lIndex(String key, long index) { return redisTemplate.opsForList().index(key, index); } @Override public Long lPush(String key, Object value) { return redisTemplate.opsForList().rightPush(key, value); } @Override public Long lPush(String key, Object value, long time) { Long index = redisTemplate.opsForList().rightPush(key, value); expire(key, time); return index; } @Override public Long lPushAll(String key, Object... values) { return redisTemplate.opsForList().rightPushAll(key, values); } @Override public Long lPushAll(String key, Long time, Object... values) { Long count = redisTemplate.opsForList().rightPushAll(key, values); expire(key, time); return count; } @Override public Long lRemove(String key, long count, Object value) { return redisTemplate.opsForList().remove(key, count, value); } } ================================================ FILE: mall-tiny-03/src/main/java/com/macro/mall/tiny/service/impl/UmsMemberServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.util.StrUtil; 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; /** * @auther macrozheng * @description 会员管理Service实现类 * @date 2018/8/3 * @github https://github.com/macrozheng */ @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 (StrUtil.isEmpty(authCode)) { return CommonResult.failed("请输入验证码"); } String realAuthCode = (String) redisService.get(REDIS_KEY_PREFIX_AUTH_CODE + telephone); boolean result = authCode.equals(realAuthCode); if (result) { return CommonResult.success(null, "验证码校验成功"); } else { return CommonResult.failed("验证码不正确"); } } } ================================================ FILE: mall-tiny-03/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER redis: host: localhost # Redis服务器地址 database: 0 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 password: # Redis服务器连接密码(默认为空) lettuce: pool: max-active: 8 # 连接池最大连接数 max-idle: 8 # 连接池最大空闲连接数 min-idle: 0 # 连接池最小空闲连接数 max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制 mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml # 自定义redis key redis: key: prefix: authCode: "portal:authCode:" expire: authCode: 120 # 验证码超期时间 ================================================ FILE: mall-tiny-03/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-03/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-03/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-03/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-04/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-04/pom.xml ================================================ 4.0.0 mall-tiny-04 1.0-SNAPSHOT mall-tiny-04 Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-security io.jsonwebtoken jjwt ${jjwt.version} org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/common/utils/JwtTokenUtil.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; /** * @auther macrozheng * @description JwtToken生成的工具类 * JWT token的格式:header.payload.signature * header的格式(算法、token的类型): * {"alg": "HS512","typ": "JWT"} * payload的格式(用户名、创建时间、生成时间): * {"sub":"wang","created":1489079981393,"exp":1489684781} * signature的生成算法: * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) * @date 2018/4/26 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/component/JwtAuthenticationTokenFilter.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; /** * @auther macrozheng * @description JWT登录授权过滤器 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当未登录或者token失效访问接口时,自定义的返回结果 * @date 2018/5/14 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当访问接口没有权限时,自定义的返回结果 * @date 2018/4/26 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/config/IgnoreUrlsConfig.java ================================================ package com.macro.mall.tiny.config; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; /** * @auther macrozheng * @description 用于配置白名单资源路径 * @date 2018/11/5 * @github https://github.com/macrozheng */ @Getter @Setter @Configuration @ConfigurationProperties(prefix = "secure.ignored") public class IgnoreUrlsConfig { private List urls = new ArrayList<>(); } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/config/MallSecurityConfig.java ================================================ package com.macro.mall.tiny.config; import com.macro.mall.tiny.domain.AdminUserDetails; 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.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; /** * @auther macrozheng * @description 自定义配置,用于配置如何获取用户信息 * @date 2022/5/20 * @github https://github.com/macrozheng */ @Configuration public class MallSecurityConfig { @Autowired private UmsAdminService adminService; @Bean public UserDetailsService userDetailsService() { //获取登录用户信息 return username -> { AdminUserDetails admin = adminService.getAdminByUsername(username); if (admin != null) { return admin; } throw new UsernameNotFoundException("用户名或密码错误"); }; } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan("com.macro.mall.tiny.mbg.mapper") public class MyBatisConfig { } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/config/RedisConfig.java ================================================ package com.macro.mall.tiny.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /** * @auther macrozheng * @description Redis配置类 * @date 2020/3/2 * @github https://github.com/macrozheng */ @EnableCaching @Configuration public class RedisConfig { /** * redis数据库自定义key */ public static final String REDIS_KEY_DATABASE="mall"; @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisSerializer serializer = redisSerializer(); RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(serializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(serializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public RedisSerializer redisSerializer() { //创建JSON序列化器 Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //必须设置,否则无法将JSON转化为对象,会转化成Map类型 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(objectMapper); return serializer; } @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); //设置Redis缓存有效期为1天 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1)); return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/config/SecurityConfig.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.domain.AdminUserDetails; 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.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 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.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * @auther macrozheng * @description SpringSecurity的配置 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) public class SecurityConfig { @Autowired private RestfulAccessDeniedHandler restfulAccessDeniedHandler; @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Bean SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity .authorizeRequests(); //不需要保护的资源路径允许访问 for (String url : ignoreUrlsConfig.getUrls()) { registry.antMatchers(url).permitAll(); } //允许跨域请求的OPTIONS请求 registry.antMatchers(HttpMethod.OPTIONS) .permitAll(); httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf .disable() .sessionManagement()// 基于token,所以不需要session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .anyRequest()// 除上面外的所有请求全部需要鉴权认证 .authenticated(); // 禁用缓存 httpSecurity.headers().cacheControl(); // 添加JWT filter httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); //添加自定义未授权和未登录结果返回 httpSecurity.exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler) .authenticationEntryPoint(restAuthenticationEntryPoint); return httpSecurity.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){ return new JwtAuthenticationTokenFilter(); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger文档的配置(带认证) * @date 2022/11/22 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .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(new Contact("macro", null, null)) .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; } @Bean public BeanPostProcessor generateBeanPostProcessor(){ return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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 @PreAuthorize("hasAuthority('brand:listAll')") public CommonResult> getBrandList() { return CommonResult.success(brandService.listAllBrand()); } @ApiOperation("添加品牌") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody @PreAuthorize("hasAuthority('brand:create')") 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 @PreAuthorize("hasAuthority('brand:update')") 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 @PreAuthorize("hasAuthority('brand:delete')") 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 @PreAuthorize("hasAuthority('brand:list')") 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)); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/controller/UmsAdminController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.domain.UmsResource; import com.macro.mall.tiny.service.UmsAdminService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; 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; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @auther macrozheng * @description 后台用户管理 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsAdminController") @Tag(name = "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 = "登录以后返回token") @RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public CommonResult login(@RequestParam String username, @RequestParam String password) { String token = adminService.login(username, password); if (token == null) { return CommonResult.validateFailed("用户名或密码错误"); } Map tokenMap = new HashMap<>(); tokenMap.put("token", token); tokenMap.put("tokenHead", tokenHead); return CommonResult.success(tokenMap); } @ApiOperation(value = "登录以后返回token") @RequestMapping(value = "/resourceList", method = RequestMethod.POST) @ResponseBody public CommonResult> resourceList() { List resourceList = adminService.getResourceList(); return CommonResult.success(resourceList); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/controller/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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 会员登录注册管理Controller * @date 2018/8/3 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsMemberController") @Tag(name = "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); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/domain/AdminUserDetails.java ================================================ package com.macro.mall.tiny.domain; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; 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; /** * @auther macrozheng * @description SpringSecurity用户信息封装类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode(callSuper = false) @Builder public class AdminUserDetails implements UserDetails { private String username; private String password; private List authorityList; @Override public Collection getAuthorities() { return this.authorityList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/domain/UmsResource.java ================================================ package com.macro.mall.tiny.domain; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.Date; /** *

* 后台资源表 *

* * @author macro * @since 2020-08-21 */ @Data @EqualsAndHashCode(callSuper = false) @ApiModel(value="UmsResource对象", description="后台资源表") @Builder public class UmsResource{ private Long id; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "资源名称") private String name; @ApiModelProperty(value = "资源URL") private String url; @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "资源分类ID") private Long categoryId; } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { int countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand record); int insertSelective(PmsBrand record); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExample(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand record); int updateByPrimaryKeyWithBLOBs(PmsBrand record); int updateByPrimaryKey(PmsBrand record); } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { private Long id; private String name; @ApiModelProperty(value = "首字母") private String firstLetter; private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/service/RedisService.java ================================================ package com.macro.mall.tiny.service; import java.util.List; import java.util.Map; import java.util.Set; /** * @auther macrozheng * @description redis操作Service * @date 2020/3/3 * @github https://github.com/macrozheng */ public interface RedisService { /** * 保存属性 */ void set(String key, Object value, long time); /** * 保存属性 */ void set(String key, Object value); /** * 获取属性 */ Object get(String key); /** * 删除属性 */ Boolean del(String key); /** * 批量删除属性 */ Long del(List keys); /** * 设置过期时间 */ Boolean expire(String key, long time); /** * 获取过期时间 */ Long getExpire(String key); /** * 判断是否有该属性 */ Boolean hasKey(String key); /** * 按delta递增 */ Long incr(String key, long delta); /** * 按delta递减 */ Long decr(String key, long delta); /** * 获取Hash结构中的属性 */ Object hGet(String key, String hashKey); /** * 向Hash结构中放入一个属性 */ Boolean hSet(String key, String hashKey, Object value, long time); /** * 向Hash结构中放入一个属性 */ void hSet(String key, String hashKey, Object value); /** * 直接获取整个Hash结构 */ Map hGetAll(String key); /** * 直接设置整个Hash结构 */ Boolean hSetAll(String key, Map map, long time); /** * 直接设置整个Hash结构 */ void hSetAll(String key, Map map); /** * 删除Hash结构中的属性 */ void hDel(String key, Object... hashKey); /** * 判断Hash结构中是否有该属性 */ Boolean hHasKey(String key, String hashKey); /** * Hash结构中属性递增 */ Long hIncr(String key, String hashKey, Long delta); /** * Hash结构中属性递减 */ Long hDecr(String key, String hashKey, Long delta); /** * 获取Set结构 */ Set sMembers(String key); /** * 向Set结构中添加属性 */ Long sAdd(String key, Object... values); /** * 向Set结构中添加属性 */ Long sAdd(String key, long time, Object... values); /** * 是否为Set中的属性 */ Boolean sIsMember(String key, Object value); /** * 获取Set结构的长度 */ Long sSize(String key); /** * 删除Set结构中的属性 */ Long sRemove(String key, Object... values); /** * 获取List结构中的属性 */ List lRange(String key, long start, long end); /** * 获取List结构的长度 */ Long lSize(String key); /** * 根据索引获取List中的属性 */ Object lIndex(String key, long index); /** * 向List结构中添加属性 */ Long lPush(String key, Object value); /** * 向List结构中添加属性 */ Long lPush(String key, Object value, long time); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Object... values); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Long time, Object... values); /** * 从List结构中移除属性 */ Long lRemove(String key, long count, Object value); } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/service/UmsAdminService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.domain.UmsResource; import java.util.List; /** * @auther macrozheng * @description 后台用户管理Service * @date 2020/10/15 * @github https://github.com/macrozheng */ public interface UmsAdminService { /** * 根据用户名获取用户信息 */ AdminUserDetails getAdminByUsername(String username); /** * 获取所以权限列表 */ List getResourceList(); /** * 用户名密码登录 */ String login(String username, String password); } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/service/UmsMemberService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.common.api.CommonResult; /** * @auther macrozheng * @description 会员管理Service * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface UmsMemberService { /** * 生成验证码 */ CommonResult generateAuthCode(String telephone); /** * 判断验证码和手机号码是否匹配 */ CommonResult verifyAuthCode(String telephone, String authCode); } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/service/impl/RedisServiceImpl.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.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @auther macrozheng * @description redis操作实现类 * @date 2020/3/3 * @github https://github.com/macrozheng */ @Service public class RedisServiceImpl implements RedisService { @Autowired private RedisTemplate redisTemplate; @Override public void set(String key, Object value, long time) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } @Override public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); } @Override public Object get(String key) { return redisTemplate.opsForValue().get(key); } @Override public Boolean del(String key) { return redisTemplate.delete(key); } @Override public Long del(List keys) { return redisTemplate.delete(keys); } @Override public Boolean expire(String key, long time) { return redisTemplate.expire(key, time, TimeUnit.SECONDS); } @Override public Long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } @Override public Boolean hasKey(String key) { return redisTemplate.hasKey(key); } @Override public Long incr(String key, long delta) { return redisTemplate.opsForValue().increment(key, delta); } @Override public Long decr(String key, long delta) { return redisTemplate.opsForValue().increment(key, -delta); } @Override public Object hGet(String key, String hashKey) { return redisTemplate.opsForHash().get(key, hashKey); } @Override public Boolean hSet(String key, String hashKey, Object value, long time) { redisTemplate.opsForHash().put(key, hashKey, value); return expire(key, time); } @Override public void hSet(String key, String hashKey, Object value) { redisTemplate.opsForHash().put(key, hashKey, value); } @Override public Map hGetAll(String key) { return redisTemplate.opsForHash().entries(key); } @Override public Boolean hSetAll(String key, Map map, long time) { redisTemplate.opsForHash().putAll(key, map); return expire(key, time); } @Override public void hSetAll(String key, Map map) { redisTemplate.opsForHash().putAll(key, map); } @Override public void hDel(String key, Object... hashKey) { redisTemplate.opsForHash().delete(key, hashKey); } @Override public Boolean hHasKey(String key, String hashKey) { return redisTemplate.opsForHash().hasKey(key, hashKey); } @Override public Long hIncr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, delta); } @Override public Long hDecr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, -delta); } @Override public Set sMembers(String key) { return redisTemplate.opsForSet().members(key); } @Override public Long sAdd(String key, Object... values) { return redisTemplate.opsForSet().add(key, values); } @Override public Long sAdd(String key, long time, Object... values) { Long count = redisTemplate.opsForSet().add(key, values); expire(key, time); return count; } @Override public Boolean sIsMember(String key, Object value) { return redisTemplate.opsForSet().isMember(key, value); } @Override public Long sSize(String key) { return redisTemplate.opsForSet().size(key); } @Override public Long sRemove(String key, Object... values) { return redisTemplate.opsForSet().remove(key, values); } @Override public List lRange(String key, long start, long end) { return redisTemplate.opsForList().range(key, start, end); } @Override public Long lSize(String key) { return redisTemplate.opsForList().size(key); } @Override public Object lIndex(String key, long index) { return redisTemplate.opsForList().index(key, index); } @Override public Long lPush(String key, Object value) { return redisTemplate.opsForList().rightPush(key, value); } @Override public Long lPush(String key, Object value, long time) { Long index = redisTemplate.opsForList().rightPush(key, value); expire(key, time); return index; } @Override public Long lPushAll(String key, Object... values) { return redisTemplate.opsForList().rightPushAll(key, values); } @Override public Long lPushAll(String key, Long time, Object... values) { Long count = redisTemplate.opsForList().rightPushAll(key, values); expire(key, time); return count; } @Override public Long lRemove(String key, long count, Object value) { return redisTemplate.opsForList().remove(key, count, value); } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/service/impl/UmsAdminServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.collection.CollUtil; import com.macro.mall.tiny.common.utils.JwtTokenUtil; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.domain.UmsResource; import com.macro.mall.tiny.service.UmsAdminService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; 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.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description 后台用户管理Service实现类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Slf4j @Service public class UmsAdminServiceImpl implements UmsAdminService { /** * 存放默认用户信息 */ private List adminUserDetailsList = new ArrayList<>(); /** * 存放默认资源信息 */ private List resourceList = new ArrayList<>(); @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private PasswordEncoder passwordEncoder; @PostConstruct private void init(){ adminUserDetailsList.add(AdminUserDetails.builder() .username("admin") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("brand:create","brand:update","brand:delete","brand:list","brand:listAll")) .build()); adminUserDetailsList.add(AdminUserDetails.builder() .username("macro") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("brand:listAll")) .build()); resourceList.add(UmsResource.builder() .id(1L) .name("brand:create") .url("/brand/create") .build()); resourceList.add(UmsResource.builder() .id(2L) .name("brand:update") .url("/brand/update/**") .build()); resourceList.add(UmsResource.builder() .id(3L) .name("brand:delete") .url("/brand/delete/**") .build()); resourceList.add(UmsResource.builder() .id(4L) .name("brand:list") .url("/brand/list") .build()); resourceList.add(UmsResource.builder() .id(5L) .name("brand:listAll") .url("/brand/listAll") .build()); } @Override public AdminUserDetails getAdminByUsername(String username) { List findList = adminUserDetailsList.stream().filter(item -> item.getUsername().equals(username)).collect(Collectors.toList()); if(CollUtil.isNotEmpty(findList)){ return findList.get(0); } return null; } @Override public List getResourceList() { return resourceList; } @Override public String login(String username, String password) { String token = null; try { UserDetails userDetails = getAdminByUsername(username); if(userDetails==null){ return token; } 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) { log.warn("登录异常:{}", e.getMessage()); } return token; } } ================================================ FILE: mall-tiny-04/src/main/java/com/macro/mall/tiny/service/impl/UmsMemberServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.util.StrUtil; 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; /** * @auther macrozheng * @description 会员管理Service实现类 * @date 2018/8/3 * @github https://github.com/macrozheng */ @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 (StrUtil.isEmpty(authCode)) { return CommonResult.failed("请输入验证码"); } String realAuthCode = (String) redisService.get(REDIS_KEY_PREFIX_AUTH_CODE + telephone); boolean result = authCode.equals(realAuthCode); if (result) { return CommonResult.success(null, "验证码校验成功"); } else { return CommonResult.failed("验证码不正确"); } } } ================================================ FILE: mall-tiny-04/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER redis: host: localhost # Redis服务器地址 database: 0 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 password: # Redis服务器连接密码(默认为空) lettuce: pool: max-active: 8 # 连接池最大连接数 max-idle: 8 # 连接池最大空闲连接数 min-idle: 0 # 连接池最小空闲连接数 max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制 mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml # 自定义redis key redis: key: prefix: authCode: "portal:authCode:" expire: authCode: 120 # 验证码超期时间 # 自定义jwt key jwt: tokenHeader: Authorization #JWT存储的请求头 secret: mySecret #JWT加解密使用的密钥 expiration: 604800 #JWT的超期限时间(60*60*24) tokenHead: Bearer #JWT负载中拿到开头 secure: ignored: urls: #安全路径白名单 - /swagger-ui/ - /swagger-resources/** - /**/v2/api-docs - /**/*.html - /**/*.js - /**/*.css - /**/*.png - /favicon.ico - /actuator/** - /druid/** - /admin/** ================================================ FILE: mall-tiny-04/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-04/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-04/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-04/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-05/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-05/pom.xml ================================================ 4.0.0 mall-tiny-05 1.0-SNAPSHOT mall-tiny-05 Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-security io.jsonwebtoken jjwt ${jjwt.version} org.springframework.boot spring-boot-starter-data-elasticsearch org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import org.springframework.data.domain.Page; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } /** * 将SpringData分页后的list转为分页信息 */ public static CommonPage restPage(Page pageInfo) { CommonPage result = new CommonPage(); result.setTotalPage(pageInfo.getTotalPages()); result.setPageNum(pageInfo.getNumber()); result.setPageSize(pageInfo.getSize()); result.setTotal(pageInfo.getTotalElements()); result.setList(pageInfo.getContent()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/common/utils/JwtTokenUtil.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; /** * @auther macrozheng * @description JwtToken生成的工具类 * JWT token的格式:header.payload.signature * header的格式(算法、token的类型): * {"alg": "HS512","typ": "JWT"} * payload的格式(用户名、创建时间、生成时间): * {"sub":"wang","created":1489079981393,"exp":1489684781} * signature的生成算法: * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) * @date 2018/4/26 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/component/JwtAuthenticationTokenFilter.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; /** * @auther macrozheng * @description JWT登录授权过滤器 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当未登录或者token失效访问接口时,自定义的返回结果 * @date 2018/5/14 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当访问接口没有权限时,自定义的返回结果 * @date 2018/4/26 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/config/IgnoreUrlsConfig.java ================================================ package com.macro.mall.tiny.config; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; /** * @auther macrozheng * @description 用于配置白名单资源路径 * @date 2018/11/5 * @github https://github.com/macrozheng */ @Getter @Setter @Configuration @ConfigurationProperties(prefix = "secure.ignored") public class IgnoreUrlsConfig { private List urls = new ArrayList<>(); } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/config/MallSecurityConfig.java ================================================ package com.macro.mall.tiny.config; import com.macro.mall.tiny.domain.AdminUserDetails; 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.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; /** * @auther macrozheng * @description 自定义配置,用于配置如何获取用户信息 * @date 2022/5/20 * @github https://github.com/macrozheng */ @Configuration public class MallSecurityConfig { @Autowired private UmsAdminService adminService; @Bean public UserDetailsService userDetailsService() { //获取登录用户信息 return username -> { AdminUserDetails admin = adminService.getAdminByUsername(username); if (admin != null) { return admin; } throw new UsernameNotFoundException("用户名或密码错误"); }; } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan({"com.macro.mall.tiny.mbg.mapper","com.macro.mall.tiny.dao"}) public class MyBatisConfig { } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/config/RedisConfig.java ================================================ package com.macro.mall.tiny.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /** * @auther macrozheng * @description Redis配置类 * @date 2020/3/2 * @github https://github.com/macrozheng */ @EnableCaching @Configuration public class RedisConfig { /** * redis数据库自定义key */ public static final String REDIS_KEY_DATABASE="mall"; @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisSerializer serializer = redisSerializer(); RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(serializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(serializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public RedisSerializer redisSerializer() { //创建JSON序列化器 Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //必须设置,否则无法将JSON转化为对象,会转化成Map类型 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(objectMapper); return serializer; } @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); //设置Redis缓存有效期为1天 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1)); return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/config/SecurityConfig.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.domain.AdminUserDetails; 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.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 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.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * @auther macrozheng * @description SpringSecurity的配置 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) public class SecurityConfig { @Autowired private RestfulAccessDeniedHandler restfulAccessDeniedHandler; @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Bean SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity .authorizeRequests(); //不需要保护的资源路径允许访问 for (String url : ignoreUrlsConfig.getUrls()) { registry.antMatchers(url).permitAll(); } //允许跨域请求的OPTIONS请求 registry.antMatchers(HttpMethod.OPTIONS) .permitAll(); httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf .disable() .sessionManagement()// 基于token,所以不需要session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .anyRequest()// 除上面外的所有请求全部需要鉴权认证 .authenticated(); // 禁用缓存 httpSecurity.headers().cacheControl(); // 添加JWT filter httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); //添加自定义未授权和未登录结果返回 httpSecurity.exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler) .authenticationEntryPoint(restAuthenticationEntryPoint); return httpSecurity.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){ return new JwtAuthenticationTokenFilter(); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger文档的配置(带认证) * @date 2022/11/22 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .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(new Contact("macro", null, null)) .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; } @Bean public BeanPostProcessor generateBeanPostProcessor(){ return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/controller/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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 搜索商品管理Controller * @date 2018/6/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "EsProductController") @Tag(name = "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 delete(@PathVariable Long id) { esProductService.delete(id); return CommonResult.success(null); } @ApiOperation(value = "根据id批量删除商品") @RequestMapping(value = "/delete/batch", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("ids") List ids) { esProductService.delete(ids); return CommonResult.success(null); } @ApiOperation(value = "根据id创建商品") @RequestMapping(value = "/create/{id}", method = RequestMethod.POST) @ResponseBody public CommonResult create(@PathVariable Long id) { EsProduct esProduct = esProductService.create(id); if (esProduct != null) { return CommonResult.success(esProduct); } else { return CommonResult.failed(); } } @ApiOperation(value = "简单搜索") @RequestMapping(value = "/search/simple", method = RequestMethod.GET) @ResponseBody public CommonResult> search(@RequestParam(required = false) String keyword, @RequestParam(required = false, defaultValue = "0") Integer pageNum, @RequestParam(required = false, defaultValue = "5") Integer pageSize) { Page esProductPage = esProductService.search(keyword, pageNum, pageSize); return CommonResult.success(CommonPage.restPage(esProductPage)); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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 @PreAuthorize("hasAuthority('brand:listAll')") public CommonResult> getBrandList() { return CommonResult.success(brandService.listAllBrand()); } @ApiOperation("添加品牌") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody @PreAuthorize("hasAuthority('brand:create')") 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 @PreAuthorize("hasAuthority('brand:update')") 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 @PreAuthorize("hasAuthority('brand:delete')") 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 @PreAuthorize("hasAuthority('brand:list')") 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)); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/controller/UmsAdminController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.domain.UmsResource; import com.macro.mall.tiny.service.UmsAdminService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; 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; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @auther macrozheng * @description 后台用户管理 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsAdminController") @Tag(name = "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 = "登录以后返回token") @RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public CommonResult login(@RequestParam String username, @RequestParam String password) { String token = adminService.login(username, password); if (token == null) { return CommonResult.validateFailed("用户名或密码错误"); } Map tokenMap = new HashMap<>(); tokenMap.put("token", token); tokenMap.put("tokenHead", tokenHead); return CommonResult.success(tokenMap); } @ApiOperation(value = "登录以后返回token") @RequestMapping(value = "/resourceList", method = RequestMethod.POST) @ResponseBody public CommonResult> resourceList() { List resourceList = adminService.getResourceList(); return CommonResult.success(resourceList); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/controller/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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 会员登录注册管理Controller * @date 2018/8/3 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsMemberController") @Tag(name = "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); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/dao/EsProductDao.java ================================================ package com.macro.mall.tiny.dao; import com.macro.mall.tiny.nosql.elasticsearch.document.EsProduct; import org.apache.ibatis.annotations.Param; import java.util.List; /** * @auther macrozheng * @description 搜索系统中的商品管理自定义Dao * @date 2018/6/19 * @github https://github.com/macrozheng */ public interface EsProductDao { List getAllEsProductList(@Param("id") Long id); } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/domain/AdminUserDetails.java ================================================ package com.macro.mall.tiny.domain; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; 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; /** * @auther macrozheng * @description SpringSecurity用户信息封装类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode(callSuper = false) @Builder public class AdminUserDetails implements UserDetails { private String username; private String password; private List authorityList; @Override public Collection getAuthorities() { return this.authorityList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/domain/UmsResource.java ================================================ package com.macro.mall.tiny.domain; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.Date; /** *

* 后台资源表 *

* * @author macro * @since 2020-08-21 */ @Data @EqualsAndHashCode(callSuper = false) @ApiModel(value="UmsResource对象", description="后台资源表") @Builder public class UmsResource{ private Long id; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "资源名称") private String name; @ApiModelProperty(value = "资源URL") private String url; @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "资源分类ID") private Long categoryId; } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { int countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand record); int insertSelective(PmsBrand record); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExample(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand record); int updateByPrimaryKeyWithBLOBs(PmsBrand record); int updateByPrimaryKey(PmsBrand record); } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { private Long id; private String name; @ApiModelProperty(value = "首字母") private String firstLetter; private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/document/EsProduct.java ================================================ package com.macro.mall.tiny.nosql.elasticsearch.document; import lombok.Data; import lombok.EqualsAndHashCode; 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 org.springframework.data.elasticsearch.annotations.Setting; import java.io.Serializable; import java.math.BigDecimal; import java.util.List; /** * @auther macrozheng * @description 搜索商品的信息 * @date 2018/6/19 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode @Document(indexName = "pms") @Setting(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; } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/document/EsProductAttributeValue.java ================================================ package com.macro.mall.tiny.nosql.elasticsearch.document; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; import java.io.Serializable; /** * @auther macrozheng * @description 搜索商品的属性信息 * @date 2018/6/27 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode public class EsProductAttributeValue implements Serializable { private static final long serialVersionUID = 1L; private Long id; private Long productAttributeId; //属性值 @Field(type = FieldType.Keyword) private String value; //属性参数:0->规格;1->参数 private Integer type; //属性名称 @Field(type=FieldType.Keyword) private String name; } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/repository/EsProductRepository.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; /** * @auther macrozheng * @description 商品ES操作类 * @date 2018/6/19 * @github https://github.com/macrozheng */ public interface EsProductRepository extends ElasticsearchRepository { /** * 搜索查询 * * @param name 商品名称 * @param subTitle 商品标题 * @param keywords 商品关键字 * @param page 分页信息 * @return */ Page findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page); } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/service/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; /** * @auther macrozheng * @description 商品搜索管理Service * @date 2018/6/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/service/RedisService.java ================================================ package com.macro.mall.tiny.service; import java.util.List; import java.util.Map; import java.util.Set; /** * @auther macrozheng * @description redis操作Service * @date 2020/3/3 * @github https://github.com/macrozheng */ public interface RedisService { /** * 保存属性 */ void set(String key, Object value, long time); /** * 保存属性 */ void set(String key, Object value); /** * 获取属性 */ Object get(String key); /** * 删除属性 */ Boolean del(String key); /** * 批量删除属性 */ Long del(List keys); /** * 设置过期时间 */ Boolean expire(String key, long time); /** * 获取过期时间 */ Long getExpire(String key); /** * 判断是否有该属性 */ Boolean hasKey(String key); /** * 按delta递增 */ Long incr(String key, long delta); /** * 按delta递减 */ Long decr(String key, long delta); /** * 获取Hash结构中的属性 */ Object hGet(String key, String hashKey); /** * 向Hash结构中放入一个属性 */ Boolean hSet(String key, String hashKey, Object value, long time); /** * 向Hash结构中放入一个属性 */ void hSet(String key, String hashKey, Object value); /** * 直接获取整个Hash结构 */ Map hGetAll(String key); /** * 直接设置整个Hash结构 */ Boolean hSetAll(String key, Map map, long time); /** * 直接设置整个Hash结构 */ void hSetAll(String key, Map map); /** * 删除Hash结构中的属性 */ void hDel(String key, Object... hashKey); /** * 判断Hash结构中是否有该属性 */ Boolean hHasKey(String key, String hashKey); /** * Hash结构中属性递增 */ Long hIncr(String key, String hashKey, Long delta); /** * Hash结构中属性递减 */ Long hDecr(String key, String hashKey, Long delta); /** * 获取Set结构 */ Set sMembers(String key); /** * 向Set结构中添加属性 */ Long sAdd(String key, Object... values); /** * 向Set结构中添加属性 */ Long sAdd(String key, long time, Object... values); /** * 是否为Set中的属性 */ Boolean sIsMember(String key, Object value); /** * 获取Set结构的长度 */ Long sSize(String key); /** * 删除Set结构中的属性 */ Long sRemove(String key, Object... values); /** * 获取List结构中的属性 */ List lRange(String key, long start, long end); /** * 获取List结构的长度 */ Long lSize(String key); /** * 根据索引获取List中的属性 */ Object lIndex(String key, long index); /** * 向List结构中添加属性 */ Long lPush(String key, Object value); /** * 向List结构中添加属性 */ Long lPush(String key, Object value, long time); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Object... values); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Long time, Object... values); /** * 从List结构中移除属性 */ Long lRemove(String key, long count, Object value); } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/service/UmsAdminService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.domain.UmsResource; import java.util.List; /** * @auther macrozheng * @description 后台用户管理Service * @date 2020/10/15 * @github https://github.com/macrozheng */ public interface UmsAdminService { /** * 根据用户名获取用户信息 */ AdminUserDetails getAdminByUsername(String username); /** * 获取所以权限列表 */ List getResourceList(); /** * 用户名密码登录 */ String login(String username, String password); } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/service/UmsMemberService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.common.api.CommonResult; /** * @auther macrozheng * @description 会员管理Service * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface UmsMemberService { /** * 生成验证码 */ CommonResult generateAuthCode(String telephone); /** * 判断验证码和手机号码是否匹配 */ CommonResult verifyAuthCode(String telephone, String authCode); } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/service/impl/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; /** * @auther macrozheng * @description 搜索商品管理Service实现类 * @date 2018/6/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/service/impl/RedisServiceImpl.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.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @auther macrozheng * @description redis操作实现类 * @date 2020/3/3 * @github https://github.com/macrozheng */ @Service public class RedisServiceImpl implements RedisService { @Autowired private RedisTemplate redisTemplate; @Override public void set(String key, Object value, long time) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } @Override public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); } @Override public Object get(String key) { return redisTemplate.opsForValue().get(key); } @Override public Boolean del(String key) { return redisTemplate.delete(key); } @Override public Long del(List keys) { return redisTemplate.delete(keys); } @Override public Boolean expire(String key, long time) { return redisTemplate.expire(key, time, TimeUnit.SECONDS); } @Override public Long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } @Override public Boolean hasKey(String key) { return redisTemplate.hasKey(key); } @Override public Long incr(String key, long delta) { return redisTemplate.opsForValue().increment(key, delta); } @Override public Long decr(String key, long delta) { return redisTemplate.opsForValue().increment(key, -delta); } @Override public Object hGet(String key, String hashKey) { return redisTemplate.opsForHash().get(key, hashKey); } @Override public Boolean hSet(String key, String hashKey, Object value, long time) { redisTemplate.opsForHash().put(key, hashKey, value); return expire(key, time); } @Override public void hSet(String key, String hashKey, Object value) { redisTemplate.opsForHash().put(key, hashKey, value); } @Override public Map hGetAll(String key) { return redisTemplate.opsForHash().entries(key); } @Override public Boolean hSetAll(String key, Map map, long time) { redisTemplate.opsForHash().putAll(key, map); return expire(key, time); } @Override public void hSetAll(String key, Map map) { redisTemplate.opsForHash().putAll(key, map); } @Override public void hDel(String key, Object... hashKey) { redisTemplate.opsForHash().delete(key, hashKey); } @Override public Boolean hHasKey(String key, String hashKey) { return redisTemplate.opsForHash().hasKey(key, hashKey); } @Override public Long hIncr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, delta); } @Override public Long hDecr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, -delta); } @Override public Set sMembers(String key) { return redisTemplate.opsForSet().members(key); } @Override public Long sAdd(String key, Object... values) { return redisTemplate.opsForSet().add(key, values); } @Override public Long sAdd(String key, long time, Object... values) { Long count = redisTemplate.opsForSet().add(key, values); expire(key, time); return count; } @Override public Boolean sIsMember(String key, Object value) { return redisTemplate.opsForSet().isMember(key, value); } @Override public Long sSize(String key) { return redisTemplate.opsForSet().size(key); } @Override public Long sRemove(String key, Object... values) { return redisTemplate.opsForSet().remove(key, values); } @Override public List lRange(String key, long start, long end) { return redisTemplate.opsForList().range(key, start, end); } @Override public Long lSize(String key) { return redisTemplate.opsForList().size(key); } @Override public Object lIndex(String key, long index) { return redisTemplate.opsForList().index(key, index); } @Override public Long lPush(String key, Object value) { return redisTemplate.opsForList().rightPush(key, value); } @Override public Long lPush(String key, Object value, long time) { Long index = redisTemplate.opsForList().rightPush(key, value); expire(key, time); return index; } @Override public Long lPushAll(String key, Object... values) { return redisTemplate.opsForList().rightPushAll(key, values); } @Override public Long lPushAll(String key, Long time, Object... values) { Long count = redisTemplate.opsForList().rightPushAll(key, values); expire(key, time); return count; } @Override public Long lRemove(String key, long count, Object value) { return redisTemplate.opsForList().remove(key, count, value); } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/service/impl/UmsAdminServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.collection.CollUtil; import com.macro.mall.tiny.common.utils.JwtTokenUtil; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.domain.UmsResource; import com.macro.mall.tiny.service.UmsAdminService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; 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.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description 后台用户管理Service实现类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Slf4j @Service public class UmsAdminServiceImpl implements UmsAdminService { /** * 存放默认用户信息 */ private List adminUserDetailsList = new ArrayList<>(); /** * 存放默认资源信息 */ private List resourceList = new ArrayList<>(); @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private PasswordEncoder passwordEncoder; @PostConstruct private void init(){ adminUserDetailsList.add(AdminUserDetails.builder() .username("admin") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("brand:create","brand:update","brand:delete","brand:list","brand:listAll")) .build()); adminUserDetailsList.add(AdminUserDetails.builder() .username("macro") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("brand:listAll")) .build()); resourceList.add(UmsResource.builder() .id(1L) .name("brand:create") .url("/brand/create") .build()); resourceList.add(UmsResource.builder() .id(2L) .name("brand:update") .url("/brand/update/**") .build()); resourceList.add(UmsResource.builder() .id(3L) .name("brand:delete") .url("/brand/delete/**") .build()); resourceList.add(UmsResource.builder() .id(4L) .name("brand:list") .url("/brand/list") .build()); resourceList.add(UmsResource.builder() .id(5L) .name("brand:listAll") .url("/brand/listAll") .build()); } @Override public AdminUserDetails getAdminByUsername(String username) { List findList = adminUserDetailsList.stream().filter(item -> item.getUsername().equals(username)).collect(Collectors.toList()); if(CollUtil.isNotEmpty(findList)){ return findList.get(0); } return null; } @Override public List getResourceList() { return resourceList; } @Override public String login(String username, String password) { String token = null; try { UserDetails userDetails = getAdminByUsername(username); if(userDetails==null){ return token; } 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) { log.warn("登录异常:{}", e.getMessage()); } return token; } } ================================================ FILE: mall-tiny-05/src/main/java/com/macro/mall/tiny/service/impl/UmsMemberServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.util.StrUtil; 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; /** * @auther macrozheng * @description 会员管理Service实现类 * @date 2018/8/3 * @github https://github.com/macrozheng */ @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 (StrUtil.isEmpty(authCode)) { return CommonResult.failed("请输入验证码"); } String realAuthCode = (String) redisService.get(REDIS_KEY_PREFIX_AUTH_CODE + telephone); boolean result = authCode.equals(realAuthCode); if (result) { return CommonResult.success(null, "验证码校验成功"); } else { return CommonResult.failed("验证码不正确"); } } } ================================================ FILE: mall-tiny-05/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER redis: host: localhost # Redis服务器地址 database: 0 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 password: # Redis服务器连接密码(默认为空) lettuce: pool: max-active: 8 # 连接池最大连接数 max-idle: 8 # 连接池最大空闲连接数 min-idle: 0 # 连接池最小空闲连接数 max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制 data: elasticsearch: repositories: enabled: true # 开启ES仓库配置,自动为仓库接口生成实现类 elasticsearch: uris: http://localhost:9200 # ES的连接地址及端口号 mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml # 自定义redis key redis: key: prefix: authCode: "portal:authCode:" expire: authCode: 120 # 验证码超期时间 # 自定义jwt key jwt: tokenHeader: Authorization #JWT存储的请求头 secret: mySecret #JWT加解密使用的密钥 expiration: 604800 #JWT的超期限时间(60*60*24) tokenHead: Bearer #JWT负载中拿到开头 secure: ignored: urls: #安全路径白名单 - /swagger-ui/ - /swagger-resources/** - /**/v2/api-docs - /**/*.html - /**/*.js - /**/*.css - /**/*.png - /favicon.ico - /actuator/** - /druid/** - /admin/** - /esProduct/** ================================================ FILE: mall-tiny-05/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-05/src/main/resources/dao/EsProductDao.xml ================================================ ================================================ FILE: mall-tiny-05/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-05/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-05/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-06/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-06/pom.xml ================================================ 4.0.0 mall-tiny-06 1.0-SNAPSHOT mall-tiny-06 Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-security io.jsonwebtoken jjwt ${jjwt.version} org.springframework.boot spring-boot-starter-data-elasticsearch org.springframework.boot spring-boot-starter-data-mongodb org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import org.springframework.data.domain.Page; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } /** * 将SpringData分页后的list转为分页信息 */ public static CommonPage restPage(Page pageInfo) { CommonPage result = new CommonPage(); result.setTotalPage(pageInfo.getTotalPages()); result.setPageNum(pageInfo.getNumber()); result.setPageSize(pageInfo.getSize()); result.setTotal(pageInfo.getTotalElements()); result.setList(pageInfo.getContent()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/common/utils/JwtTokenUtil.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; /** * @auther macrozheng * @description JwtToken生成的工具类 * JWT token的格式:header.payload.signature * header的格式(算法、token的类型): * {"alg": "HS512","typ": "JWT"} * payload的格式(用户名、创建时间、生成时间): * {"sub":"wang","created":1489079981393,"exp":1489684781} * signature的生成算法: * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) * @date 2018/4/26 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/component/JwtAuthenticationTokenFilter.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; /** * @auther macrozheng * @description JWT登录授权过滤器 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当未登录或者token失效访问接口时,自定义的返回结果 * @date 2018/5/14 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当访问接口没有权限时,自定义的返回结果 * @date 2018/4/26 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/config/IgnoreUrlsConfig.java ================================================ package com.macro.mall.tiny.config; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; /** * @auther macrozheng * @description 用于配置白名单资源路径 * @date 2018/11/5 * @github https://github.com/macrozheng */ @Getter @Setter @Configuration @ConfigurationProperties(prefix = "secure.ignored") public class IgnoreUrlsConfig { private List urls = new ArrayList<>(); } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/config/MallSecurityConfig.java ================================================ package com.macro.mall.tiny.config; import com.macro.mall.tiny.domain.AdminUserDetails; 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.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; /** * @auther macrozheng * @description 自定义配置,用于配置如何获取用户信息 * @date 2022/5/20 * @github https://github.com/macrozheng */ @Configuration public class MallSecurityConfig { @Autowired private UmsAdminService adminService; @Bean public UserDetailsService userDetailsService() { //获取登录用户信息 return username -> { AdminUserDetails admin = adminService.getAdminByUsername(username); if (admin != null) { return admin; } throw new UsernameNotFoundException("用户名或密码错误"); }; } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan({"com.macro.mall.tiny.mbg.mapper","com.macro.mall.tiny.dao"}) public class MyBatisConfig { } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/config/RedisConfig.java ================================================ package com.macro.mall.tiny.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /** * @auther macrozheng * @description Redis配置类 * @date 2020/3/2 * @github https://github.com/macrozheng */ @EnableCaching @Configuration public class RedisConfig { /** * redis数据库自定义key */ public static final String REDIS_KEY_DATABASE="mall"; @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisSerializer serializer = redisSerializer(); RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(serializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(serializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public RedisSerializer redisSerializer() { //创建JSON序列化器 Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //必须设置,否则无法将JSON转化为对象,会转化成Map类型 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(objectMapper); return serializer; } @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); //设置Redis缓存有效期为1天 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1)); return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/config/SecurityConfig.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.domain.AdminUserDetails; 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.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 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.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * @auther macrozheng * @description SpringSecurity的配置 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) public class SecurityConfig { @Autowired private RestfulAccessDeniedHandler restfulAccessDeniedHandler; @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Bean SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity .authorizeRequests(); //不需要保护的资源路径允许访问 for (String url : ignoreUrlsConfig.getUrls()) { registry.antMatchers(url).permitAll(); } //允许跨域请求的OPTIONS请求 registry.antMatchers(HttpMethod.OPTIONS) .permitAll(); httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf .disable() .sessionManagement()// 基于token,所以不需要session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .anyRequest()// 除上面外的所有请求全部需要鉴权认证 .authenticated(); // 禁用缓存 httpSecurity.headers().cacheControl(); // 添加JWT filter httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); //添加自定义未授权和未登录结果返回 httpSecurity.exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler) .authenticationEntryPoint(restAuthenticationEntryPoint); return httpSecurity.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){ return new JwtAuthenticationTokenFilter(); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger文档的配置(带认证) * @date 2022/11/22 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .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(new Contact("macro", null, null)) .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; } @Bean public BeanPostProcessor generateBeanPostProcessor(){ return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/controller/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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 搜索商品管理Controller * @date 2018/6/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "EsProductController") @Tag(name = "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 delete(@PathVariable Long id) { esProductService.delete(id); return CommonResult.success(null); } @ApiOperation(value = "根据id批量删除商品") @RequestMapping(value = "/delete/batch", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("ids") List ids) { esProductService.delete(ids); return CommonResult.success(null); } @ApiOperation(value = "根据id创建商品") @RequestMapping(value = "/create/{id}", method = RequestMethod.POST) @ResponseBody public CommonResult create(@PathVariable Long id) { EsProduct esProduct = esProductService.create(id); if (esProduct != null) { return CommonResult.success(esProduct); } else { return CommonResult.failed(); } } @ApiOperation(value = "简单搜索") @RequestMapping(value = "/search/simple", method = RequestMethod.GET) @ResponseBody public CommonResult> search(@RequestParam(required = false) String keyword, @RequestParam(required = false, defaultValue = "0") Integer pageNum, @RequestParam(required = false, defaultValue = "5") Integer pageSize) { Page esProductPage = esProductService.search(keyword, pageNum, pageSize); return CommonResult.success(CommonPage.restPage(esProductPage)); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/controller/MemberReadHistoryController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import com.macro.mall.tiny.service.MemberReadHistoryService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 会员商品浏览记录管理Controller * @date 2018/8/3 * @github https://github.com/macrozheng */ @Controller @Api(tags = "MemberReadHistoryController") @Tag(name = "MemberReadHistoryController", description = "会员商品浏览记录管理") @RequestMapping("/member/readHistory") public class MemberReadHistoryController { @Autowired private MemberReadHistoryService memberReadHistoryService; @ApiOperation("创建浏览记录") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody public CommonResult create(@RequestBody MemberReadHistory memberReadHistory) { int count = memberReadHistoryService.create(memberReadHistory); if (count > 0) { return CommonResult.success(count); } else { return CommonResult.failed(); } } @ApiOperation("删除浏览记录") @RequestMapping(value = "/delete", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("ids") List ids) { int count = memberReadHistoryService.delete(ids); if (count > 0) { return CommonResult.success(count); } else { return CommonResult.failed(); } } @ApiOperation("展示浏览记录") @RequestMapping(value = "/list", method = RequestMethod.GET) @ResponseBody public CommonResult> list(Long memberId) { List memberReadHistoryList = memberReadHistoryService.list(memberId); return CommonResult.success(memberReadHistoryList); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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 @PreAuthorize("hasAuthority('brand:listAll')") public CommonResult> getBrandList() { return CommonResult.success(brandService.listAllBrand()); } @ApiOperation("添加品牌") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody @PreAuthorize("hasAuthority('brand:create')") 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 @PreAuthorize("hasAuthority('brand:update')") 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 @PreAuthorize("hasAuthority('brand:delete')") 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 @PreAuthorize("hasAuthority('brand:list')") 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)); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/controller/UmsAdminController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.domain.UmsResource; import com.macro.mall.tiny.service.UmsAdminService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; 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; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @auther macrozheng * @description 后台用户管理 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsAdminController") @Tag(name = "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 = "登录以后返回token") @RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public CommonResult login(@RequestParam String username, @RequestParam String password) { String token = adminService.login(username, password); if (token == null) { return CommonResult.validateFailed("用户名或密码错误"); } Map tokenMap = new HashMap<>(); tokenMap.put("token", token); tokenMap.put("tokenHead", tokenHead); return CommonResult.success(tokenMap); } @ApiOperation(value = "登录以后返回token") @RequestMapping(value = "/resourceList", method = RequestMethod.POST) @ResponseBody public CommonResult> resourceList() { List resourceList = adminService.getResourceList(); return CommonResult.success(resourceList); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/controller/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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 会员登录注册管理Controller * @date 2018/8/3 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsMemberController") @Tag(name = "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); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/dao/EsProductDao.java ================================================ package com.macro.mall.tiny.dao; import com.macro.mall.tiny.nosql.elasticsearch.document.EsProduct; import org.apache.ibatis.annotations.Param; import java.util.List; /** * @auther macrozheng * @description 搜索系统中的商品管理自定义Dao * @date 2018/6/19 * @github https://github.com/macrozheng */ public interface EsProductDao { List getAllEsProductList(@Param("id") Long id); } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/domain/AdminUserDetails.java ================================================ package com.macro.mall.tiny.domain; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; 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; /** * @auther macrozheng * @description SpringSecurity用户信息封装类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode(callSuper = false) @Builder public class AdminUserDetails implements UserDetails { private String username; private String password; private List authorityList; @Override public Collection getAuthorities() { return this.authorityList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/domain/UmsResource.java ================================================ package com.macro.mall.tiny.domain; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.Date; /** *

* 后台资源表 *

* * @author macro * @since 2020-08-21 */ @Data @EqualsAndHashCode(callSuper = false) @ApiModel(value="UmsResource对象", description="后台资源表") @Builder public class UmsResource{ private Long id; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "资源名称") private String name; @ApiModelProperty(value = "资源URL") private String url; @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "资源分类ID") private Long categoryId; } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { int countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand record); int insertSelective(PmsBrand record); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExample(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand record); int updateByPrimaryKeyWithBLOBs(PmsBrand record); int updateByPrimaryKey(PmsBrand record); } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { private Long id; private String name; @ApiModelProperty(value = "首字母") private String firstLetter; private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/document/EsProduct.java ================================================ package com.macro.mall.tiny.nosql.elasticsearch.document; import lombok.Data; import lombok.EqualsAndHashCode; 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 org.springframework.data.elasticsearch.annotations.Setting; import java.io.Serializable; import java.math.BigDecimal; import java.util.List; /** * @auther macrozheng * @description 搜索商品的信息 * @date 2018/6/19 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode @Document(indexName = "pms") @Setting(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; } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/document/EsProductAttributeValue.java ================================================ package com.macro.mall.tiny.nosql.elasticsearch.document; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; import java.io.Serializable; /** * @auther macrozheng * @description 搜索商品的属性信息 * @date 2018/6/27 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode public class EsProductAttributeValue implements Serializable { private static final long serialVersionUID = 1L; private Long id; private Long productAttributeId; //属性值 @Field(type = FieldType.Keyword) private String value; //属性参数:0->规格;1->参数 private Integer type; //属性名称 @Field(type=FieldType.Keyword) private String name; } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/repository/EsProductRepository.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; /** * @auther macrozheng * @description 商品ES操作类 * @date 2018/6/19 * @github https://github.com/macrozheng */ public interface EsProductRepository extends ElasticsearchRepository { /** * 搜索查询 * * @param name 商品名称 * @param subTitle 商品标题 * @param keywords 商品关键字 * @param page 分页信息 * @return */ Page findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page); } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/nosql/mongodb/document/MemberReadHistory.java ================================================ package com.macro.mall.tiny.nosql.mongodb.document; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; /** * @auther macrozheng * @description 用户商品浏览历史记录 * @date 2018/8/3 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode @Document public class MemberReadHistory { @Id private String id; @Indexed private Long memberId; private String memberNickname; private String memberIcon; @Indexed private Long productId; private String productName; private String productPic; private String productSubTitle; private String productPrice; private Date createTime; } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/nosql/mongodb/repository/MemberReadHistoryRepository.java ================================================ package com.macro.mall.tiny.nosql.mongodb.repository; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import org.springframework.data.mongodb.repository.MongoRepository; import java.util.List; /** * @auther macrozheng * @description 会员商品浏览历史Repository * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface MemberReadHistoryRepository extends MongoRepository { /** * 根据会员id按时间倒序获取浏览记录 * @param memberId 会员id */ List findByMemberIdOrderByCreateTimeDesc(Long memberId); } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/service/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; /** * @auther macrozheng * @description 商品搜索管理Service * @date 2018/6/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/service/MemberReadHistoryService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import java.util.List; /** * @auther macrozheng * @description 会员浏览记录管理Service * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface MemberReadHistoryService { /** * 生成浏览记录 */ int create(MemberReadHistory memberReadHistory); /** * 批量删除浏览记录 */ int delete(List ids); /** * 获取用户浏览历史记录 */ List list(Long memberId); } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/service/RedisService.java ================================================ package com.macro.mall.tiny.service; import java.util.List; import java.util.Map; import java.util.Set; /** * @auther macrozheng * @description redis操作Service * @date 2020/3/3 * @github https://github.com/macrozheng */ public interface RedisService { /** * 保存属性 */ void set(String key, Object value, long time); /** * 保存属性 */ void set(String key, Object value); /** * 获取属性 */ Object get(String key); /** * 删除属性 */ Boolean del(String key); /** * 批量删除属性 */ Long del(List keys); /** * 设置过期时间 */ Boolean expire(String key, long time); /** * 获取过期时间 */ Long getExpire(String key); /** * 判断是否有该属性 */ Boolean hasKey(String key); /** * 按delta递增 */ Long incr(String key, long delta); /** * 按delta递减 */ Long decr(String key, long delta); /** * 获取Hash结构中的属性 */ Object hGet(String key, String hashKey); /** * 向Hash结构中放入一个属性 */ Boolean hSet(String key, String hashKey, Object value, long time); /** * 向Hash结构中放入一个属性 */ void hSet(String key, String hashKey, Object value); /** * 直接获取整个Hash结构 */ Map hGetAll(String key); /** * 直接设置整个Hash结构 */ Boolean hSetAll(String key, Map map, long time); /** * 直接设置整个Hash结构 */ void hSetAll(String key, Map map); /** * 删除Hash结构中的属性 */ void hDel(String key, Object... hashKey); /** * 判断Hash结构中是否有该属性 */ Boolean hHasKey(String key, String hashKey); /** * Hash结构中属性递增 */ Long hIncr(String key, String hashKey, Long delta); /** * Hash结构中属性递减 */ Long hDecr(String key, String hashKey, Long delta); /** * 获取Set结构 */ Set sMembers(String key); /** * 向Set结构中添加属性 */ Long sAdd(String key, Object... values); /** * 向Set结构中添加属性 */ Long sAdd(String key, long time, Object... values); /** * 是否为Set中的属性 */ Boolean sIsMember(String key, Object value); /** * 获取Set结构的长度 */ Long sSize(String key); /** * 删除Set结构中的属性 */ Long sRemove(String key, Object... values); /** * 获取List结构中的属性 */ List lRange(String key, long start, long end); /** * 获取List结构的长度 */ Long lSize(String key); /** * 根据索引获取List中的属性 */ Object lIndex(String key, long index); /** * 向List结构中添加属性 */ Long lPush(String key, Object value); /** * 向List结构中添加属性 */ Long lPush(String key, Object value, long time); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Object... values); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Long time, Object... values); /** * 从List结构中移除属性 */ Long lRemove(String key, long count, Object value); } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/service/UmsAdminService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.domain.UmsResource; import java.util.List; /** * @auther macrozheng * @description 后台用户管理Service * @date 2020/10/15 * @github https://github.com/macrozheng */ public interface UmsAdminService { /** * 根据用户名获取用户信息 */ AdminUserDetails getAdminByUsername(String username); /** * 获取所以权限列表 */ List getResourceList(); /** * 用户名密码登录 */ String login(String username, String password); } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/service/UmsMemberService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.common.api.CommonResult; /** * @auther macrozheng * @description 会员管理Service * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface UmsMemberService { /** * 生成验证码 */ CommonResult generateAuthCode(String telephone); /** * 判断验证码和手机号码是否匹配 */ CommonResult verifyAuthCode(String telephone, String authCode); } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/service/impl/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; /** * @auther macrozheng * @description 搜索商品管理Service实现类 * @date 2018/6/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/service/impl/MemberReadHistoryServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import com.macro.mall.tiny.nosql.mongodb.repository.MemberReadHistoryRepository; import com.macro.mall.tiny.service.MemberReadHistoryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @auther macrozheng * @description 会员浏览记录管理Service实现类 * @date 2018/8/3 * @github https://github.com/macrozheng */ @Service public class MemberReadHistoryServiceImpl implements MemberReadHistoryService { @Autowired private MemberReadHistoryRepository memberReadHistoryRepository; @Override public int create(MemberReadHistory memberReadHistory) { memberReadHistory.setId(null); memberReadHistory.setCreateTime(new Date()); memberReadHistoryRepository.save(memberReadHistory); return 1; } @Override public int delete(List ids) { List deleteList = new ArrayList<>(); for(String id:ids){ MemberReadHistory memberReadHistory = new MemberReadHistory(); memberReadHistory.setId(id); deleteList.add(memberReadHistory); } memberReadHistoryRepository.deleteAll(deleteList); return ids.size(); } @Override public List list(Long memberId) { return memberReadHistoryRepository.findByMemberIdOrderByCreateTimeDesc(memberId); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/service/impl/RedisServiceImpl.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.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @auther macrozheng * @description redis操作实现类 * @date 2020/3/3 * @github https://github.com/macrozheng */ @Service public class RedisServiceImpl implements RedisService { @Autowired private RedisTemplate redisTemplate; @Override public void set(String key, Object value, long time) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } @Override public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); } @Override public Object get(String key) { return redisTemplate.opsForValue().get(key); } @Override public Boolean del(String key) { return redisTemplate.delete(key); } @Override public Long del(List keys) { return redisTemplate.delete(keys); } @Override public Boolean expire(String key, long time) { return redisTemplate.expire(key, time, TimeUnit.SECONDS); } @Override public Long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } @Override public Boolean hasKey(String key) { return redisTemplate.hasKey(key); } @Override public Long incr(String key, long delta) { return redisTemplate.opsForValue().increment(key, delta); } @Override public Long decr(String key, long delta) { return redisTemplate.opsForValue().increment(key, -delta); } @Override public Object hGet(String key, String hashKey) { return redisTemplate.opsForHash().get(key, hashKey); } @Override public Boolean hSet(String key, String hashKey, Object value, long time) { redisTemplate.opsForHash().put(key, hashKey, value); return expire(key, time); } @Override public void hSet(String key, String hashKey, Object value) { redisTemplate.opsForHash().put(key, hashKey, value); } @Override public Map hGetAll(String key) { return redisTemplate.opsForHash().entries(key); } @Override public Boolean hSetAll(String key, Map map, long time) { redisTemplate.opsForHash().putAll(key, map); return expire(key, time); } @Override public void hSetAll(String key, Map map) { redisTemplate.opsForHash().putAll(key, map); } @Override public void hDel(String key, Object... hashKey) { redisTemplate.opsForHash().delete(key, hashKey); } @Override public Boolean hHasKey(String key, String hashKey) { return redisTemplate.opsForHash().hasKey(key, hashKey); } @Override public Long hIncr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, delta); } @Override public Long hDecr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, -delta); } @Override public Set sMembers(String key) { return redisTemplate.opsForSet().members(key); } @Override public Long sAdd(String key, Object... values) { return redisTemplate.opsForSet().add(key, values); } @Override public Long sAdd(String key, long time, Object... values) { Long count = redisTemplate.opsForSet().add(key, values); expire(key, time); return count; } @Override public Boolean sIsMember(String key, Object value) { return redisTemplate.opsForSet().isMember(key, value); } @Override public Long sSize(String key) { return redisTemplate.opsForSet().size(key); } @Override public Long sRemove(String key, Object... values) { return redisTemplate.opsForSet().remove(key, values); } @Override public List lRange(String key, long start, long end) { return redisTemplate.opsForList().range(key, start, end); } @Override public Long lSize(String key) { return redisTemplate.opsForList().size(key); } @Override public Object lIndex(String key, long index) { return redisTemplate.opsForList().index(key, index); } @Override public Long lPush(String key, Object value) { return redisTemplate.opsForList().rightPush(key, value); } @Override public Long lPush(String key, Object value, long time) { Long index = redisTemplate.opsForList().rightPush(key, value); expire(key, time); return index; } @Override public Long lPushAll(String key, Object... values) { return redisTemplate.opsForList().rightPushAll(key, values); } @Override public Long lPushAll(String key, Long time, Object... values) { Long count = redisTemplate.opsForList().rightPushAll(key, values); expire(key, time); return count; } @Override public Long lRemove(String key, long count, Object value) { return redisTemplate.opsForList().remove(key, count, value); } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/service/impl/UmsAdminServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.collection.CollUtil; import com.macro.mall.tiny.common.utils.JwtTokenUtil; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.domain.UmsResource; import com.macro.mall.tiny.service.UmsAdminService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; 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.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description 后台用户管理Service实现类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Slf4j @Service public class UmsAdminServiceImpl implements UmsAdminService { /** * 存放默认用户信息 */ private List adminUserDetailsList = new ArrayList<>(); /** * 存放默认资源信息 */ private List resourceList = new ArrayList<>(); @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private PasswordEncoder passwordEncoder; @PostConstruct private void init(){ adminUserDetailsList.add(AdminUserDetails.builder() .username("admin") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("brand:create","brand:update","brand:delete","brand:list","brand:listAll")) .build()); adminUserDetailsList.add(AdminUserDetails.builder() .username("macro") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("brand:listAll")) .build()); resourceList.add(UmsResource.builder() .id(1L) .name("brand:create") .url("/brand/create") .build()); resourceList.add(UmsResource.builder() .id(2L) .name("brand:update") .url("/brand/update/**") .build()); resourceList.add(UmsResource.builder() .id(3L) .name("brand:delete") .url("/brand/delete/**") .build()); resourceList.add(UmsResource.builder() .id(4L) .name("brand:list") .url("/brand/list") .build()); resourceList.add(UmsResource.builder() .id(5L) .name("brand:listAll") .url("/brand/listAll") .build()); } @Override public AdminUserDetails getAdminByUsername(String username) { List findList = adminUserDetailsList.stream().filter(item -> item.getUsername().equals(username)).collect(Collectors.toList()); if(CollUtil.isNotEmpty(findList)){ return findList.get(0); } return null; } @Override public List getResourceList() { return resourceList; } @Override public String login(String username, String password) { String token = null; try { UserDetails userDetails = getAdminByUsername(username); if(userDetails==null){ return token; } 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) { log.warn("登录异常:{}", e.getMessage()); } return token; } } ================================================ FILE: mall-tiny-06/src/main/java/com/macro/mall/tiny/service/impl/UmsMemberServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.util.StrUtil; 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; /** * @auther macrozheng * @description 会员管理Service实现类 * @date 2018/8/3 * @github https://github.com/macrozheng */ @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 (StrUtil.isEmpty(authCode)) { return CommonResult.failed("请输入验证码"); } String realAuthCode = (String) redisService.get(REDIS_KEY_PREFIX_AUTH_CODE + telephone); boolean result = authCode.equals(realAuthCode); if (result) { return CommonResult.success(null, "验证码校验成功"); } else { return CommonResult.failed("验证码不正确"); } } } ================================================ FILE: mall-tiny-06/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER redis: host: localhost # Redis服务器地址 database: 0 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 password: # Redis服务器连接密码(默认为空) lettuce: pool: max-active: 8 # 连接池最大连接数 max-idle: 8 # 连接池最大空闲连接数 min-idle: 0 # 连接池最小空闲连接数 max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制 data: elasticsearch: repositories: enabled: true # 开启ES仓库配置,自动为仓库接口生成实现类 mongodb: host: localhost # MongoDB的连接地址 port: 27017 # MongoDB的连接端口号 database: mall-port # MongoDB的连接的数据库 elasticsearch: uris: http://localhost:9200 # ES的连接地址及端口号 mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml # 自定义redis key redis: key: prefix: authCode: "portal:authCode:" expire: authCode: 120 # 验证码超期时间 # 自定义jwt key jwt: tokenHeader: Authorization #JWT存储的请求头 secret: mySecret #JWT加解密使用的密钥 expiration: 604800 #JWT的超期限时间(60*60*24) tokenHead: Bearer #JWT负载中拿到开头 secure: ignored: urls: #安全路径白名单 - /swagger-ui/ - /swagger-resources/** - /**/v2/api-docs - /**/*.html - /**/*.js - /**/*.css - /**/*.png - /favicon.ico - /actuator/** - /druid/** - /admin/** - /esProduct/** - /member/readHistory/** ================================================ FILE: mall-tiny-06/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-06/src/main/resources/dao/EsProductDao.xml ================================================ ================================================ FILE: mall-tiny-06/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-06/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-06/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-07/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-07/pom.xml ================================================ 4.0.0 mall-tiny-07 1.0-SNAPSHOT mall-tiny-07 Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-security io.jsonwebtoken jjwt ${jjwt.version} org.springframework.boot spring-boot-starter-data-elasticsearch org.springframework.boot spring-boot-starter-data-mongodb org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import org.springframework.data.domain.Page; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } /** * 将SpringData分页后的list转为分页信息 */ public static CommonPage restPage(Page pageInfo) { CommonPage result = new CommonPage(); result.setTotalPage(pageInfo.getTotalPages()); result.setPageNum(pageInfo.getNumber()); result.setPageSize(pageInfo.getSize()); result.setTotal(pageInfo.getTotalElements()); result.setList(pageInfo.getContent()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/common/utils/JwtTokenUtil.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; /** * @auther macrozheng * @description JwtToken生成的工具类 * JWT token的格式:header.payload.signature * header的格式(算法、token的类型): * {"alg": "HS512","typ": "JWT"} * payload的格式(用户名、创建时间、生成时间): * {"sub":"wang","created":1489079981393,"exp":1489684781} * signature的生成算法: * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) * @date 2018/4/26 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/component/CancelOrderReceiver.java ================================================ package com.macro.mall.tiny.component; import com.macro.mall.tiny.service.OmsPortalOrderService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @auther macrozheng * @description 取消订单消息的处理者 * @date 2018/9/14 * @github https://github.com/macrozheng */ @Component @RabbitListener(queues = "mall.order.cancel") public class CancelOrderReceiver { private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderReceiver.class); @Autowired private OmsPortalOrderService portalOrderService; @RabbitHandler public void handle(Long orderId){ LOGGER.info("receive delay message orderId:{}",orderId); portalOrderService.cancelOrder(orderId); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/component/CancelOrderSender.java ================================================ package com.macro.mall.tiny.component; import com.macro.mall.tiny.dto.QueueEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.AmqpException; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessagePostProcessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @auther macrozheng * @description 取消订单消息的发出者 * @date 2018/9/14 * @github https://github.com/macrozheng */ @Component public class CancelOrderSender { private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderSender.class); @Autowired private AmqpTemplate amqpTemplate; public void sendMessage(Long orderId,final long delayTimes){ //给延迟队列发送消息 amqpTemplate.convertAndSend(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange(), QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey(), orderId, new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { //给消息设置延迟毫秒值 message.getMessageProperties().setExpiration(String.valueOf(delayTimes)); return message; } }); LOGGER.info("send delay message orderId:{}",orderId); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/component/JwtAuthenticationTokenFilter.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; /** * @auther macrozheng * @description JWT登录授权过滤器 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当未登录或者token失效访问接口时,自定义的返回结果 * @date 2018/5/14 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当访问接口没有权限时,自定义的返回结果 * @date 2018/4/26 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/config/IgnoreUrlsConfig.java ================================================ package com.macro.mall.tiny.config; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; /** * @auther macrozheng * @description 用于配置白名单资源路径 * @date 2018/11/5 * @github https://github.com/macrozheng */ @Getter @Setter @Configuration @ConfigurationProperties(prefix = "secure.ignored") public class IgnoreUrlsConfig { private List urls = new ArrayList<>(); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/config/MallSecurityConfig.java ================================================ package com.macro.mall.tiny.config; import com.macro.mall.tiny.domain.AdminUserDetails; 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.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; /** * @auther macrozheng * @description 自定义配置,用于配置如何获取用户信息 * @date 2022/5/20 * @github https://github.com/macrozheng */ @Configuration public class MallSecurityConfig { @Autowired private UmsAdminService adminService; @Bean public UserDetailsService userDetailsService() { //获取登录用户信息 return username -> { AdminUserDetails admin = adminService.getAdminByUsername(username); if (admin != null) { return admin; } throw new UsernameNotFoundException("用户名或密码错误"); }; } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan({"com.macro.mall.tiny.mbg.mapper","com.macro.mall.tiny.dao"}) public class MyBatisConfig { } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/config/RabbitMqConfig.java ================================================ package com.macro.mall.tiny.config; import com.macro.mall.tiny.dto.QueueEnum; import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description 消息队列配置 * @date 2018/9/14 * @github https://github.com/macrozheng */ @Configuration public class RabbitMqConfig { /** * 订单消息实际消费队列所绑定的交换机 */ @Bean DirectExchange orderDirect() { return ExchangeBuilder .directExchange(QueueEnum.QUEUE_ORDER_CANCEL.getExchange()) .durable(true) .build(); } /** * 订单延迟队列所绑定的交换机 */ @Bean DirectExchange orderTtlDirect() { return ExchangeBuilder .directExchange(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange()) .durable(true) .build(); } /** * 订单实际消费队列 */ @Bean public Queue orderQueue() { return new Queue(QueueEnum.QUEUE_ORDER_CANCEL.getName()); } /** * 订单延迟队列(死信队列) */ @Bean public Queue orderTtlQueue() { return QueueBuilder .durable(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getName()) .withArgument("x-dead-letter-exchange", QueueEnum.QUEUE_ORDER_CANCEL.getExchange())//到期后转发的交换机 .withArgument("x-dead-letter-routing-key", QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey())//到期后转发的路由键 .build(); } /** * 将订单队列绑定到交换机 */ @Bean Binding orderBinding(DirectExchange orderDirect,Queue orderQueue){ return BindingBuilder .bind(orderQueue) .to(orderDirect) .with(QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey()); } /** * 将订单延迟队列绑定到交换机 */ @Bean Binding orderTtlBinding(DirectExchange orderTtlDirect,Queue orderTtlQueue){ return BindingBuilder .bind(orderTtlQueue) .to(orderTtlDirect) .with(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey()); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/config/RedisConfig.java ================================================ package com.macro.mall.tiny.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /** * @auther macrozheng * @description Redis配置类 * @date 2020/3/2 * @github https://github.com/macrozheng */ @EnableCaching @Configuration public class RedisConfig { /** * redis数据库自定义key */ public static final String REDIS_KEY_DATABASE="mall"; @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisSerializer serializer = redisSerializer(); RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(serializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(serializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public RedisSerializer redisSerializer() { //创建JSON序列化器 Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //必须设置,否则无法将JSON转化为对象,会转化成Map类型 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(objectMapper); return serializer; } @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); //设置Redis缓存有效期为1天 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1)); return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/config/SecurityConfig.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.domain.AdminUserDetails; 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.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 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.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * @auther macrozheng * @description SpringSecurity的配置 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) public class SecurityConfig { @Autowired private RestfulAccessDeniedHandler restfulAccessDeniedHandler; @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Bean SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity .authorizeRequests(); //不需要保护的资源路径允许访问 for (String url : ignoreUrlsConfig.getUrls()) { registry.antMatchers(url).permitAll(); } //允许跨域请求的OPTIONS请求 registry.antMatchers(HttpMethod.OPTIONS) .permitAll(); httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf .disable() .sessionManagement()// 基于token,所以不需要session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .anyRequest()// 除上面外的所有请求全部需要鉴权认证 .authenticated(); // 禁用缓存 httpSecurity.headers().cacheControl(); // 添加JWT filter httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); //添加自定义未授权和未登录结果返回 httpSecurity.exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler) .authenticationEntryPoint(restAuthenticationEntryPoint); return httpSecurity.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){ return new JwtAuthenticationTokenFilter(); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger文档的配置(带认证) * @date 2022/11/22 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .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(new Contact("macro", null, null)) .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; } @Bean public BeanPostProcessor generateBeanPostProcessor(){ return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/controller/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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 搜索商品管理Controller * @date 2018/6/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "EsProductController") @Tag(name = "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 delete(@PathVariable Long id) { esProductService.delete(id); return CommonResult.success(null); } @ApiOperation(value = "根据id批量删除商品") @RequestMapping(value = "/delete/batch", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("ids") List ids) { esProductService.delete(ids); return CommonResult.success(null); } @ApiOperation(value = "根据id创建商品") @RequestMapping(value = "/create/{id}", method = RequestMethod.POST) @ResponseBody public CommonResult create(@PathVariable Long id) { EsProduct esProduct = esProductService.create(id); if (esProduct != null) { return CommonResult.success(esProduct); } else { return CommonResult.failed(); } } @ApiOperation(value = "简单搜索") @RequestMapping(value = "/search/simple", method = RequestMethod.GET) @ResponseBody public CommonResult> search(@RequestParam(required = false) String keyword, @RequestParam(required = false, defaultValue = "0") Integer pageNum, @RequestParam(required = false, defaultValue = "5") Integer pageSize) { Page esProductPage = esProductService.search(keyword, pageNum, pageSize); return CommonResult.success(CommonPage.restPage(esProductPage)); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/controller/MemberReadHistoryController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import com.macro.mall.tiny.service.MemberReadHistoryService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 会员商品浏览记录管理Controller * @date 2018/8/3 * @github https://github.com/macrozheng */ @Controller @Api(tags = "MemberReadHistoryController") @Tag(name = "MemberReadHistoryController", description = "会员商品浏览记录管理") @RequestMapping("/member/readHistory") public class MemberReadHistoryController { @Autowired private MemberReadHistoryService memberReadHistoryService; @ApiOperation("创建浏览记录") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody public CommonResult create(@RequestBody MemberReadHistory memberReadHistory) { int count = memberReadHistoryService.create(memberReadHistory); if (count > 0) { return CommonResult.success(count); } else { return CommonResult.failed(); } } @ApiOperation("删除浏览记录") @RequestMapping(value = "/delete", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("ids") List ids) { int count = memberReadHistoryService.delete(ids); if (count > 0) { return CommonResult.success(count); } else { return CommonResult.failed(); } } @ApiOperation("展示浏览记录") @RequestMapping(value = "/list", method = RequestMethod.GET) @ResponseBody public CommonResult> list(Long memberId) { List memberReadHistoryList = memberReadHistoryService.list(memberId); return CommonResult.success(memberReadHistoryList); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/controller/OmsPortalOrderController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.dto.OrderParam; import com.macro.mall.tiny.service.OmsPortalOrderService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; /** * @auther macrozheng * @description 订单管理Controller * @date 2018/8/30 * @github https://github.com/macrozheng */ @Controller @Api(tags = "OmsPortalOrderController") @Tag(name = "OmsPortalOrderController", description = "订单管理") @RequestMapping("/order") public class OmsPortalOrderController { @Autowired private OmsPortalOrderService portalOrderService; @ApiOperation("根据购物车信息生成订单") @RequestMapping(value = "/generateOrder", method = RequestMethod.POST) @ResponseBody public Object generateOrder(@RequestBody OrderParam orderParam) { return portalOrderService.generateOrder(orderParam); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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 @PreAuthorize("hasAuthority('brand:listAll')") public CommonResult> getBrandList() { return CommonResult.success(brandService.listAllBrand()); } @ApiOperation("添加品牌") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody @PreAuthorize("hasAuthority('brand:create')") 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 @PreAuthorize("hasAuthority('brand:update')") 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 @PreAuthorize("hasAuthority('brand:delete')") 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 @PreAuthorize("hasAuthority('brand:list')") 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)); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/controller/UmsAdminController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.domain.UmsResource; import com.macro.mall.tiny.service.UmsAdminService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; 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; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @auther macrozheng * @description 后台用户管理 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsAdminController") @Tag(name = "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 = "登录以后返回token") @RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public CommonResult login(@RequestParam String username, @RequestParam String password) { String token = adminService.login(username, password); if (token == null) { return CommonResult.validateFailed("用户名或密码错误"); } Map tokenMap = new HashMap<>(); tokenMap.put("token", token); tokenMap.put("tokenHead", tokenHead); return CommonResult.success(tokenMap); } @ApiOperation(value = "登录以后返回token") @RequestMapping(value = "/resourceList", method = RequestMethod.POST) @ResponseBody public CommonResult> resourceList() { List resourceList = adminService.getResourceList(); return CommonResult.success(resourceList); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/controller/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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 会员登录注册管理Controller * @date 2018/8/3 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsMemberController") @Tag(name = "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); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/dao/EsProductDao.java ================================================ package com.macro.mall.tiny.dao; import com.macro.mall.tiny.nosql.elasticsearch.document.EsProduct; import org.apache.ibatis.annotations.Param; import java.util.List; /** * @auther macrozheng * @description 搜索系统中的商品管理自定义Dao * @date 2018/6/19 * @github https://github.com/macrozheng */ public interface EsProductDao { List getAllEsProductList(@Param("id") Long id); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/domain/AdminUserDetails.java ================================================ package com.macro.mall.tiny.domain; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; 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; /** * @auther macrozheng * @description SpringSecurity用户信息封装类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode(callSuper = false) @Builder public class AdminUserDetails implements UserDetails { private String username; private String password; private List authorityList; @Override public Collection getAuthorities() { return this.authorityList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/domain/UmsResource.java ================================================ package com.macro.mall.tiny.domain; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.Date; /** *

* 后台资源表 *

* * @author macro * @since 2020-08-21 */ @Data @EqualsAndHashCode(callSuper = false) @ApiModel(value="UmsResource对象", description="后台资源表") @Builder public class UmsResource{ private Long id; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "资源名称") private String name; @ApiModelProperty(value = "资源URL") private String url; @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "资源分类ID") private Long categoryId; } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/dto/OrderParam.java ================================================ package com.macro.mall.tiny.dto; import lombok.Data; /** * @auther macrozheng * @description 生成订单时传入的参数 * @date 2018/8/30 * @github https://github.com/macrozheng */ @Data public class OrderParam { //收货地址id private Long memberReceiveAddressId; //优惠券id private Long couponId; //使用的积分数 private Integer useIntegration; //支付方式 private Integer payType; } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/dto/QueueEnum.java ================================================ package com.macro.mall.tiny.dto; import lombok.Getter; /** * @auther macrozheng * @description 消息队列枚举配置 * @date 2018/9/14 * @github https://github.com/macrozheng */ @Getter public enum QueueEnum { /** * 消息通知队列 */ QUEUE_ORDER_CANCEL("mall.order.direct", "mall.order.cancel", "mall.order.cancel"), /** * 消息通知ttl队列 */ QUEUE_TTL_ORDER_CANCEL("mall.order.direct.ttl", "mall.order.cancel.ttl", "mall.order.cancel.ttl"); /** * 交换机名称 */ private String exchange; /** * 队列名称 */ private String name; /** * 路由键 */ private String routeKey; QueueEnum(String exchange, String name, String routeKey) { this.exchange = exchange; this.name = name; this.routeKey = routeKey; } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { int countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand record); int insertSelective(PmsBrand record); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExample(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand record); int updateByPrimaryKeyWithBLOBs(PmsBrand record); int updateByPrimaryKey(PmsBrand record); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { private Long id; private String name; @ApiModelProperty(value = "首字母") private String firstLetter; private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/document/EsProduct.java ================================================ package com.macro.mall.tiny.nosql.elasticsearch.document; import lombok.Data; import lombok.EqualsAndHashCode; 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 org.springframework.data.elasticsearch.annotations.Setting; import java.io.Serializable; import java.math.BigDecimal; import java.util.List; /** * @auther macrozheng * @description 搜索商品的信息 * @date 2018/6/19 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode @Document(indexName = "pms") @Setting(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; } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/document/EsProductAttributeValue.java ================================================ package com.macro.mall.tiny.nosql.elasticsearch.document; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; import java.io.Serializable; /** * @auther macrozheng * @description 搜索商品的属性信息 * @date 2018/6/27 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode public class EsProductAttributeValue implements Serializable { private static final long serialVersionUID = 1L; private Long id; private Long productAttributeId; //属性值 @Field(type = FieldType.Keyword) private String value; //属性参数:0->规格;1->参数 private Integer type; //属性名称 @Field(type=FieldType.Keyword) private String name; } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/repository/EsProductRepository.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; /** * @auther macrozheng * @description 商品ES操作类 * @date 2018/6/19 * @github https://github.com/macrozheng */ public interface EsProductRepository extends ElasticsearchRepository { /** * 搜索查询 * * @param name 商品名称 * @param subTitle 商品标题 * @param keywords 商品关键字 * @param page 分页信息 * @return */ Page findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/nosql/mongodb/document/MemberReadHistory.java ================================================ package com.macro.mall.tiny.nosql.mongodb.document; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; /** * @auther macrozheng * @description 用户商品浏览历史记录 * @date 2018/8/3 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode @Document public class MemberReadHistory { @Id private String id; @Indexed private Long memberId; private String memberNickname; private String memberIcon; @Indexed private Long productId; private String productName; private String productPic; private String productSubTitle; private String productPrice; private Date createTime; } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/nosql/mongodb/repository/MemberReadHistoryRepository.java ================================================ package com.macro.mall.tiny.nosql.mongodb.repository; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import org.springframework.data.mongodb.repository.MongoRepository; import java.util.List; /** * @auther macrozheng * @description 会员商品浏览历史Repository * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface MemberReadHistoryRepository extends MongoRepository { /** * 根据会员id按时间倒序获取浏览记录 * @param memberId 会员id */ List findByMemberIdOrderByCreateTimeDesc(Long memberId); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/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; /** * @auther macrozheng * @description 商品搜索管理Service * @date 2018/6/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/MemberReadHistoryService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import java.util.List; /** * @auther macrozheng * @description 会员浏览记录管理Service * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface MemberReadHistoryService { /** * 生成浏览记录 */ int create(MemberReadHistory memberReadHistory); /** * 批量删除浏览记录 */ int delete(List ids); /** * 获取用户浏览历史记录 */ List list(Long memberId); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/OmsPortalOrderService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.dto.OrderParam; import org.springframework.transaction.annotation.Transactional; /** * @auther macrozheng * @description 前台订单管理Service * @date 2018/8/30 * @github https://github.com/macrozheng */ public interface OmsPortalOrderService { /** * 根据提交信息生成订单 */ @Transactional CommonResult generateOrder(OrderParam orderParam); /** * 取消单个超时订单 */ @Transactional void cancelOrder(Long orderId); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/RedisService.java ================================================ package com.macro.mall.tiny.service; import java.util.List; import java.util.Map; import java.util.Set; /** * @auther macrozheng * @description redis操作Service * @date 2020/3/3 * @github https://github.com/macrozheng */ public interface RedisService { /** * 保存属性 */ void set(String key, Object value, long time); /** * 保存属性 */ void set(String key, Object value); /** * 获取属性 */ Object get(String key); /** * 删除属性 */ Boolean del(String key); /** * 批量删除属性 */ Long del(List keys); /** * 设置过期时间 */ Boolean expire(String key, long time); /** * 获取过期时间 */ Long getExpire(String key); /** * 判断是否有该属性 */ Boolean hasKey(String key); /** * 按delta递增 */ Long incr(String key, long delta); /** * 按delta递减 */ Long decr(String key, long delta); /** * 获取Hash结构中的属性 */ Object hGet(String key, String hashKey); /** * 向Hash结构中放入一个属性 */ Boolean hSet(String key, String hashKey, Object value, long time); /** * 向Hash结构中放入一个属性 */ void hSet(String key, String hashKey, Object value); /** * 直接获取整个Hash结构 */ Map hGetAll(String key); /** * 直接设置整个Hash结构 */ Boolean hSetAll(String key, Map map, long time); /** * 直接设置整个Hash结构 */ void hSetAll(String key, Map map); /** * 删除Hash结构中的属性 */ void hDel(String key, Object... hashKey); /** * 判断Hash结构中是否有该属性 */ Boolean hHasKey(String key, String hashKey); /** * Hash结构中属性递增 */ Long hIncr(String key, String hashKey, Long delta); /** * Hash结构中属性递减 */ Long hDecr(String key, String hashKey, Long delta); /** * 获取Set结构 */ Set sMembers(String key); /** * 向Set结构中添加属性 */ Long sAdd(String key, Object... values); /** * 向Set结构中添加属性 */ Long sAdd(String key, long time, Object... values); /** * 是否为Set中的属性 */ Boolean sIsMember(String key, Object value); /** * 获取Set结构的长度 */ Long sSize(String key); /** * 删除Set结构中的属性 */ Long sRemove(String key, Object... values); /** * 获取List结构中的属性 */ List lRange(String key, long start, long end); /** * 获取List结构的长度 */ Long lSize(String key); /** * 根据索引获取List中的属性 */ Object lIndex(String key, long index); /** * 向List结构中添加属性 */ Long lPush(String key, Object value); /** * 向List结构中添加属性 */ Long lPush(String key, Object value, long time); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Object... values); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Long time, Object... values); /** * 从List结构中移除属性 */ Long lRemove(String key, long count, Object value); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/UmsAdminService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.domain.UmsResource; import java.util.List; /** * @auther macrozheng * @description 后台用户管理Service * @date 2020/10/15 * @github https://github.com/macrozheng */ public interface UmsAdminService { /** * 根据用户名获取用户信息 */ AdminUserDetails getAdminByUsername(String username); /** * 获取所以权限列表 */ List getResourceList(); /** * 用户名密码登录 */ String login(String username, String password); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/UmsMemberService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.common.api.CommonResult; /** * @auther macrozheng * @description 会员管理Service * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface UmsMemberService { /** * 生成验证码 */ CommonResult generateAuthCode(String telephone); /** * 判断验证码和手机号码是否匹配 */ CommonResult verifyAuthCode(String telephone, String authCode); } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/impl/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; /** * @auther macrozheng * @description 搜索商品管理Service实现类 * @date 2018/6/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/impl/MemberReadHistoryServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import com.macro.mall.tiny.nosql.mongodb.repository.MemberReadHistoryRepository; import com.macro.mall.tiny.service.MemberReadHistoryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @auther macrozheng * @description 会员浏览记录管理Service实现类 * @date 2018/8/3 * @github https://github.com/macrozheng */ @Service public class MemberReadHistoryServiceImpl implements MemberReadHistoryService { @Autowired private MemberReadHistoryRepository memberReadHistoryRepository; @Override public int create(MemberReadHistory memberReadHistory) { memberReadHistory.setId(null); memberReadHistory.setCreateTime(new Date()); memberReadHistoryRepository.save(memberReadHistory); return 1; } @Override public int delete(List ids) { List deleteList = new ArrayList<>(); for(String id:ids){ MemberReadHistory memberReadHistory = new MemberReadHistory(); memberReadHistory.setId(id); deleteList.add(memberReadHistory); } memberReadHistoryRepository.deleteAll(deleteList); return ids.size(); } @Override public List list(Long memberId) { return memberReadHistoryRepository.findByMemberIdOrderByCreateTimeDesc(memberId); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/impl/OmsPortalOrderServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.component.CancelOrderSender; import com.macro.mall.tiny.dto.OrderParam; import com.macro.mall.tiny.service.OmsPortalOrderService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @auther macrozheng * @description 前台订单管理Service * @date 2018/8/30 * @github https://github.com/macrozheng */ @Service public class OmsPortalOrderServiceImpl implements OmsPortalOrderService { private static Logger LOGGER = LoggerFactory.getLogger(OmsPortalOrderServiceImpl.class); @Autowired private CancelOrderSender cancelOrderSender; @Override public CommonResult generateOrder(OrderParam orderParam) { //todo 执行一系类下单操作,具体参考mall项目 LOGGER.info("process generateOrder"); //下单完成后开启一个延迟消息,用于当用户没有付款时取消订单(orderId应该在下单后生成) sendDelayMessageCancelOrder(11L); return CommonResult.success(null, "下单成功"); } @Override public void cancelOrder(Long orderId) { //todo 执行一系类取消订单操作,具体参考mall项目 LOGGER.info("process cancelOrder orderId:{}",orderId); } private void sendDelayMessageCancelOrder(Long orderId) { //获取订单超时时间,假设为60分钟(测试用的30秒) long delayTimes = 30 * 1000; //发送延迟消息 cancelOrderSender.sendMessage(orderId, delayTimes); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/impl/RedisServiceImpl.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.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @auther macrozheng * @description redis操作实现类 * @date 2020/3/3 * @github https://github.com/macrozheng */ @Service public class RedisServiceImpl implements RedisService { @Autowired private RedisTemplate redisTemplate; @Override public void set(String key, Object value, long time) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } @Override public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); } @Override public Object get(String key) { return redisTemplate.opsForValue().get(key); } @Override public Boolean del(String key) { return redisTemplate.delete(key); } @Override public Long del(List keys) { return redisTemplate.delete(keys); } @Override public Boolean expire(String key, long time) { return redisTemplate.expire(key, time, TimeUnit.SECONDS); } @Override public Long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } @Override public Boolean hasKey(String key) { return redisTemplate.hasKey(key); } @Override public Long incr(String key, long delta) { return redisTemplate.opsForValue().increment(key, delta); } @Override public Long decr(String key, long delta) { return redisTemplate.opsForValue().increment(key, -delta); } @Override public Object hGet(String key, String hashKey) { return redisTemplate.opsForHash().get(key, hashKey); } @Override public Boolean hSet(String key, String hashKey, Object value, long time) { redisTemplate.opsForHash().put(key, hashKey, value); return expire(key, time); } @Override public void hSet(String key, String hashKey, Object value) { redisTemplate.opsForHash().put(key, hashKey, value); } @Override public Map hGetAll(String key) { return redisTemplate.opsForHash().entries(key); } @Override public Boolean hSetAll(String key, Map map, long time) { redisTemplate.opsForHash().putAll(key, map); return expire(key, time); } @Override public void hSetAll(String key, Map map) { redisTemplate.opsForHash().putAll(key, map); } @Override public void hDel(String key, Object... hashKey) { redisTemplate.opsForHash().delete(key, hashKey); } @Override public Boolean hHasKey(String key, String hashKey) { return redisTemplate.opsForHash().hasKey(key, hashKey); } @Override public Long hIncr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, delta); } @Override public Long hDecr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, -delta); } @Override public Set sMembers(String key) { return redisTemplate.opsForSet().members(key); } @Override public Long sAdd(String key, Object... values) { return redisTemplate.opsForSet().add(key, values); } @Override public Long sAdd(String key, long time, Object... values) { Long count = redisTemplate.opsForSet().add(key, values); expire(key, time); return count; } @Override public Boolean sIsMember(String key, Object value) { return redisTemplate.opsForSet().isMember(key, value); } @Override public Long sSize(String key) { return redisTemplate.opsForSet().size(key); } @Override public Long sRemove(String key, Object... values) { return redisTemplate.opsForSet().remove(key, values); } @Override public List lRange(String key, long start, long end) { return redisTemplate.opsForList().range(key, start, end); } @Override public Long lSize(String key) { return redisTemplate.opsForList().size(key); } @Override public Object lIndex(String key, long index) { return redisTemplate.opsForList().index(key, index); } @Override public Long lPush(String key, Object value) { return redisTemplate.opsForList().rightPush(key, value); } @Override public Long lPush(String key, Object value, long time) { Long index = redisTemplate.opsForList().rightPush(key, value); expire(key, time); return index; } @Override public Long lPushAll(String key, Object... values) { return redisTemplate.opsForList().rightPushAll(key, values); } @Override public Long lPushAll(String key, Long time, Object... values) { Long count = redisTemplate.opsForList().rightPushAll(key, values); expire(key, time); return count; } @Override public Long lRemove(String key, long count, Object value) { return redisTemplate.opsForList().remove(key, count, value); } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/impl/UmsAdminServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.collection.CollUtil; import com.macro.mall.tiny.common.utils.JwtTokenUtil; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.domain.UmsResource; import com.macro.mall.tiny.service.UmsAdminService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; 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.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description 后台用户管理Service实现类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Slf4j @Service public class UmsAdminServiceImpl implements UmsAdminService { /** * 存放默认用户信息 */ private List adminUserDetailsList = new ArrayList<>(); /** * 存放默认资源信息 */ private List resourceList = new ArrayList<>(); @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private PasswordEncoder passwordEncoder; @PostConstruct private void init(){ adminUserDetailsList.add(AdminUserDetails.builder() .username("admin") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("brand:create","brand:update","brand:delete","brand:list","brand:listAll")) .build()); adminUserDetailsList.add(AdminUserDetails.builder() .username("macro") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("brand:listAll")) .build()); resourceList.add(UmsResource.builder() .id(1L) .name("brand:create") .url("/brand/create") .build()); resourceList.add(UmsResource.builder() .id(2L) .name("brand:update") .url("/brand/update/**") .build()); resourceList.add(UmsResource.builder() .id(3L) .name("brand:delete") .url("/brand/delete/**") .build()); resourceList.add(UmsResource.builder() .id(4L) .name("brand:list") .url("/brand/list") .build()); resourceList.add(UmsResource.builder() .id(5L) .name("brand:listAll") .url("/brand/listAll") .build()); } @Override public AdminUserDetails getAdminByUsername(String username) { List findList = adminUserDetailsList.stream().filter(item -> item.getUsername().equals(username)).collect(Collectors.toList()); if(CollUtil.isNotEmpty(findList)){ return findList.get(0); } return null; } @Override public List getResourceList() { return resourceList; } @Override public String login(String username, String password) { String token = null; try { UserDetails userDetails = getAdminByUsername(username); if(userDetails==null){ return token; } 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) { log.warn("登录异常:{}", e.getMessage()); } return token; } } ================================================ FILE: mall-tiny-07/src/main/java/com/macro/mall/tiny/service/impl/UmsMemberServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.util.StrUtil; 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; /** * @auther macrozheng * @description 会员管理Service实现类 * @date 2018/8/3 * @github https://github.com/macrozheng */ @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 (StrUtil.isEmpty(authCode)) { return CommonResult.failed("请输入验证码"); } String realAuthCode = (String) redisService.get(REDIS_KEY_PREFIX_AUTH_CODE + telephone); boolean result = authCode.equals(realAuthCode); if (result) { return CommonResult.success(null, "验证码校验成功"); } else { return CommonResult.failed("验证码不正确"); } } } ================================================ FILE: mall-tiny-07/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER redis: host: localhost # Redis服务器地址 database: 0 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 password: # Redis服务器连接密码(默认为空) lettuce: pool: max-active: 8 # 连接池最大连接数 max-idle: 8 # 连接池最大空闲连接数 min-idle: 0 # 连接池最小空闲连接数 max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制 data: elasticsearch: repositories: enabled: true # 开启ES仓库配置,自动为仓库接口生成实现类 mongodb: host: localhost # MongoDB的连接地址 port: 27017 # MongoDB的连接端口号 database: mall-port # MongoDB的连接的数据库 elasticsearch: uris: http://localhost:9200 # ES的连接地址及端口号 rabbitmq: host: localhost port: 5672 virtual-host: /mall username: mall password: mall publisher-returns: true #消息发送到队列确认 publisher-confirm-type: simple #消息发送到交换器确认 mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml # 自定义redis key redis: key: prefix: authCode: "portal:authCode:" expire: authCode: 120 # 验证码超期时间 # 自定义jwt key jwt: tokenHeader: Authorization #JWT存储的请求头 secret: mySecret #JWT加解密使用的密钥 expiration: 604800 #JWT的超期限时间(60*60*24) tokenHead: Bearer #JWT负载中拿到开头 secure: ignored: urls: #安全路径白名单 - /swagger-ui/ - /swagger-resources/** - /**/v2/api-docs - /**/*.html - /**/*.js - /**/*.css - /**/*.png - /favicon.ico - /actuator/** - /druid/** - /admin/** - /esProduct/** - /member/readHistory/** - /order/** ================================================ FILE: mall-tiny-07/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-07/src/main/resources/dao/EsProductDao.xml ================================================ ================================================ FILE: mall-tiny-07/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-07/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-07/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-08/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-08/pom.xml ================================================ 4.0.0 mall-tiny-08 1.0-SNAPSHOT mall-tiny-08 Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-security io.jsonwebtoken jjwt ${jjwt.version} org.springframework.boot spring-boot-starter-data-elasticsearch org.springframework.boot spring-boot-starter-data-mongodb org.springframework.boot spring-boot-starter-amqp io.minio minio ${minio.version} org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import org.springframework.data.domain.Page; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } /** * 将SpringData分页后的list转为分页信息 */ public static CommonPage restPage(Page pageInfo) { CommonPage result = new CommonPage(); result.setTotalPage(pageInfo.getTotalPages()); result.setPageNum(pageInfo.getNumber()); result.setPageSize(pageInfo.getSize()); result.setTotal(pageInfo.getTotalElements()); result.setList(pageInfo.getContent()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/common/utils/JwtTokenUtil.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; /** * @auther macrozheng * @description JwtToken生成的工具类 * JWT token的格式:header.payload.signature * header的格式(算法、token的类型): * {"alg": "HS512","typ": "JWT"} * payload的格式(用户名、创建时间、生成时间): * {"sub":"wang","created":1489079981393,"exp":1489684781} * signature的生成算法: * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) * @date 2018/4/26 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/component/CancelOrderReceiver.java ================================================ package com.macro.mall.tiny.component; import com.macro.mall.tiny.service.OmsPortalOrderService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @auther macrozheng * @description 取消订单消息的处理者 * @date 2018/9/14 * @github https://github.com/macrozheng */ @Component @RabbitListener(queues = "mall.order.cancel") public class CancelOrderReceiver { private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderReceiver.class); @Autowired private OmsPortalOrderService portalOrderService; @RabbitHandler public void handle(Long orderId){ LOGGER.info("receive delay message orderId:{}",orderId); portalOrderService.cancelOrder(orderId); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/component/CancelOrderSender.java ================================================ package com.macro.mall.tiny.component; import com.macro.mall.tiny.dto.QueueEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.AmqpException; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessagePostProcessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @auther macrozheng * @description 取消订单消息的发出者 * @date 2018/9/14 * @github https://github.com/macrozheng */ @Component public class CancelOrderSender { private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderSender.class); @Autowired private AmqpTemplate amqpTemplate; public void sendMessage(Long orderId,final long delayTimes){ //给延迟队列发送消息 amqpTemplate.convertAndSend(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange(), QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey(), orderId, new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { //给消息设置延迟毫秒值 message.getMessageProperties().setExpiration(String.valueOf(delayTimes)); return message; } }); LOGGER.info("send delay message orderId:{}",orderId); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/component/JwtAuthenticationTokenFilter.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; /** * @auther macrozheng * @description JWT登录授权过滤器 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当未登录或者token失效访问接口时,自定义的返回结果 * @date 2018/5/14 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当访问接口没有权限时,自定义的返回结果 * @date 2018/4/26 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/config/GlobalCorsConfig.java ================================================ package com.macro.mall.tiny.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; /** * @auther macrozheng * @description 全局跨域配置 * @date 2019/7/27 * @github https://github.com/macrozheng */ @Configuration public class GlobalCorsConfig { /** * 允许跨域调用的过滤器 */ @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); //允许所有域名进行跨域调用 config.addAllowedOriginPattern("*"); //允许跨越发送cookie config.setAllowCredentials(true); //放行全部原始头信息 config.addAllowedHeader("*"); //允许所有请求方法跨域调用 config.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/config/IgnoreUrlsConfig.java ================================================ package com.macro.mall.tiny.config; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; /** * @auther macrozheng * @description 用于配置白名单资源路径 * @date 2018/11/5 * @github https://github.com/macrozheng */ @Getter @Setter @Configuration @ConfigurationProperties(prefix = "secure.ignored") public class IgnoreUrlsConfig { private List urls = new ArrayList<>(); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/config/MallSecurityConfig.java ================================================ package com.macro.mall.tiny.config; import com.macro.mall.tiny.domain.AdminUserDetails; 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.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; /** * @auther macrozheng * @description 自定义配置,用于配置如何获取用户信息 * @date 2022/5/20 * @github https://github.com/macrozheng */ @Configuration public class MallSecurityConfig { @Autowired private UmsAdminService adminService; @Bean public UserDetailsService userDetailsService() { //获取登录用户信息 return username -> { AdminUserDetails admin = adminService.getAdminByUsername(username); if (admin != null) { return admin; } throw new UsernameNotFoundException("用户名或密码错误"); }; } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @EnableTransactionManagement @MapperScan({"com.macro.mall.tiny.mbg.mapper","com.macro.mall.tiny.dao"}) public class MyBatisConfig { } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/config/RabbitMqConfig.java ================================================ package com.macro.mall.tiny.config; import com.macro.mall.tiny.dto.QueueEnum; import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description 消息队列配置 * @date 2018/9/14 * @github https://github.com/macrozheng */ @Configuration public class RabbitMqConfig { /** * 订单消息实际消费队列所绑定的交换机 */ @Bean DirectExchange orderDirect() { return ExchangeBuilder .directExchange(QueueEnum.QUEUE_ORDER_CANCEL.getExchange()) .durable(true) .build(); } /** * 订单延迟队列所绑定的交换机 */ @Bean DirectExchange orderTtlDirect() { return ExchangeBuilder .directExchange(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange()) .durable(true) .build(); } /** * 订单实际消费队列 */ @Bean public Queue orderQueue() { return new Queue(QueueEnum.QUEUE_ORDER_CANCEL.getName()); } /** * 订单延迟队列(死信队列) */ @Bean public Queue orderTtlQueue() { return QueueBuilder .durable(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getName()) .withArgument("x-dead-letter-exchange", QueueEnum.QUEUE_ORDER_CANCEL.getExchange())//到期后转发的交换机 .withArgument("x-dead-letter-routing-key", QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey())//到期后转发的路由键 .build(); } /** * 将订单队列绑定到交换机 */ @Bean Binding orderBinding(DirectExchange orderDirect,Queue orderQueue){ return BindingBuilder .bind(orderQueue) .to(orderDirect) .with(QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey()); } /** * 将订单延迟队列绑定到交换机 */ @Bean Binding orderTtlBinding(DirectExchange orderTtlDirect,Queue orderTtlQueue){ return BindingBuilder .bind(orderTtlQueue) .to(orderTtlDirect) .with(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey()); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/config/RedisConfig.java ================================================ package com.macro.mall.tiny.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /** * @auther macrozheng * @description Redis配置类 * @date 2020/3/2 * @github https://github.com/macrozheng */ @EnableCaching @Configuration public class RedisConfig { /** * redis数据库自定义key */ public static final String REDIS_KEY_DATABASE="mall"; @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisSerializer serializer = redisSerializer(); RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(serializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(serializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public RedisSerializer redisSerializer() { //创建JSON序列化器 Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //必须设置,否则无法将JSON转化为对象,会转化成Map类型 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(objectMapper); return serializer; } @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); //设置Redis缓存有效期为1天 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1)); return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/config/SecurityConfig.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.domain.AdminUserDetails; 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.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 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.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * @auther macrozheng * @description SpringSecurity的配置 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) public class SecurityConfig { @Autowired private RestfulAccessDeniedHandler restfulAccessDeniedHandler; @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Bean SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity .authorizeRequests(); //不需要保护的资源路径允许访问 for (String url : ignoreUrlsConfig.getUrls()) { registry.antMatchers(url).permitAll(); } //允许跨域请求的OPTIONS请求 registry.antMatchers(HttpMethod.OPTIONS) .permitAll(); httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf .disable() .sessionManagement()// 基于token,所以不需要session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .anyRequest()// 除上面外的所有请求全部需要鉴权认证 .authenticated(); // 禁用缓存 httpSecurity.headers().cacheControl(); // 添加JWT filter httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); //添加自定义未授权和未登录结果返回 httpSecurity.exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler) .authenticationEntryPoint(restAuthenticationEntryPoint); return httpSecurity.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){ return new JwtAuthenticationTokenFilter(); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger文档的配置(带认证) * @date 2022/11/22 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .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(new Contact("macro", null, null)) .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; } @Bean public BeanPostProcessor generateBeanPostProcessor(){ return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/controller/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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 搜索商品管理Controller * @date 2018/6/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "EsProductController") @Tag(name = "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 delete(@PathVariable Long id) { esProductService.delete(id); return CommonResult.success(null); } @ApiOperation(value = "根据id批量删除商品") @RequestMapping(value = "/delete/batch", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("ids") List ids) { esProductService.delete(ids); return CommonResult.success(null); } @ApiOperation(value = "根据id创建商品") @RequestMapping(value = "/create/{id}", method = RequestMethod.POST) @ResponseBody public CommonResult create(@PathVariable Long id) { EsProduct esProduct = esProductService.create(id); if (esProduct != null) { return CommonResult.success(esProduct); } else { return CommonResult.failed(); } } @ApiOperation(value = "简单搜索") @RequestMapping(value = "/search/simple", method = RequestMethod.GET) @ResponseBody public CommonResult> search(@RequestParam(required = false) String keyword, @RequestParam(required = false, defaultValue = "0") Integer pageNum, @RequestParam(required = false, defaultValue = "5") Integer pageSize) { Page esProductPage = esProductService.search(keyword, pageNum, pageSize); return CommonResult.success(CommonPage.restPage(esProductPage)); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/controller/MemberReadHistoryController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import com.macro.mall.tiny.service.MemberReadHistoryService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 会员商品浏览记录管理Controller * @date 2018/8/3 * @github https://github.com/macrozheng */ @Controller @Api(tags = "MemberReadHistoryController") @Tag(name = "MemberReadHistoryController", description = "会员商品浏览记录管理") @RequestMapping("/member/readHistory") public class MemberReadHistoryController { @Autowired private MemberReadHistoryService memberReadHistoryService; @ApiOperation("创建浏览记录") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody public CommonResult create(@RequestBody MemberReadHistory memberReadHistory) { int count = memberReadHistoryService.create(memberReadHistory); if (count > 0) { return CommonResult.success(count); } else { return CommonResult.failed(); } } @ApiOperation("删除浏览记录") @RequestMapping(value = "/delete", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("ids") List ids) { int count = memberReadHistoryService.delete(ids); if (count > 0) { return CommonResult.success(count); } else { return CommonResult.failed(); } } @ApiOperation("展示浏览记录") @RequestMapping(value = "/list", method = RequestMethod.GET) @ResponseBody public CommonResult> list(Long memberId) { List memberReadHistoryList = memberReadHistoryService.list(memberId); return CommonResult.success(memberReadHistoryList); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/controller/MinioController.java ================================================ package com.macro.mall.tiny.controller; import cn.hutool.core.collection.CollUtil; import cn.hutool.json.JSONUtil; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.dto.BucketPolicyConfigDto; import com.macro.mall.tiny.dto.MinioUploadDto; import io.minio.*; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.text.SimpleDateFormat; import java.util.Date; /** * @auther macrozheng * @description MinIO对象存储管理Controller * @date 2019/12/25 * @github https://github.com/macrozheng */ @Controller @Api(tags = "MinioController") @Tag(name = "MinioController", description = "MinIO对象存储管理") @RequestMapping("/minio") public class MinioController { private static final Logger LOGGER = LoggerFactory.getLogger(MinioController.class); @Value("${minio.endpoint}") private String ENDPOINT; @Value("${minio.bucketName}") private String BUCKET_NAME; @Value("${minio.accessKey}") private String ACCESS_KEY; @Value("${minio.secretKey}") private String SECRET_KEY; @ApiOperation("文件上传") @RequestMapping(value = "/upload", method = RequestMethod.POST) @ResponseBody public CommonResult upload(@RequestPart("file") MultipartFile file) { try { //创建一个MinIO的Java客户端 MinioClient minioClient =MinioClient.builder() .endpoint(ENDPOINT) .credentials(ACCESS_KEY,SECRET_KEY) .build(); boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET_NAME).build()); if (isExist) { LOGGER.info("存储桶已经存在!"); } else { //创建存储桶并设置只读权限 minioClient.makeBucket(MakeBucketArgs.builder().bucket(BUCKET_NAME).build()); BucketPolicyConfigDto bucketPolicyConfigDto = createBucketPolicyConfigDto(BUCKET_NAME); SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder() .bucket(BUCKET_NAME) .config(JSONUtil.toJsonStr(bucketPolicyConfigDto)) .build(); minioClient.setBucketPolicy(setBucketPolicyArgs); } String filename = file.getOriginalFilename(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); // 设置存储对象名称 String objectName = sdf.format(new Date()) + "/" + filename; // 使用putObject上传一个文件到存储桶中 PutObjectArgs putObjectArgs = PutObjectArgs.builder() .bucket(BUCKET_NAME) .object(objectName) .contentType(file.getContentType()) .stream(file.getInputStream(), file.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE).build(); minioClient.putObject(putObjectArgs); LOGGER.info("文件上传成功!"); MinioUploadDto minioUploadDto = new MinioUploadDto(); minioUploadDto.setName(filename); minioUploadDto.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName); return CommonResult.success(minioUploadDto); } catch (Exception e) { e.printStackTrace(); LOGGER.info("上传发生错误: {}!", e.getMessage()); } return CommonResult.failed(); } /** * 创建存储桶的访问策略,设置为只读权限 */ private BucketPolicyConfigDto createBucketPolicyConfigDto(String bucketName) { BucketPolicyConfigDto.Statement statement = BucketPolicyConfigDto.Statement.builder() .Effect("Allow") .Principal("*") .Action("s3:GetObject") .Resource("arn:aws:s3:::"+bucketName+"/*.**").build(); return BucketPolicyConfigDto.builder() .Version("2012-10-17") .Statement(CollUtil.toList(statement)) .build(); } @ApiOperation("文件删除") @RequestMapping(value = "/delete", method = RequestMethod.POST) @ResponseBody public CommonResult delete(@RequestParam("objectName") String objectName) { try { MinioClient minioClient = MinioClient.builder() .endpoint(ENDPOINT) .credentials(ACCESS_KEY,SECRET_KEY) .build(); minioClient.removeObject(RemoveObjectArgs.builder().bucket(BUCKET_NAME).object(objectName).build()); return CommonResult.success(null); } catch (Exception e) { e.printStackTrace(); } return CommonResult.failed(); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/controller/OmsPortalOrderController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.dto.OrderParam; import com.macro.mall.tiny.service.OmsPortalOrderService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; /** * @auther macrozheng * @description 订单管理Controller * @date 2018/8/30 * @github https://github.com/macrozheng */ @Controller @Api(tags = "OmsPortalOrderController") @Tag(name = "OmsPortalOrderController", description = "订单管理") @RequestMapping("/order") public class OmsPortalOrderController { @Autowired private OmsPortalOrderService portalOrderService; @ApiOperation("根据购物车信息生成订单") @RequestMapping(value = "/generateOrder", method = RequestMethod.POST) @ResponseBody public Object generateOrder(@RequestBody OrderParam orderParam) { return portalOrderService.generateOrder(orderParam); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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 @PreAuthorize("hasAuthority('brand:listAll')") public CommonResult> getBrandList() { return CommonResult.success(brandService.listAllBrand()); } @ApiOperation("添加品牌") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody @PreAuthorize("hasAuthority('brand:create')") 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 @PreAuthorize("hasAuthority('brand:update')") 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 @PreAuthorize("hasAuthority('brand:delete')") 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 @PreAuthorize("hasAuthority('brand:list')") 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)); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/controller/UmsAdminController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.domain.UmsResource; import com.macro.mall.tiny.service.UmsAdminService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; 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; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @auther macrozheng * @description 后台用户管理 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsAdminController") @Tag(name = "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 = "登录以后返回token") @RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public CommonResult login(@RequestParam String username, @RequestParam String password) { String token = adminService.login(username, password); if (token == null) { return CommonResult.validateFailed("用户名或密码错误"); } Map tokenMap = new HashMap<>(); tokenMap.put("token", token); tokenMap.put("tokenHead", tokenHead); return CommonResult.success(tokenMap); } @ApiOperation(value = "登录以后返回token") @RequestMapping(value = "/resourceList", method = RequestMethod.POST) @ResponseBody public CommonResult> resourceList() { List resourceList = adminService.getResourceList(); return CommonResult.success(resourceList); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/controller/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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 会员登录注册管理Controller * @date 2018/8/3 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsMemberController") @Tag(name = "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); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/dao/EsProductDao.java ================================================ package com.macro.mall.tiny.dao; import com.macro.mall.tiny.nosql.elasticsearch.document.EsProduct; import org.apache.ibatis.annotations.Param; import java.util.List; /** * @auther macrozheng * @description 搜索系统中的商品管理自定义Dao * @date 2018/6/19 * @github https://github.com/macrozheng */ public interface EsProductDao { List getAllEsProductList(@Param("id") Long id); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/domain/AdminUserDetails.java ================================================ package com.macro.mall.tiny.domain; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; 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; /** * @auther macrozheng * @description SpringSecurity用户信息封装类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode(callSuper = false) @Builder public class AdminUserDetails implements UserDetails { private String username; private String password; private List authorityList; @Override public Collection getAuthorities() { return this.authorityList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/domain/UmsResource.java ================================================ package com.macro.mall.tiny.domain; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.Date; /** *

* 后台资源表 *

* * @author macro * @since 2020-08-21 */ @Data @EqualsAndHashCode(callSuper = false) @ApiModel(value="UmsResource对象", description="后台资源表") @Builder public class UmsResource{ private Long id; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "资源名称") private String name; @ApiModelProperty(value = "资源URL") private String url; @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "资源分类ID") private Long categoryId; } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/dto/BucketPolicyConfigDto.java ================================================ package com.macro.mall.tiny.dto; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.List; /** * @auther macrozheng * @description Minio Bucket访问策略配置 * @date 2020/8/11 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode @Builder public class BucketPolicyConfigDto { private String Version; private List Statement; @Data @EqualsAndHashCode @Builder public static class Statement { private String Effect; private String Principal; private String Action; private String Resource; } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/dto/MinioUploadDto.java ================================================ package com.macro.mall.tiny.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; /** * @auther macrozheng * @description 文件上传返回结果 * @date 2019/12/25 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode public class MinioUploadDto { @ApiModelProperty("文件访问URL") private String url; @ApiModelProperty("文件名称") private String name; } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/dto/OrderParam.java ================================================ package com.macro.mall.tiny.dto; import lombok.Data; /** * @auther macrozheng * @description 生成订单时传入的参数 * @date 2018/8/30 * @github https://github.com/macrozheng */ @Data public class OrderParam { //收货地址id private Long memberReceiveAddressId; //优惠券id private Long couponId; //使用的积分数 private Integer useIntegration; //支付方式 private Integer payType; } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/dto/QueueEnum.java ================================================ package com.macro.mall.tiny.dto; import lombok.Getter; /** * @auther macrozheng * @description 消息队列枚举配置 * @date 2018/9/14 * @github https://github.com/macrozheng */ @Getter public enum QueueEnum { /** * 消息通知队列 */ QUEUE_ORDER_CANCEL("mall.order.direct", "mall.order.cancel", "mall.order.cancel"), /** * 消息通知ttl队列 */ QUEUE_TTL_ORDER_CANCEL("mall.order.direct.ttl", "mall.order.cancel.ttl", "mall.order.cancel.ttl"); /** * 交换机名称 */ private String exchange; /** * 队列名称 */ private String name; /** * 路由键 */ private String routeKey; QueueEnum(String exchange, String name, String routeKey) { this.exchange = exchange; this.name = name; this.routeKey = routeKey; } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { long countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand row); int insertSelective(PmsBrand row); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExample(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand row); int updateByPrimaryKeyWithBLOBs(PmsBrand row); int updateByPrimaryKey(PmsBrand row); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { private Long id; private String name; @ApiModelProperty(value = "首字母") private String firstLetter; private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/document/EsProduct.java ================================================ package com.macro.mall.tiny.nosql.elasticsearch.document; import lombok.Data; import lombok.EqualsAndHashCode; 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 org.springframework.data.elasticsearch.annotations.Setting; import java.io.Serializable; import java.math.BigDecimal; import java.util.List; /** * @auther macrozheng * @description 搜索商品的信息 * @date 2018/6/19 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode @Document(indexName = "pms") @Setting(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; } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/document/EsProductAttributeValue.java ================================================ package com.macro.mall.tiny.nosql.elasticsearch.document; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; import java.io.Serializable; /** * @auther macrozheng * @description 搜索商品的属性信息 * @date 2018/6/27 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode public class EsProductAttributeValue implements Serializable { private static final long serialVersionUID = 1L; private Long id; private Long productAttributeId; //属性值 @Field(type = FieldType.Keyword) private String value; //属性参数:0->规格;1->参数 private Integer type; //属性名称 @Field(type=FieldType.Keyword) private String name; } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/nosql/elasticsearch/repository/EsProductRepository.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; /** * @auther macrozheng * @description 商品ES操作类 * @date 2018/6/19 * @github https://github.com/macrozheng */ public interface EsProductRepository extends ElasticsearchRepository { /** * 搜索查询 * * @param name 商品名称 * @param subTitle 商品标题 * @param keywords 商品关键字 * @param page 分页信息 * @return */ Page findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/nosql/mongodb/document/MemberReadHistory.java ================================================ package com.macro.mall.tiny.nosql.mongodb.document; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; /** * @auther macrozheng * @description 用户商品浏览历史记录 * @date 2018/8/3 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode @Document public class MemberReadHistory { @Id private String id; @Indexed private Long memberId; private String memberNickname; private String memberIcon; @Indexed private Long productId; private String productName; private String productPic; private String productSubTitle; private String productPrice; private Date createTime; } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/nosql/mongodb/repository/MemberReadHistoryRepository.java ================================================ package com.macro.mall.tiny.nosql.mongodb.repository; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import org.springframework.data.mongodb.repository.MongoRepository; import java.util.List; /** * @auther macrozheng * @description 会员商品浏览历史Repository * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface MemberReadHistoryRepository extends MongoRepository { /** * 根据会员id按时间倒序获取浏览记录 * @param memberId 会员id */ List findByMemberIdOrderByCreateTimeDesc(Long memberId); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/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; /** * @auther macrozheng * @description 商品搜索管理Service * @date 2018/6/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/MemberReadHistoryService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import java.util.List; /** * @auther macrozheng * @description 会员浏览记录管理Service * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface MemberReadHistoryService { /** * 生成浏览记录 */ int create(MemberReadHistory memberReadHistory); /** * 批量删除浏览记录 */ int delete(List ids); /** * 获取用户浏览历史记录 */ List list(Long memberId); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/OmsPortalOrderService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.dto.OrderParam; import org.springframework.transaction.annotation.Transactional; /** * @auther macrozheng * @description 前台订单管理Service * @date 2018/8/30 * @github https://github.com/macrozheng */ public interface OmsPortalOrderService { /** * 根据提交信息生成订单 */ @Transactional CommonResult generateOrder(OrderParam orderParam); /** * 取消单个超时订单 */ @Transactional void cancelOrder(Long orderId); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/RedisService.java ================================================ package com.macro.mall.tiny.service; import java.util.List; import java.util.Map; import java.util.Set; /** * @auther macrozheng * @description redis操作Service * @date 2020/3/3 * @github https://github.com/macrozheng */ public interface RedisService { /** * 保存属性 */ void set(String key, Object value, long time); /** * 保存属性 */ void set(String key, Object value); /** * 获取属性 */ Object get(String key); /** * 删除属性 */ Boolean del(String key); /** * 批量删除属性 */ Long del(List keys); /** * 设置过期时间 */ Boolean expire(String key, long time); /** * 获取过期时间 */ Long getExpire(String key); /** * 判断是否有该属性 */ Boolean hasKey(String key); /** * 按delta递增 */ Long incr(String key, long delta); /** * 按delta递减 */ Long decr(String key, long delta); /** * 获取Hash结构中的属性 */ Object hGet(String key, String hashKey); /** * 向Hash结构中放入一个属性 */ Boolean hSet(String key, String hashKey, Object value, long time); /** * 向Hash结构中放入一个属性 */ void hSet(String key, String hashKey, Object value); /** * 直接获取整个Hash结构 */ Map hGetAll(String key); /** * 直接设置整个Hash结构 */ Boolean hSetAll(String key, Map map, long time); /** * 直接设置整个Hash结构 */ void hSetAll(String key, Map map); /** * 删除Hash结构中的属性 */ void hDel(String key, Object... hashKey); /** * 判断Hash结构中是否有该属性 */ Boolean hHasKey(String key, String hashKey); /** * Hash结构中属性递增 */ Long hIncr(String key, String hashKey, Long delta); /** * Hash结构中属性递减 */ Long hDecr(String key, String hashKey, Long delta); /** * 获取Set结构 */ Set sMembers(String key); /** * 向Set结构中添加属性 */ Long sAdd(String key, Object... values); /** * 向Set结构中添加属性 */ Long sAdd(String key, long time, Object... values); /** * 是否为Set中的属性 */ Boolean sIsMember(String key, Object value); /** * 获取Set结构的长度 */ Long sSize(String key); /** * 删除Set结构中的属性 */ Long sRemove(String key, Object... values); /** * 获取List结构中的属性 */ List lRange(String key, long start, long end); /** * 获取List结构的长度 */ Long lSize(String key); /** * 根据索引获取List中的属性 */ Object lIndex(String key, long index); /** * 向List结构中添加属性 */ Long lPush(String key, Object value); /** * 向List结构中添加属性 */ Long lPush(String key, Object value, long time); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Object... values); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Long time, Object... values); /** * 从List结构中移除属性 */ Long lRemove(String key, long count, Object value); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/UmsAdminService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.domain.UmsResource; import java.util.List; /** * @auther macrozheng * @description 后台用户管理Service * @date 2020/10/15 * @github https://github.com/macrozheng */ public interface UmsAdminService { /** * 根据用户名获取用户信息 */ AdminUserDetails getAdminByUsername(String username); /** * 获取所以权限列表 */ List getResourceList(); /** * 用户名密码登录 */ String login(String username, String password); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/UmsMemberService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.common.api.CommonResult; /** * @auther macrozheng * @description 会员管理Service * @date 2018/8/3 * @github https://github.com/macrozheng */ public interface UmsMemberService { /** * 生成验证码 */ CommonResult generateAuthCode(String telephone); /** * 判断验证码和手机号码是否匹配 */ CommonResult verifyAuthCode(String telephone, String authCode); } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/impl/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; /** * @auther macrozheng * @description 搜索商品管理Service实现类 * @date 2018/6/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/impl/MemberReadHistoryServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import com.macro.mall.tiny.nosql.mongodb.document.MemberReadHistory; import com.macro.mall.tiny.nosql.mongodb.repository.MemberReadHistoryRepository; import com.macro.mall.tiny.service.MemberReadHistoryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @auther macrozheng * @description 会员浏览记录管理Service实现类 * @date 2018/8/3 * @github https://github.com/macrozheng */ @Service public class MemberReadHistoryServiceImpl implements MemberReadHistoryService { @Autowired private MemberReadHistoryRepository memberReadHistoryRepository; @Override public int create(MemberReadHistory memberReadHistory) { memberReadHistory.setId(null); memberReadHistory.setCreateTime(new Date()); memberReadHistoryRepository.save(memberReadHistory); return 1; } @Override public int delete(List ids) { List deleteList = new ArrayList<>(); for(String id:ids){ MemberReadHistory memberReadHistory = new MemberReadHistory(); memberReadHistory.setId(id); deleteList.add(memberReadHistory); } memberReadHistoryRepository.deleteAll(deleteList); return ids.size(); } @Override public List list(Long memberId) { return memberReadHistoryRepository.findByMemberIdOrderByCreateTimeDesc(memberId); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/impl/OmsPortalOrderServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.component.CancelOrderSender; import com.macro.mall.tiny.dto.OrderParam; import com.macro.mall.tiny.service.OmsPortalOrderService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @auther macrozheng * @description 前台订单管理Service * @date 2018/8/30 * @github https://github.com/macrozheng */ @Service public class OmsPortalOrderServiceImpl implements OmsPortalOrderService { private static Logger LOGGER = LoggerFactory.getLogger(OmsPortalOrderServiceImpl.class); @Autowired private CancelOrderSender cancelOrderSender; @Override public CommonResult generateOrder(OrderParam orderParam) { //todo 执行一系类下单操作,具体参考mall项目 LOGGER.info("process generateOrder"); //下单完成后开启一个延迟消息,用于当用户没有付款时取消订单(orderId应该在下单后生成) sendDelayMessageCancelOrder(11L); return CommonResult.success(null, "下单成功"); } @Override public void cancelOrder(Long orderId) { //todo 执行一系类取消订单操作,具体参考mall项目 LOGGER.info("process cancelOrder orderId:{}",orderId); } private void sendDelayMessageCancelOrder(Long orderId) { //获取订单超时时间,假设为60分钟(测试用的30秒) long delayTimes = 30 * 1000; //发送延迟消息 cancelOrderSender.sendMessage(orderId, delayTimes); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/impl/RedisServiceImpl.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.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @auther macrozheng * @description redis操作实现类 * @date 2020/3/3 * @github https://github.com/macrozheng */ @Service public class RedisServiceImpl implements RedisService { @Autowired private RedisTemplate redisTemplate; @Override public void set(String key, Object value, long time) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } @Override public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); } @Override public Object get(String key) { return redisTemplate.opsForValue().get(key); } @Override public Boolean del(String key) { return redisTemplate.delete(key); } @Override public Long del(List keys) { return redisTemplate.delete(keys); } @Override public Boolean expire(String key, long time) { return redisTemplate.expire(key, time, TimeUnit.SECONDS); } @Override public Long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } @Override public Boolean hasKey(String key) { return redisTemplate.hasKey(key); } @Override public Long incr(String key, long delta) { return redisTemplate.opsForValue().increment(key, delta); } @Override public Long decr(String key, long delta) { return redisTemplate.opsForValue().increment(key, -delta); } @Override public Object hGet(String key, String hashKey) { return redisTemplate.opsForHash().get(key, hashKey); } @Override public Boolean hSet(String key, String hashKey, Object value, long time) { redisTemplate.opsForHash().put(key, hashKey, value); return expire(key, time); } @Override public void hSet(String key, String hashKey, Object value) { redisTemplate.opsForHash().put(key, hashKey, value); } @Override public Map hGetAll(String key) { return redisTemplate.opsForHash().entries(key); } @Override public Boolean hSetAll(String key, Map map, long time) { redisTemplate.opsForHash().putAll(key, map); return expire(key, time); } @Override public void hSetAll(String key, Map map) { redisTemplate.opsForHash().putAll(key, map); } @Override public void hDel(String key, Object... hashKey) { redisTemplate.opsForHash().delete(key, hashKey); } @Override public Boolean hHasKey(String key, String hashKey) { return redisTemplate.opsForHash().hasKey(key, hashKey); } @Override public Long hIncr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, delta); } @Override public Long hDecr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, -delta); } @Override public Set sMembers(String key) { return redisTemplate.opsForSet().members(key); } @Override public Long sAdd(String key, Object... values) { return redisTemplate.opsForSet().add(key, values); } @Override public Long sAdd(String key, long time, Object... values) { Long count = redisTemplate.opsForSet().add(key, values); expire(key, time); return count; } @Override public Boolean sIsMember(String key, Object value) { return redisTemplate.opsForSet().isMember(key, value); } @Override public Long sSize(String key) { return redisTemplate.opsForSet().size(key); } @Override public Long sRemove(String key, Object... values) { return redisTemplate.opsForSet().remove(key, values); } @Override public List lRange(String key, long start, long end) { return redisTemplate.opsForList().range(key, start, end); } @Override public Long lSize(String key) { return redisTemplate.opsForList().size(key); } @Override public Object lIndex(String key, long index) { return redisTemplate.opsForList().index(key, index); } @Override public Long lPush(String key, Object value) { return redisTemplate.opsForList().rightPush(key, value); } @Override public Long lPush(String key, Object value, long time) { Long index = redisTemplate.opsForList().rightPush(key, value); expire(key, time); return index; } @Override public Long lPushAll(String key, Object... values) { return redisTemplate.opsForList().rightPushAll(key, values); } @Override public Long lPushAll(String key, Long time, Object... values) { Long count = redisTemplate.opsForList().rightPushAll(key, values); expire(key, time); return count; } @Override public Long lRemove(String key, long count, Object value) { return redisTemplate.opsForList().remove(key, count, value); } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/impl/UmsAdminServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.collection.CollUtil; import com.macro.mall.tiny.common.utils.JwtTokenUtil; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.domain.UmsResource; import com.macro.mall.tiny.service.UmsAdminService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; 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.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description 后台用户管理Service实现类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Slf4j @Service public class UmsAdminServiceImpl implements UmsAdminService { /** * 存放默认用户信息 */ private List adminUserDetailsList = new ArrayList<>(); /** * 存放默认资源信息 */ private List resourceList = new ArrayList<>(); @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private PasswordEncoder passwordEncoder; @PostConstruct private void init(){ adminUserDetailsList.add(AdminUserDetails.builder() .username("admin") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("brand:create","brand:update","brand:delete","brand:list","brand:listAll")) .build()); adminUserDetailsList.add(AdminUserDetails.builder() .username("macro") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("brand:listAll")) .build()); resourceList.add(UmsResource.builder() .id(1L) .name("brand:create") .url("/brand/create") .build()); resourceList.add(UmsResource.builder() .id(2L) .name("brand:update") .url("/brand/update/**") .build()); resourceList.add(UmsResource.builder() .id(3L) .name("brand:delete") .url("/brand/delete/**") .build()); resourceList.add(UmsResource.builder() .id(4L) .name("brand:list") .url("/brand/list") .build()); resourceList.add(UmsResource.builder() .id(5L) .name("brand:listAll") .url("/brand/listAll") .build()); } @Override public AdminUserDetails getAdminByUsername(String username) { List findList = adminUserDetailsList.stream().filter(item -> item.getUsername().equals(username)).collect(Collectors.toList()); if(CollUtil.isNotEmpty(findList)){ return findList.get(0); } return null; } @Override public List getResourceList() { return resourceList; } @Override public String login(String username, String password) { String token = null; try { UserDetails userDetails = getAdminByUsername(username); if(userDetails==null){ return token; } 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) { log.warn("登录异常:{}", e.getMessage()); } return token; } } ================================================ FILE: mall-tiny-08/src/main/java/com/macro/mall/tiny/service/impl/UmsMemberServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.util.StrUtil; 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; /** * @auther macrozheng * @description 会员管理Service实现类 * @date 2018/8/3 * @github https://github.com/macrozheng */ @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 (StrUtil.isEmpty(authCode)) { return CommonResult.failed("请输入验证码"); } String realAuthCode = (String) redisService.get(REDIS_KEY_PREFIX_AUTH_CODE + telephone); boolean result = authCode.equals(realAuthCode); if (result) { return CommonResult.success(null, "验证码校验成功"); } else { return CommonResult.failed("验证码不正确"); } } } ================================================ FILE: mall-tiny-08/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root servlet: multipart: enabled: true #开 启文件上传 max-file-size: 10MB # 限制文件上传大小为10M mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER redis: host: localhost # Redis服务器地址 database: 0 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 password: # Redis服务器连接密码(默认为空) lettuce: pool: max-active: 8 # 连接池最大连接数 max-idle: 8 # 连接池最大空闲连接数 min-idle: 0 # 连接池最小空闲连接数 max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制 data: elasticsearch: repositories: enabled: true # 开启ES仓库配置,自动为仓库接口生成实现类 mongodb: host: localhost # MongoDB的连接地址 port: 27017 # MongoDB的连接端口号 database: mall-port # MongoDB的连接的数据库 elasticsearch: uris: http://localhost:9200 # ES的连接地址及端口号 rabbitmq: host: localhost port: 5672 virtual-host: /mall username: mall password: mall publisher-returns: true # 消息发送到队列确认 publisher-confirm-type: simple # 消息发送到交换器确认 mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml minio: endpoint: http://localhost:9000 # MinIO服务所在地址 bucketName: mall # 存储桶名称 accessKey: minioadmin # 访问的key secretKey: minioadmin # 访问的秘钥 # 自定义redis key redis: key: prefix: authCode: "portal:authCode:" expire: authCode: 120 # 验证码超期时间 # 自定义jwt key jwt: tokenHeader: Authorization # JWT存储的请求头 secret: mySecret # JWT加解密使用的密钥 expiration: 604800 # JWT的超期限时间(60*60*24) tokenHead: Bearer # JWT负载中拿到开头 secure: ignored: urls: # 安全路径白名单 - /swagger-ui/ - /swagger-resources/** - /**/v2/api-docs - /**/*.html - /**/*.js - /**/*.css - /**/*.png - /favicon.ico - /actuator/** - /druid/** - /admin/** - /esProduct/** - /member/readHistory/** - /order/** - /minio/** ================================================ FILE: mall-tiny-08/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-08/src/main/resources/dao/EsProductDao.xml ================================================ ================================================ FILE: mall-tiny-08/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-08/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-08/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-alipay/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-alipay/pom.xml ================================================ 4.0.0 mall-tiny-alipay 1.0-SNAPSHOT mall-tiny-alipay Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-configuration-processor true com.alipay.sdk alipay-sdk-java 4.38.72.ALL org.springframework.boot spring-boot-maven-plugin io.fabric8 docker-maven-plugin ${docker.maven.plugin.version} build-image package build ${docker.host} mall-tiny/${project.name}:${project.version} openjdk:8 ${project.build.finalName}.jar / artifact ["java", "-jar","-Dspring.profiles.active=prod","/${project.build.finalName}.jar"] macrozheng ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/config/AlipayClientConfig.java ================================================ package com.macro.mall.tiny.config; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description 支付宝请求客户端配置 * @date 2023/9/8 * @github https://github.com/macrozheng */ @Configuration public class AlipayClientConfig { @Bean public AlipayClient alipayClient(AlipayConfig config){ return new DefaultAlipayClient(config.getGatewayUrl(),config.getAppId(),config.getAppPrivateKey(), config.getFormat(),config.getCharset(),config.getAlipayPublicKey(),config.getSignType()); } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/config/AlipayConfig.java ================================================ package com.macro.mall.tiny.config; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; /** * @auther macrozheng * @description 支付宝支付相关配置 * @date 2023/9/8 * @github https://github.com/macrozheng */ @Getter @Setter @Component @ConfigurationProperties(prefix = "alipay") public class AlipayConfig { /** * 支付宝网关 */ private String gatewayUrl; /** * 支付宝分配给开发者的应用ID */ private String appId; /** * 开发者私钥,由开发者自己生成 */ private String appPrivateKey; /** * 支付宝公钥,由支付宝生成。 */ private String alipayPublicKey; /** * 用户确认支付后,支付宝调用的页面返回路径 */ private String returnUrl; /** * 支付宝服务器主动通知商户服务器里的异步通知回调(需要公网能访问) */ private String notifyUrl; /** * 参数返回格式,只支持JSON */ private String format = "JSON"; /** * 请求使用的编码格式 */ private String charset = "UTF-8"; /** * 生成签名字符串所使用的签名算法类型 */ private String signType = "RSA2"; } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan("com.macro.mall.tiny.mbg.mapper") public class MyBatisConfig { } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger相关配置 * @date 2022/11/23 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("SwaggerUI演示") .description("mall-tiny") .contact(new Contact("macro", null, null)) .version("1.0") .build(); } @Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/controller/AlipayController.java ================================================ package com.macro.mall.tiny.controller; import cn.hutool.http.HttpUtil; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.config.AlipayConfig; import com.macro.mall.tiny.dto.AliPayParam; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.service.AlipayService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; 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.ResponseBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @auther macrozheng * @description 支付宝支付Controller * @date 2023/9/8 * @github https://github.com/macrozheng */ @Controller @Api(tags = "AlipayController") @Tag(name = "AlipayController", description = "支付宝支付相关接口") @RequestMapping("/alipay") public class AlipayController { @Autowired private AlipayConfig alipayConfig; @Autowired private AlipayService alipayService; @ApiOperation("支付宝电脑网站支付") @RequestMapping(value = "/pay", method = RequestMethod.GET) public void pay(AliPayParam aliPayParam, HttpServletResponse response) throws IOException { response.setContentType("text/html;charset=" + alipayConfig.getCharset()); response.getWriter().write(alipayService.pay(aliPayParam)); response.getWriter().flush(); response.getWriter().close(); } @ApiOperation("支付宝手机网站支付") @RequestMapping(value = "/webPay", method = RequestMethod.GET) public void webPay(AliPayParam aliPayParam, HttpServletResponse response) throws IOException { response.setContentType("text/html;charset=" + alipayConfig.getCharset()); response.getWriter().write(alipayService.webPay(aliPayParam)); response.getWriter().flush(); response.getWriter().close(); } @ApiOperation(value = "支付宝异步回调",notes = "必须为POST请求,执行成功返回success,执行失败返回failure") @RequestMapping(value = "/notify", method = RequestMethod.POST) public String notify(HttpServletRequest request){ Map params = new HashMap<>(); Map requestParams = request.getParameterMap(); for (String name : requestParams.keySet()) { params.put(name, request.getParameter(name)); } return alipayService.notify(params); } @ApiOperation(value = "支付宝统一收单线下交易查询",notes = "订单支付成功返回:TRADE_SUCCESS") @RequestMapping(value = "/query", method = RequestMethod.GET) @ResponseBody public CommonResult query(String outTradeNo,String tradeNo){ return CommonResult.success(alipayService.query(outTradeNo,tradeNo)); } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/controller/AlipayOrderController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.mbg.model.AlipayOrder; import com.macro.mall.tiny.service.AlipayOrderService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import java.text.SimpleDateFormat; import java.util.Date; /** * @auther macrozheng * @description 支付宝订单管理Controller * @date 2023/9/8 * @github https://github.com/macrozheng */ @Controller @Api(tags = "AlipayOrderController") @Tag(name = "AlipayOrderController", description = "支付宝订单管理") @RequestMapping("/alipayOrder") public class AlipayOrderController { @Autowired private AlipayOrderService alipayOrderService; @ApiOperation("创建订单") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody public CommonResult create() { AlipayOrder order = alipayOrderService.create(); return CommonResult.success(order); } @ApiOperation("查询订单") @RequestMapping(value = "/info/{orderId}", method = RequestMethod.GET) @ResponseBody public CommonResult info(@PathVariable String orderId) { AlipayOrder order = alipayOrderService.info(orderId); return CommonResult.success(order); } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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)); } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/dto/AliPayParam.java ================================================ package com.macro.mall.tiny.dto; import lombok.Data; import java.math.BigDecimal; /** * @auther macrozheng * @description 支付宝支付请求参数 * @date 2023/9/8 * @github https://github.com/macrozheng */ @Data public class AliPayParam { /** * 商户订单号,商家自定义,保持唯一性 */ private String outTradeNo; /** * 商品的标题/交易标题/订单标题/订单关键字等 */ private String subject; /** * 订单总金额,单位为元,精确到小数点后两位 */ private BigDecimal totalAmount; } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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 MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/mbg/mapper/AlipayOrderMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.AlipayOrder; import com.macro.mall.tiny.mbg.model.AlipayOrderExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface AlipayOrderMapper { long countByExample(AlipayOrderExample example); int deleteByExample(AlipayOrderExample example); int deleteByPrimaryKey(Long id); int insert(AlipayOrder row); int insertSelective(AlipayOrder row); List selectByExample(AlipayOrderExample example); AlipayOrder selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") AlipayOrder row, @Param("example") AlipayOrderExample example); int updateByExample(@Param("row") AlipayOrder row, @Param("example") AlipayOrderExample example); int updateByPrimaryKeySelective(AlipayOrder row); int updateByPrimaryKey(AlipayOrder row); } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { long countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand row); int insertSelective(PmsBrand row); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExample(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand row); int updateByPrimaryKeyWithBLOBs(PmsBrand row); int updateByPrimaryKey(PmsBrand row); } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/mbg/model/AlipayOrder.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; public class AlipayOrder implements Serializable { private Long id; @ApiModelProperty(value = "订单ID") private String orderId; @ApiModelProperty(value = "订单标题/商品标题/交易标题") private String subject; @ApiModelProperty(value = "订单总金额") private BigDecimal totalAmount; @ApiModelProperty(value = "交易状态") private String tradeStatus; @ApiModelProperty(value = "支付宝交易号") private String tradeNo; @ApiModelProperty(value = "买家支付宝账号") private String buyerId; @ApiModelProperty(value = "交易付款时间") private Date gmtPayment; @ApiModelProperty(value = "用户在交易中支付的金额") private BigDecimal buyerPayAmount; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public BigDecimal getTotalAmount() { return totalAmount; } public void setTotalAmount(BigDecimal totalAmount) { this.totalAmount = totalAmount; } public String getTradeStatus() { return tradeStatus; } public void setTradeStatus(String tradeStatus) { this.tradeStatus = tradeStatus; } public String getTradeNo() { return tradeNo; } public void setTradeNo(String tradeNo) { this.tradeNo = tradeNo; } public String getBuyerId() { return buyerId; } public void setBuyerId(String buyerId) { this.buyerId = buyerId; } public Date getGmtPayment() { return gmtPayment; } public void setGmtPayment(Date gmtPayment) { this.gmtPayment = gmtPayment; } public BigDecimal getBuyerPayAmount() { return buyerPayAmount; } public void setBuyerPayAmount(BigDecimal buyerPayAmount) { this.buyerPayAmount = buyerPayAmount; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", orderId=").append(orderId); sb.append(", subject=").append(subject); sb.append(", totalAmount=").append(totalAmount); sb.append(", tradeStatus=").append(tradeStatus); sb.append(", tradeNo=").append(tradeNo); sb.append(", buyerId=").append(buyerId); sb.append(", gmtPayment=").append(gmtPayment); sb.append(", buyerPayAmount=").append(buyerPayAmount); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/mbg/model/AlipayOrderExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.List; public class AlipayOrderExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public AlipayOrderExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andOrderIdIsNull() { addCriterion("order_id is null"); return (Criteria) this; } public Criteria andOrderIdIsNotNull() { addCriterion("order_id is not null"); return (Criteria) this; } public Criteria andOrderIdEqualTo(String value) { addCriterion("order_id =", value, "orderId"); return (Criteria) this; } public Criteria andOrderIdNotEqualTo(String value) { addCriterion("order_id <>", value, "orderId"); return (Criteria) this; } public Criteria andOrderIdGreaterThan(String value) { addCriterion("order_id >", value, "orderId"); return (Criteria) this; } public Criteria andOrderIdGreaterThanOrEqualTo(String value) { addCriterion("order_id >=", value, "orderId"); return (Criteria) this; } public Criteria andOrderIdLessThan(String value) { addCriterion("order_id <", value, "orderId"); return (Criteria) this; } public Criteria andOrderIdLessThanOrEqualTo(String value) { addCriterion("order_id <=", value, "orderId"); return (Criteria) this; } public Criteria andOrderIdLike(String value) { addCriterion("order_id like", value, "orderId"); return (Criteria) this; } public Criteria andOrderIdNotLike(String value) { addCriterion("order_id not like", value, "orderId"); return (Criteria) this; } public Criteria andOrderIdIn(List values) { addCriterion("order_id in", values, "orderId"); return (Criteria) this; } public Criteria andOrderIdNotIn(List values) { addCriterion("order_id not in", values, "orderId"); return (Criteria) this; } public Criteria andOrderIdBetween(String value1, String value2) { addCriterion("order_id between", value1, value2, "orderId"); return (Criteria) this; } public Criteria andOrderIdNotBetween(String value1, String value2) { addCriterion("order_id not between", value1, value2, "orderId"); return (Criteria) this; } public Criteria andSubjectIsNull() { addCriterion("subject is null"); return (Criteria) this; } public Criteria andSubjectIsNotNull() { addCriterion("subject is not null"); return (Criteria) this; } public Criteria andSubjectEqualTo(String value) { addCriterion("subject =", value, "subject"); return (Criteria) this; } public Criteria andSubjectNotEqualTo(String value) { addCriterion("subject <>", value, "subject"); return (Criteria) this; } public Criteria andSubjectGreaterThan(String value) { addCriterion("subject >", value, "subject"); return (Criteria) this; } public Criteria andSubjectGreaterThanOrEqualTo(String value) { addCriterion("subject >=", value, "subject"); return (Criteria) this; } public Criteria andSubjectLessThan(String value) { addCriterion("subject <", value, "subject"); return (Criteria) this; } public Criteria andSubjectLessThanOrEqualTo(String value) { addCriterion("subject <=", value, "subject"); return (Criteria) this; } public Criteria andSubjectLike(String value) { addCriterion("subject like", value, "subject"); return (Criteria) this; } public Criteria andSubjectNotLike(String value) { addCriterion("subject not like", value, "subject"); return (Criteria) this; } public Criteria andSubjectIn(List values) { addCriterion("subject in", values, "subject"); return (Criteria) this; } public Criteria andSubjectNotIn(List values) { addCriterion("subject not in", values, "subject"); return (Criteria) this; } public Criteria andSubjectBetween(String value1, String value2) { addCriterion("subject between", value1, value2, "subject"); return (Criteria) this; } public Criteria andSubjectNotBetween(String value1, String value2) { addCriterion("subject not between", value1, value2, "subject"); return (Criteria) this; } public Criteria andTotalAmountIsNull() { addCriterion("total_amount is null"); return (Criteria) this; } public Criteria andTotalAmountIsNotNull() { addCriterion("total_amount is not null"); return (Criteria) this; } public Criteria andTotalAmountEqualTo(BigDecimal value) { addCriterion("total_amount =", value, "totalAmount"); return (Criteria) this; } public Criteria andTotalAmountNotEqualTo(BigDecimal value) { addCriterion("total_amount <>", value, "totalAmount"); return (Criteria) this; } public Criteria andTotalAmountGreaterThan(BigDecimal value) { addCriterion("total_amount >", value, "totalAmount"); return (Criteria) this; } public Criteria andTotalAmountGreaterThanOrEqualTo(BigDecimal value) { addCriterion("total_amount >=", value, "totalAmount"); return (Criteria) this; } public Criteria andTotalAmountLessThan(BigDecimal value) { addCriterion("total_amount <", value, "totalAmount"); return (Criteria) this; } public Criteria andTotalAmountLessThanOrEqualTo(BigDecimal value) { addCriterion("total_amount <=", value, "totalAmount"); return (Criteria) this; } public Criteria andTotalAmountIn(List values) { addCriterion("total_amount in", values, "totalAmount"); return (Criteria) this; } public Criteria andTotalAmountNotIn(List values) { addCriterion("total_amount not in", values, "totalAmount"); return (Criteria) this; } public Criteria andTotalAmountBetween(BigDecimal value1, BigDecimal value2) { addCriterion("total_amount between", value1, value2, "totalAmount"); return (Criteria) this; } public Criteria andTotalAmountNotBetween(BigDecimal value1, BigDecimal value2) { addCriterion("total_amount not between", value1, value2, "totalAmount"); return (Criteria) this; } public Criteria andTradeStatusIsNull() { addCriterion("trade_status is null"); return (Criteria) this; } public Criteria andTradeStatusIsNotNull() { addCriterion("trade_status is not null"); return (Criteria) this; } public Criteria andTradeStatusEqualTo(String value) { addCriterion("trade_status =", value, "tradeStatus"); return (Criteria) this; } public Criteria andTradeStatusNotEqualTo(String value) { addCriterion("trade_status <>", value, "tradeStatus"); return (Criteria) this; } public Criteria andTradeStatusGreaterThan(String value) { addCriterion("trade_status >", value, "tradeStatus"); return (Criteria) this; } public Criteria andTradeStatusGreaterThanOrEqualTo(String value) { addCriterion("trade_status >=", value, "tradeStatus"); return (Criteria) this; } public Criteria andTradeStatusLessThan(String value) { addCriterion("trade_status <", value, "tradeStatus"); return (Criteria) this; } public Criteria andTradeStatusLessThanOrEqualTo(String value) { addCriterion("trade_status <=", value, "tradeStatus"); return (Criteria) this; } public Criteria andTradeStatusLike(String value) { addCriterion("trade_status like", value, "tradeStatus"); return (Criteria) this; } public Criteria andTradeStatusNotLike(String value) { addCriterion("trade_status not like", value, "tradeStatus"); return (Criteria) this; } public Criteria andTradeStatusIn(List values) { addCriterion("trade_status in", values, "tradeStatus"); return (Criteria) this; } public Criteria andTradeStatusNotIn(List values) { addCriterion("trade_status not in", values, "tradeStatus"); return (Criteria) this; } public Criteria andTradeStatusBetween(String value1, String value2) { addCriterion("trade_status between", value1, value2, "tradeStatus"); return (Criteria) this; } public Criteria andTradeStatusNotBetween(String value1, String value2) { addCriterion("trade_status not between", value1, value2, "tradeStatus"); return (Criteria) this; } public Criteria andTradeNoIsNull() { addCriterion("trade_no is null"); return (Criteria) this; } public Criteria andTradeNoIsNotNull() { addCriterion("trade_no is not null"); return (Criteria) this; } public Criteria andTradeNoEqualTo(String value) { addCriterion("trade_no =", value, "tradeNo"); return (Criteria) this; } public Criteria andTradeNoNotEqualTo(String value) { addCriterion("trade_no <>", value, "tradeNo"); return (Criteria) this; } public Criteria andTradeNoGreaterThan(String value) { addCriterion("trade_no >", value, "tradeNo"); return (Criteria) this; } public Criteria andTradeNoGreaterThanOrEqualTo(String value) { addCriterion("trade_no >=", value, "tradeNo"); return (Criteria) this; } public Criteria andTradeNoLessThan(String value) { addCriterion("trade_no <", value, "tradeNo"); return (Criteria) this; } public Criteria andTradeNoLessThanOrEqualTo(String value) { addCriterion("trade_no <=", value, "tradeNo"); return (Criteria) this; } public Criteria andTradeNoLike(String value) { addCriterion("trade_no like", value, "tradeNo"); return (Criteria) this; } public Criteria andTradeNoNotLike(String value) { addCriterion("trade_no not like", value, "tradeNo"); return (Criteria) this; } public Criteria andTradeNoIn(List values) { addCriterion("trade_no in", values, "tradeNo"); return (Criteria) this; } public Criteria andTradeNoNotIn(List values) { addCriterion("trade_no not in", values, "tradeNo"); return (Criteria) this; } public Criteria andTradeNoBetween(String value1, String value2) { addCriterion("trade_no between", value1, value2, "tradeNo"); return (Criteria) this; } public Criteria andTradeNoNotBetween(String value1, String value2) { addCriterion("trade_no not between", value1, value2, "tradeNo"); return (Criteria) this; } public Criteria andBuyerIdIsNull() { addCriterion("buyer_id is null"); return (Criteria) this; } public Criteria andBuyerIdIsNotNull() { addCriterion("buyer_id is not null"); return (Criteria) this; } public Criteria andBuyerIdEqualTo(String value) { addCriterion("buyer_id =", value, "buyerId"); return (Criteria) this; } public Criteria andBuyerIdNotEqualTo(String value) { addCriterion("buyer_id <>", value, "buyerId"); return (Criteria) this; } public Criteria andBuyerIdGreaterThan(String value) { addCriterion("buyer_id >", value, "buyerId"); return (Criteria) this; } public Criteria andBuyerIdGreaterThanOrEqualTo(String value) { addCriterion("buyer_id >=", value, "buyerId"); return (Criteria) this; } public Criteria andBuyerIdLessThan(String value) { addCriterion("buyer_id <", value, "buyerId"); return (Criteria) this; } public Criteria andBuyerIdLessThanOrEqualTo(String value) { addCriterion("buyer_id <=", value, "buyerId"); return (Criteria) this; } public Criteria andBuyerIdLike(String value) { addCriterion("buyer_id like", value, "buyerId"); return (Criteria) this; } public Criteria andBuyerIdNotLike(String value) { addCriterion("buyer_id not like", value, "buyerId"); return (Criteria) this; } public Criteria andBuyerIdIn(List values) { addCriterion("buyer_id in", values, "buyerId"); return (Criteria) this; } public Criteria andBuyerIdNotIn(List values) { addCriterion("buyer_id not in", values, "buyerId"); return (Criteria) this; } public Criteria andBuyerIdBetween(String value1, String value2) { addCriterion("buyer_id between", value1, value2, "buyerId"); return (Criteria) this; } public Criteria andBuyerIdNotBetween(String value1, String value2) { addCriterion("buyer_id not between", value1, value2, "buyerId"); return (Criteria) this; } public Criteria andGmtPaymentIsNull() { addCriterion("gmt_payment is null"); return (Criteria) this; } public Criteria andGmtPaymentIsNotNull() { addCriterion("gmt_payment is not null"); return (Criteria) this; } public Criteria andGmtPaymentEqualTo(Date value) { addCriterion("gmt_payment =", value, "gmtPayment"); return (Criteria) this; } public Criteria andGmtPaymentNotEqualTo(Date value) { addCriterion("gmt_payment <>", value, "gmtPayment"); return (Criteria) this; } public Criteria andGmtPaymentGreaterThan(Date value) { addCriterion("gmt_payment >", value, "gmtPayment"); return (Criteria) this; } public Criteria andGmtPaymentGreaterThanOrEqualTo(Date value) { addCriterion("gmt_payment >=", value, "gmtPayment"); return (Criteria) this; } public Criteria andGmtPaymentLessThan(Date value) { addCriterion("gmt_payment <", value, "gmtPayment"); return (Criteria) this; } public Criteria andGmtPaymentLessThanOrEqualTo(Date value) { addCriterion("gmt_payment <=", value, "gmtPayment"); return (Criteria) this; } public Criteria andGmtPaymentIn(List values) { addCriterion("gmt_payment in", values, "gmtPayment"); return (Criteria) this; } public Criteria andGmtPaymentNotIn(List values) { addCriterion("gmt_payment not in", values, "gmtPayment"); return (Criteria) this; } public Criteria andGmtPaymentBetween(Date value1, Date value2) { addCriterion("gmt_payment between", value1, value2, "gmtPayment"); return (Criteria) this; } public Criteria andGmtPaymentNotBetween(Date value1, Date value2) { addCriterion("gmt_payment not between", value1, value2, "gmtPayment"); return (Criteria) this; } public Criteria andBuyerPayAmountIsNull() { addCriterion("buyer_pay_amount is null"); return (Criteria) this; } public Criteria andBuyerPayAmountIsNotNull() { addCriterion("buyer_pay_amount is not null"); return (Criteria) this; } public Criteria andBuyerPayAmountEqualTo(BigDecimal value) { addCriterion("buyer_pay_amount =", value, "buyerPayAmount"); return (Criteria) this; } public Criteria andBuyerPayAmountNotEqualTo(BigDecimal value) { addCriterion("buyer_pay_amount <>", value, "buyerPayAmount"); return (Criteria) this; } public Criteria andBuyerPayAmountGreaterThan(BigDecimal value) { addCriterion("buyer_pay_amount >", value, "buyerPayAmount"); return (Criteria) this; } public Criteria andBuyerPayAmountGreaterThanOrEqualTo(BigDecimal value) { addCriterion("buyer_pay_amount >=", value, "buyerPayAmount"); return (Criteria) this; } public Criteria andBuyerPayAmountLessThan(BigDecimal value) { addCriterion("buyer_pay_amount <", value, "buyerPayAmount"); return (Criteria) this; } public Criteria andBuyerPayAmountLessThanOrEqualTo(BigDecimal value) { addCriterion("buyer_pay_amount <=", value, "buyerPayAmount"); return (Criteria) this; } public Criteria andBuyerPayAmountIn(List values) { addCriterion("buyer_pay_amount in", values, "buyerPayAmount"); return (Criteria) this; } public Criteria andBuyerPayAmountNotIn(List values) { addCriterion("buyer_pay_amount not in", values, "buyerPayAmount"); return (Criteria) this; } public Criteria andBuyerPayAmountBetween(BigDecimal value1, BigDecimal value2) { addCriterion("buyer_pay_amount between", value1, value2, "buyerPayAmount"); return (Criteria) this; } public Criteria andBuyerPayAmountNotBetween(BigDecimal value1, BigDecimal value2) { addCriterion("buyer_pay_amount not between", value1, value2, "buyerPayAmount"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { @ApiModelProperty(value = "主键ID") private Long id; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "首字母") private String firstLetter; @ApiModelProperty(value = "排序") private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; @ApiModelProperty(value = "是否显示") private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/service/AlipayOrderService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.AlipayOrder; /** * @auther macrozheng * @description 支付宝订单管理Service * @date 2023/9/8 * @github https://github.com/macrozheng */ public interface AlipayOrderService { /** * 创建订单 */ AlipayOrder create(); /** * 根据订单ID查询订单 */ AlipayOrder info(String orderId); } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/service/AlipayService.java ================================================ package com.macro.mall.tiny.service; import com.alipay.api.AlipayApiException; import com.macro.mall.tiny.dto.AliPayParam; import java.util.Map; /** * @auther macrozheng * @description 支付宝支付Service * @date 2023/9/8 * @github https://github.com/macrozheng */ public interface AlipayService { /** * 根据提交参数生成电脑支付页面 */ String pay(AliPayParam aliPayParam); /** * 支付宝异步回调处理 */ String notify(Map params); /** * @param outTradeNo 商户订单编号 * @param tradeNo 支付宝交易编号 * @return 支付宝交易状态 */ String query(String outTradeNo, String tradeNo); /** * 根据提交参数生成手机支付页面 */ String webPay(AliPayParam aliPayParam); } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/service/impl/AlipayOrderServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.RandomUtil; import com.macro.mall.tiny.mbg.mapper.AlipayOrderMapper; import com.macro.mall.tiny.mbg.model.AlipayOrder; import com.macro.mall.tiny.mbg.model.AlipayOrderExample; import com.macro.mall.tiny.service.AlipayOrderService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; /** * @auther macrozheng * @description 支付宝订单管理Service实现类 * @date 2023/9/8 * @github https://github.com/macrozheng */ @Service public class AlipayOrderServiceImpl implements AlipayOrderService { @Autowired private AlipayOrderMapper alipayOrderMapper; @Override public AlipayOrder create() { String orderId = new SimpleDateFormat("yyyyMMdd").format(new Date()) + System.currentTimeMillis(); AlipayOrder alipayOrder = new AlipayOrder(); alipayOrder.setOrderId(orderId); //模拟数量 int quantity = RandomUtil.randomInt(1, 5); BigDecimal price = new BigDecimal(20); alipayOrder.setSubject("测试商品"+quantity+"个"); alipayOrder.setTotalAmount(price.multiply(new BigDecimal(quantity))); alipayOrderMapper.insert(alipayOrder); return alipayOrder; } @Override public AlipayOrder info(String orderId) { AlipayOrderExample alipayOrderExample = new AlipayOrderExample(); alipayOrderExample.createCriteria().andOrderIdEqualTo(orderId); List orderList = alipayOrderMapper.selectByExample(alipayOrderExample); if(CollUtil.isNotEmpty(orderList)){ return orderList.get(0); } return null; } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/service/impl/AlipayServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSONObject; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.internal.util.AlipaySignature; import com.alipay.api.request.AlipayTradePagePayRequest; import com.alipay.api.request.AlipayTradeQueryRequest; import com.alipay.api.request.AlipayTradeWapPayRequest; import com.alipay.api.response.AlipayTradeQueryResponse; import com.macro.mall.tiny.config.AlipayConfig; import com.macro.mall.tiny.dto.AliPayParam; import com.macro.mall.tiny.mbg.mapper.AlipayOrderMapper; import com.macro.mall.tiny.mbg.model.AlipayOrder; import com.macro.mall.tiny.mbg.model.AlipayOrderExample; import com.macro.mall.tiny.service.AlipayOrderService; import com.macro.mall.tiny.service.AlipayService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Map; /** * @auther macrozheng * @description 支付宝支付Service实现类 * @date 2023/9/8 * @github https://github.com/macrozheng */ @Slf4j @Service public class AlipayServiceImpl implements AlipayService { @Autowired private AlipayConfig alipayConfig; @Autowired private AlipayClient alipayClient; @Autowired private AlipayOrderMapper alipayOrderMapper; @Override public String pay(AliPayParam aliPayParam) { AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); if(StrUtil.isNotEmpty(alipayConfig.getNotifyUrl())){ //异步接收地址,公网可访问 request.setNotifyUrl(alipayConfig.getNotifyUrl()); } if(StrUtil.isNotEmpty(alipayConfig.getReturnUrl())){ //同步跳转地址 request.setReturnUrl(alipayConfig.getReturnUrl()); } //******必传参数****** JSONObject bizContent = new JSONObject(); //商户订单号,商家自定义,保持唯一性 bizContent.put("out_trade_no", aliPayParam.getOutTradeNo()); //支付金额,最小值0.01元 bizContent.put("total_amount", aliPayParam.getTotalAmount()); //订单标题,不可使用特殊符号 bizContent.put("subject", aliPayParam.getSubject()); //电脑网站支付场景固定传值FAST_INSTANT_TRADE_PAY bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); request.setBizContent(bizContent.toString()); String formHtml = null; try { formHtml = alipayClient.pageExecute(request).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); } return formHtml; } @Override public String notify(Map params) { String result = "failure"; boolean signVerified = false; try { //调用SDK验证签名 signVerified = AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(), alipayConfig.getCharset(), alipayConfig.getSignType()); } catch (AlipayApiException e) { log.error("支付回调签名校验异常!",e); e.printStackTrace(); } if (signVerified) { String tradeStatus = params.get("trade_status"); if("TRADE_SUCCESS".equals(tradeStatus)){ result = "success"; AlipayOrder alipayOrder = BeanUtil.mapToBean(params, AlipayOrder.class, true, null); alipayOrder.setOrderId(params.get("out_trade_no")); log.info("notify方法被调用了,alipayOrder:{}",JSONUtil.toJsonStr(alipayOrder)); //根据orderId查询订单,并修改订单状态 AlipayOrderExample example = new AlipayOrderExample(); example.createCriteria().andOrderIdEqualTo(alipayOrder.getOrderId()); alipayOrderMapper.updateByExampleSelective(alipayOrder,example); }else{ log.warn("订单未支付成功,trade_status:{}",tradeStatus); } } else { log.warn("支付回调签名校验失败!"); } return result; } @Override public String query(String outTradeNo, String tradeNo) { AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); //******必传参数****** JSONObject bizContent = new JSONObject(); //设置查询参数,out_trade_no和trade_no至少传一个 if(StrUtil.isNotEmpty(outTradeNo)){ bizContent.put("out_trade_no",outTradeNo); } if(StrUtil.isNotEmpty(tradeNo)){ bizContent.put("trade_no",tradeNo); } //交易结算信息: trade_settle_info String[] queryOptions = {"trade_settle_info"}; bizContent.put("query_options", queryOptions); request.setBizContent(bizContent.toString()); AlipayTradeQueryResponse response = null; try { response = alipayClient.execute(request); } catch (AlipayApiException e) { log.error("查询支付宝账单异常!",e); } if(response.isSuccess()){ log.info("查询支付宝账单成功!"); } else { log.error("查询支付宝账单失败!"); } //交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)、TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、TRADE_SUCCESS(交易支付成功)、TRADE_FINISHED(交易结束,不可退款) return response.getTradeStatus(); } @Override public String webPay(AliPayParam aliPayParam) { AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest (); if(StrUtil.isNotEmpty(alipayConfig.getNotifyUrl())){ //异步接收地址,公网可访问 request.setNotifyUrl(alipayConfig.getNotifyUrl()); } if(StrUtil.isNotEmpty(alipayConfig.getReturnUrl())){ //同步跳转地址 request.setReturnUrl(alipayConfig.getReturnUrl()); } //******必传参数****** JSONObject bizContent = new JSONObject(); //商户订单号,商家自定义,保持唯一性 bizContent.put("out_trade_no", aliPayParam.getOutTradeNo()); //支付金额,最小值0.01元 bizContent.put("total_amount", aliPayParam.getTotalAmount()); //订单标题,不可使用特殊符号 bizContent.put("subject", aliPayParam.getSubject()); //手机网站支付默认传值FAST_INSTANT_TRADE_PAY bizContent.put("product_code", "QUICK_WAP_WAY"); request.setBizContent(bizContent.toString()); String formHtml = null; try { formHtml = alipayClient.pageExecute(request).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); } return formHtml; } } ================================================ FILE: mall-tiny-alipay/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-alipay/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml alipay: gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do appId: your appId alipayPublicKey: your alipayPublicKey appPrivateKey: your appPrivateKey returnUrl: http://localhost:8080/swagger-ui/ notifyUrl: ================================================ FILE: mall-tiny-alipay/src/main/resources/com/macro/mall/tiny/mbg/mapper/AlipayOrderMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, order_id, subject, total_amount, trade_status, trade_no, buyer_id, gmt_payment, buyer_pay_amount delete from alipay_order where id = #{id,jdbcType=BIGINT} delete from alipay_order SELECT LAST_INSERT_ID() insert into alipay_order (order_id, subject, total_amount, trade_status, trade_no, buyer_id, gmt_payment, buyer_pay_amount) values (#{orderId,jdbcType=VARCHAR}, #{subject,jdbcType=VARCHAR}, #{totalAmount,jdbcType=DECIMAL}, #{tradeStatus,jdbcType=VARCHAR}, #{tradeNo,jdbcType=VARCHAR}, #{buyerId,jdbcType=VARCHAR}, #{gmtPayment,jdbcType=TIMESTAMP}, #{buyerPayAmount,jdbcType=DECIMAL}) SELECT LAST_INSERT_ID() insert into alipay_order order_id, subject, total_amount, trade_status, trade_no, buyer_id, gmt_payment, buyer_pay_amount, #{orderId,jdbcType=VARCHAR}, #{subject,jdbcType=VARCHAR}, #{totalAmount,jdbcType=DECIMAL}, #{tradeStatus,jdbcType=VARCHAR}, #{tradeNo,jdbcType=VARCHAR}, #{buyerId,jdbcType=VARCHAR}, #{gmtPayment,jdbcType=TIMESTAMP}, #{buyerPayAmount,jdbcType=DECIMAL}, update alipay_order id = #{row.id,jdbcType=BIGINT}, order_id = #{row.orderId,jdbcType=VARCHAR}, subject = #{row.subject,jdbcType=VARCHAR}, total_amount = #{row.totalAmount,jdbcType=DECIMAL}, trade_status = #{row.tradeStatus,jdbcType=VARCHAR}, trade_no = #{row.tradeNo,jdbcType=VARCHAR}, buyer_id = #{row.buyerId,jdbcType=VARCHAR}, gmt_payment = #{row.gmtPayment,jdbcType=TIMESTAMP}, buyer_pay_amount = #{row.buyerPayAmount,jdbcType=DECIMAL}, update alipay_order set id = #{row.id,jdbcType=BIGINT}, order_id = #{row.orderId,jdbcType=VARCHAR}, subject = #{row.subject,jdbcType=VARCHAR}, total_amount = #{row.totalAmount,jdbcType=DECIMAL}, trade_status = #{row.tradeStatus,jdbcType=VARCHAR}, trade_no = #{row.tradeNo,jdbcType=VARCHAR}, buyer_id = #{row.buyerId,jdbcType=VARCHAR}, gmt_payment = #{row.gmtPayment,jdbcType=TIMESTAMP}, buyer_pay_amount = #{row.buyerPayAmount,jdbcType=DECIMAL} update alipay_order order_id = #{orderId,jdbcType=VARCHAR}, subject = #{subject,jdbcType=VARCHAR}, total_amount = #{totalAmount,jdbcType=DECIMAL}, trade_status = #{tradeStatus,jdbcType=VARCHAR}, trade_no = #{tradeNo,jdbcType=VARCHAR}, buyer_id = #{buyerId,jdbcType=VARCHAR}, gmt_payment = #{gmtPayment,jdbcType=TIMESTAMP}, buyer_pay_amount = #{buyerPayAmount,jdbcType=DECIMAL}, where id = #{id,jdbcType=BIGINT} update alipay_order set order_id = #{orderId,jdbcType=VARCHAR}, subject = #{subject,jdbcType=VARCHAR}, total_amount = #{totalAmount,jdbcType=DECIMAL}, trade_status = #{tradeStatus,jdbcType=VARCHAR}, trade_no = #{tradeNo,jdbcType=VARCHAR}, buyer_id = #{buyerId,jdbcType=VARCHAR}, gmt_payment = #{gmtPayment,jdbcType=TIMESTAMP}, buyer_pay_amount = #{buyerPayAmount,jdbcType=DECIMAL} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-alipay/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-alipay/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-alipay/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-alipay/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-boot/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-boot/pom.xml ================================================ 4.0.0 mall-tiny-boot 1.0-SNAPSHOT mall-tiny-boot Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true org.springframework.boot spring-boot-starter-security cn.hutool hutool-all ${hutool.version} io.jsonwebtoken jjwt ${jjwt.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-configuration-processor true org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/common/utils/JwtTokenUtil.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; /** * @auther macrozheng * @description JwtToken生成的工具类 * JWT token的格式:header.payload.signature * header的格式(算法、token的类型): * {"alg": "HS512","typ": "JWT"} * payload的格式(用户名、创建时间、生成时间): * {"sub":"wang","created":1489079981393,"exp":1489684781} * signature的生成算法: * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) * @date 2018/4/26 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/component/JwtAuthenticationTokenFilter.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; /** * @auther macrozheng * @description JWT登录授权过滤器 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当未登录或者token失效访问接口时,自定义的返回结果 * @date 2018/5/14 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当访问接口没有权限时,自定义的返回结果 * @date 2018/4/26 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/config/IgnoreUrlsConfig.java ================================================ package com.macro.mall.tiny.config; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; /** * @auther macrozheng * @description 用于配置白名单资源路径 * @date 2018/11/5 * @github https://github.com/macrozheng */ @Getter @Setter @Component @ConfigurationProperties(prefix = "secure.ignored") public class IgnoreUrlsConfig { private List urls = new ArrayList<>(); } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan("com.macro.mall.tiny.mbg.mapper") public class MyBatisConfig { } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/config/SecurityConfig.java ================================================ package com.macro.mall.tiny.config; import cn.hutool.core.util.ArrayUtil; 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.domain.AdminUserDetails; 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.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.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.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import java.util.List; /** * @auther macrozheng * @description SpringSecurity的配置 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig{ @Autowired private UmsAdminService adminService; @Autowired private RestfulAccessDeniedHandler restfulAccessDeniedHandler; @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Bean SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { List urls = ignoreUrlsConfig.getUrls(); String[] urlArray = ArrayUtil.toArray(urls, String.class); httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf .disable() .sessionManagement()// 基于token,所以不需要session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(HttpMethod.GET,urlArray) // 允许对于网站静态资源的无授权访问 .permitAll() .antMatchers(HttpMethod.POST,urlArray) // 允许对于网站静态资源的无授权访问 .permitAll() .antMatchers("/admin/login")// 对登录注册要允许匿名访问 .permitAll() .antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求 .permitAll() .anyRequest()// 除上面外的所有请求全部需要鉴权认证 .authenticated(); // 禁用缓存 httpSecurity.headers().cacheControl(); // 添加JWT filter httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); //添加自定义未授权和未登录结果返回 httpSecurity.exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler) .authenticationEntryPoint(restAuthenticationEntryPoint); return httpSecurity.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public UserDetailsService userDetailsService() { //获取登录用户信息 return username -> { AdminUserDetails admin = adminService.getAdminByUsername(username); if (admin != null) { return admin; } throw new UsernameNotFoundException("用户名或密码错误"); }; } @Bean public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() { return new JwtAuthenticationTokenFilter(); } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger文档的配置(带认证) * @date 2022/11/22 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .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(new Contact("macro", null, null)) .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; } @Bean public BeanPostProcessor generateBeanPostProcessor(){ return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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 @PreAuthorize("hasRole('ADMIN')") 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 @PreAuthorize("hasRole('ADMIN')") 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 @PreAuthorize("hasRole('ADMIN')") 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 @PreAuthorize("hasRole('ADMIN')") 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 @PreAuthorize("hasRole('ADMIN')") public CommonResult brand(@PathVariable("id") Long id) { return CommonResult.success(brandService.getBrand(id)); } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/controller/UmsAdminController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.service.UmsAdminService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; 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; import java.util.HashMap; import java.util.Map; /** * @auther macrozheng * @description 后台用户管理 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsAdminController") @Tag(name = "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 = "登录以后返回token") @RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public CommonResult login(@RequestParam String username, @RequestParam String password) { String token = adminService.login(username, password); if (token == null) { return CommonResult.validateFailed("用户名或密码错误"); } Map tokenMap = new HashMap<>(); tokenMap.put("token", token); tokenMap.put("tokenHead", tokenHead); return CommonResult.success(tokenMap); } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/domain/AdminUserDetails.java ================================================ package com.macro.mall.tiny.domain; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; 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; /** * @auther macrozheng * @description SpringSecurity需要的用户详情 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode(callSuper = false) @Builder public class AdminUserDetails implements UserDetails { private String username; private String password; private List authorityList; @Override public Collection getAuthorities() { return this.authorityList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { int countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand record); int insertSelective(PmsBrand record); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExample(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand record); int updateByPrimaryKeyWithBLOBs(PmsBrand record); int updateByPrimaryKey(PmsBrand record); } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { private Long id; private String name; @ApiModelProperty(value = "首字母") private String firstLetter; private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/service/UmsAdminService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.domain.AdminUserDetails; /** * @auther macrozheng * @description 后台用于管理Service * @date 2020/10/15 * @github https://github.com/macrozheng */ public interface UmsAdminService { /** * 根据用户名获取用户信息 */ AdminUserDetails getAdminByUsername(String username); /** * 用户名密码登录 */ String login(String username, String password); } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-boot/src/main/java/com/macro/mall/tiny/service/impl/UmsAdminServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.collection.CollUtil; import com.macro.mall.tiny.common.utils.JwtTokenUtil; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.service.UmsAdminService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; 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.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description UmsAdminService实现类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Slf4j @Service public class UmsAdminServiceImpl implements UmsAdminService { /** * 存放默认用户信息 */ private List adminUserDetailsList = new ArrayList<>(); @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private PasswordEncoder passwordEncoder; @PostConstruct private void init(){ adminUserDetailsList.add(AdminUserDetails.builder() .username("admin") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("ROLE_ADMIN")) .build()); adminUserDetailsList.add(AdminUserDetails.builder() .username("macro") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("ROLE_USER")) .build()); } @Override public AdminUserDetails getAdminByUsername(String username) { List findList = adminUserDetailsList.stream().filter(item -> item.getUsername().equals(username)).collect(Collectors.toList()); if(CollUtil.isNotEmpty(findList)){ return findList.get(0); } return null; } @Override public String login(String username, String password) { String token = null; try { UserDetails userDetails = getAdminByUsername(username); if(userDetails==null){ return token; } 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) { log.warn("登录异常:{}", e.getMessage()); } return token; } } ================================================ FILE: mall-tiny-boot/src/main/resources/application.yml ================================================ server: port: 8088 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root main: allow-circular-references: true mvc: pathmatch: matching-strategy: ant_path_matcher mybatis: mapper-locations: - classpath:mapper/*.xml - classpath*:com/**/mapper/*.xml # 自定义jwt key jwt: tokenHeader: Authorization #JWT存储的请求头 secret: mySecret #JWT加解密使用的密钥 expiration: 604800 #JWT的超期限时间(60*60*24) tokenHead: Bearer #JWT负载中拿到开头 springfox: documentation: enabled: true secure: ignored: urls: - / - /swagger-ui/ - /*.html - /favicon.ico - /**/*.html - /**/*.css - /**/*.js - /swagger-resources/** - /v2/api-docs/** - /actuator/** management: endpoints: web: exposure: include: '*' endpoint: shutdown: enabled: true logging: level: com.macro.mall.tiny: debug ================================================ FILE: mall-tiny-boot/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-boot/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-boot/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-boot/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.service.PmsBrandService; import lombok.extern.log4j.Log4j; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; @SpringBootTest @Slf4j public class MallTinyApplicationTests { @Autowired private PmsBrandService pmsBrandService; @Test public void testMethod() { List brandList = pmsBrandService.listAllBrand(); log.info("testMethod:{}", brandList); } } ================================================ FILE: mall-tiny-docker/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-docker/pom.xml ================================================ 4.0.0 mall-tiny-docker 1.0-SNAPSHOT mall-tiny-docker Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-maven-plugin io.fabric8 docker-maven-plugin ${docker.maven.plugin.version} build-image package build ${docker.host} mall-tiny/${project.name}:${project.version} openjdk:8 ${project.build.finalName}.jar / artifact ["java", "-jar","-Dspring.profiles.active=prod","/${project.build.finalName}.jar"] macrozheng ================================================ FILE: mall-tiny-docker/src/main/docker/Dockerfile ================================================ # 该镜像需要依赖的基础镜像 FROM openjdk:8 # 将当前目录下的jar包复制到docker容器的/目录下 ADD mall-tiny-docker-1.0-SNAPSHOT.jar /mall-tiny-docker-1.0-SNAPSHOT.jar # 声明服务运行在8080端口 EXPOSE 8080 # 指定docker容器启动时运行jar包 ENTRYPOINT ["java", "-jar","-Dspring.profiles.active=prod","/mall-tiny-docker-1.0-SNAPSHOT.jar"] # 指定维护者的名字 MAINTAINER macrozheng ================================================ FILE: mall-tiny-docker/src/main/docker/docker-compose.yml ================================================ version: '3' services: # 指定服务名称 db: # 指定服务使用的镜像 image: mysql:5.7 # 指定容器名称 container_name: mysql # 指定服务运行的端口 ports: - 3306:3306 # 指定容器中需要挂载的文件 volumes: - /mydata/mysql/log:/var/log/mysql - /mydata/mysql/data:/var/lib/mysql # 指定容器的环境变量 environment: - MYSQL_ROOT_PASSWORD=root # 指定服务名称 mall-tiny-docker: # 指定服务使用的镜像 image: mall-tiny/mall-tiny-docker:1.0-SNAPSHOT # 指定容器名称 container_name: mall-tiny-docker # 指定服务运行的端口 ports: - 8080:8080 # 指定容器中需要挂载的文件 volumes: - /etc/localtime:/etc/localtime - /mydata/app/mall-tiny-docker/logs:/var/logs ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan("com.macro.mall.tiny.mbg.mapper") public class MyBatisConfig { } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger相关配置 * @date 2022/11/23 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("SwaggerUI演示") .description("mall-tiny") .contact(new Contact("macro", null, null)) .version("1.0") .build(); } @Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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)); } } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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 MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { long countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand row); int insertSelective(PmsBrand row); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExample(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand row); int updateByPrimaryKeyWithBLOBs(PmsBrand row); int updateByPrimaryKey(PmsBrand row); } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { @ApiModelProperty(value = "主键ID") private Long id; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "首字母") private String firstLetter; @ApiModelProperty(value = "排序") private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; @ApiModelProperty(value = "是否显示") private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-docker/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-docker/src/main/resources/application-prod.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://db:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root ================================================ FILE: mall-tiny-docker/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml ================================================ FILE: mall-tiny-docker/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-docker/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-docker/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-docker/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-generator/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-generator/pom.xml ================================================ 4.0.0 mall-tiny-generator 1.0-SNAPSHOT mall-tiny-generator Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 cn.hutool hutool-all ${hutool.version} org.mybatis.spring.boot mybatis-spring-boot-starter ${mybatis-starter.version} com.github.pagehelper pagehelper-spring-boot-starter ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan({"com.macro.mall.tiny.mbg.mapper","com.macro.mall.tiny.dao"}) public class MyBatisConfig { } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger文档的配置 * @date 2022/11/22 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("SwaggerUI演示") .description("mall-tiny") .contact(new Contact("macro", null, null)) .version("1.0") .build(); } @Bean public BeanPostProcessor generateBeanPostProcessor(){ return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/controller/UmsAdminController.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.domain.AdminRoleDto; import com.macro.mall.tiny.domain.ResourceWithCateDto; import com.macro.mall.tiny.domain.RoleStatDto; import com.macro.mall.tiny.mbg.model.UmsAdmin; import com.macro.mall.tiny.service.UmsAdminService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 后台用户管理Controller * @date 2020/12/8 * @github https://github.com/macrozheng */ @RestController @Api(tags = "UmsAdminController") @Tag(name = "UmsAdminController", description = "后台用户管理") @RequestMapping("/admin") public class UmsAdminController { @Autowired private UmsAdminService adminService; @ApiOperation("创建") @PostMapping("/create") public CommonResult create(@RequestBody UmsAdmin entity) { adminService.create(entity); return CommonResult.success(null); } @ApiOperation("修改") @PostMapping("/update/{id}") public CommonResult update(@PathVariable Long id, @RequestBody UmsAdmin entity) { entity.setId(id); adminService.update(entity); return CommonResult.success(null); } @ApiOperation("删除") @PostMapping("/delete/{id}") public CommonResult delete(@PathVariable Long id) { adminService.delete(id); return CommonResult.success(null); } @ApiOperation("根据ID查询") @GetMapping("/select/{id}") public CommonResult select(@PathVariable Long id) { UmsAdmin entity = adminService.select(id); return CommonResult.success(entity); } @ApiOperation("分页查询所有") @GetMapping("/listAll") public CommonResult> listAll(@RequestParam(value = "pageNum", defaultValue = "1") @ApiParam("页码") Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "5") @ApiParam("每页数量") Integer pageSize) { List list = adminService.listAll(pageNum, pageSize); return CommonResult.success(CommonPage.restPage(list)); } @ApiOperation("分页条件查询") @GetMapping("/list") public CommonResult> list(@RequestParam(value = "pageNum", defaultValue = "1") @ApiParam("页码") Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "5") @ApiParam("每页数量") Integer pageSize, @RequestParam(required = false) @ApiParam("用户名") String username, @RequestParam @ApiParam(value = "状态",required = true) List statusList) { List list = adminService.list(pageNum, pageSize, username, statusList); return CommonResult.success(CommonPage.restPage(list)); } @ApiOperation("使用子查询") @GetMapping("/subList") public CommonResult> subList(@RequestParam Long roleId) { List list = adminService.subList(roleId); return CommonResult.success(list); } @ApiOperation("使用Group和Join查询") @GetMapping("/groupList") public CommonResult> groupList() { List list = adminService.groupList(); return CommonResult.success(list); } @ApiOperation("条件删除") @GetMapping("/deleteByUsername") public CommonResult deleteByUsername(@RequestParam String username) { adminService.deleteByUsername(username); return CommonResult.success(null); } @ApiOperation("条件修改") @GetMapping("/updateByIds") public CommonResult updateByIds(@RequestParam List ids,@RequestParam Integer status) { adminService.updateByIds(ids, status); return CommonResult.success(null); } @ApiOperation("一对多查询") @GetMapping("/selectWithRoleList") public CommonResult selectWithRoleList(@RequestParam Long id) { AdminRoleDto adminRoleDto = adminService.selectWithRoleList(id); return CommonResult.success(adminRoleDto); } @ApiOperation("一对一查询") @GetMapping("/selectResourceWithCate") public CommonResult selectResourceWithCate(@RequestParam Long id) { ResourceWithCateDto resourceWithCateDto = adminService.selectResourceWithCate(id); return CommonResult.success(resourceWithCateDto); } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/dao/UmsAdminDao.java ================================================ package com.macro.mall.tiny.dao; import com.macro.mall.tiny.domain.AdminRoleDto; import com.macro.mall.tiny.domain.ResourceWithCateDto; import com.macro.mall.tiny.domain.RoleStatDto; import com.macro.mall.tiny.mbg.model.UmsAdmin; import org.apache.ibatis.annotations.Param; import java.util.List; /** * @auther macrozheng * @description UmsAdmin自定义Dao * @date 2022/11/22 * @github https://github.com/macrozheng */ public interface UmsAdminDao { List groupList(); AdminRoleDto selectWithRoleList(@Param("id") Long id); List subList(@Param("roleId") Long roleId); ResourceWithCateDto selectResourceWithCate(@Param("id")Long id); } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/domain/AdminRoleDto.java ================================================ package com.macro.mall.tiny.domain; import com.macro.mall.tiny.mbg.model.UmsAdmin; import com.macro.mall.tiny.mbg.model.UmsRole; import java.util.List; /** * Created by macro on 2020/12/9. */ public class AdminRoleDto extends UmsAdmin { private List roleList; public List getRoleList() { return roleList; } public void setRoleList(List roleList) { this.roleList = roleList; } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/domain/ResourceWithCateDto.java ================================================ package com.macro.mall.tiny.domain; import com.macro.mall.tiny.mbg.model.UmsResource; import com.macro.mall.tiny.mbg.model.UmsResourceCategory; /** * Created by macro on 2020/12/9. */ public class ResourceWithCateDto extends UmsResource { private UmsResourceCategory resourceCategory; public UmsResourceCategory getResourceCategory() { return resourceCategory; } public void setResourceCategory(UmsResourceCategory resourceCategory) { this.resourceCategory = resourceCategory; } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/domain/RoleStatDto.java ================================================ package com.macro.mall.tiny.domain; /** * Created by macro on 2020/12/9. */ public class RoleStatDto { private Long roleId; private String roleName; private Integer count; public Long getRoleId() { return roleId; } public String getRoleName() { return roleName; } public Integer getCount() { return count; } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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)){ //数据库中特殊字符需要转义 if(remarks.contains("\"")){ remarks = remarks.replace("\"","'"); } //给model的字段添加swagger注解 field.addJavaDocLine("@ApiModelProperty(value = \""+remarks+"\")"); } } @Override public void addJavaFileComment(CompilationUnit compilationUnit) { super.addJavaFileComment(compilationUnit); //只在model中添加swagger注解类的导入 String fullyQualifiedName = compilationUnit.getType().getFullyQualifiedName(); if(!fullyQualifiedName.contains(MAPPER_SUFFIX)&&!fullyQualifiedName.contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/mapper/UmsAdminMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.UmsAdmin; import com.macro.mall.tiny.mbg.model.UmsAdminExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface UmsAdminMapper { long countByExample(UmsAdminExample example); int deleteByExample(UmsAdminExample example); int deleteByPrimaryKey(Long id); int insert(UmsAdmin row); int insertSelective(UmsAdmin row); List selectByExample(UmsAdminExample example); UmsAdmin selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") UmsAdmin row, @Param("example") UmsAdminExample example); int updateByExample(@Param("row") UmsAdmin row, @Param("example") UmsAdminExample example); int updateByPrimaryKeySelective(UmsAdmin row); int updateByPrimaryKey(UmsAdmin row); } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/mapper/UmsAdminRoleRelationMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.UmsAdminRoleRelation; import com.macro.mall.tiny.mbg.model.UmsAdminRoleRelationExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface UmsAdminRoleRelationMapper { long countByExample(UmsAdminRoleRelationExample example); int deleteByExample(UmsAdminRoleRelationExample example); int deleteByPrimaryKey(Long id); int insert(UmsAdminRoleRelation row); int insertSelective(UmsAdminRoleRelation row); List selectByExample(UmsAdminRoleRelationExample example); UmsAdminRoleRelation selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") UmsAdminRoleRelation row, @Param("example") UmsAdminRoleRelationExample example); int updateByExample(@Param("row") UmsAdminRoleRelation row, @Param("example") UmsAdminRoleRelationExample example); int updateByPrimaryKeySelective(UmsAdminRoleRelation row); int updateByPrimaryKey(UmsAdminRoleRelation row); } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/mapper/UmsResourceCategoryMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.UmsResourceCategory; import com.macro.mall.tiny.mbg.model.UmsResourceCategoryExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface UmsResourceCategoryMapper { long countByExample(UmsResourceCategoryExample example); int deleteByExample(UmsResourceCategoryExample example); int deleteByPrimaryKey(Long id); int insert(UmsResourceCategory row); int insertSelective(UmsResourceCategory row); List selectByExample(UmsResourceCategoryExample example); UmsResourceCategory selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") UmsResourceCategory row, @Param("example") UmsResourceCategoryExample example); int updateByExample(@Param("row") UmsResourceCategory row, @Param("example") UmsResourceCategoryExample example); int updateByPrimaryKeySelective(UmsResourceCategory row); int updateByPrimaryKey(UmsResourceCategory row); } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/mapper/UmsResourceMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.UmsResource; import com.macro.mall.tiny.mbg.model.UmsResourceExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface UmsResourceMapper { long countByExample(UmsResourceExample example); int deleteByExample(UmsResourceExample example); int deleteByPrimaryKey(Long id); int insert(UmsResource row); int insertSelective(UmsResource row); List selectByExample(UmsResourceExample example); UmsResource selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") UmsResource row, @Param("example") UmsResourceExample example); int updateByExample(@Param("row") UmsResource row, @Param("example") UmsResourceExample example); int updateByPrimaryKeySelective(UmsResource row); int updateByPrimaryKey(UmsResource row); } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/mapper/UmsRoleMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.UmsRole; import com.macro.mall.tiny.mbg.model.UmsRoleExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface UmsRoleMapper { long countByExample(UmsRoleExample example); int deleteByExample(UmsRoleExample example); int deleteByPrimaryKey(Long id); int insert(UmsRole row); int insertSelective(UmsRole row); List selectByExample(UmsRoleExample example); UmsRole selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") UmsRole row, @Param("example") UmsRoleExample example); int updateByExample(@Param("row") UmsRole row, @Param("example") UmsRoleExample example); int updateByPrimaryKeySelective(UmsRole row); int updateByPrimaryKey(UmsRole row); } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/model/UmsAdmin.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Date; public class UmsAdmin implements Serializable { private Long id; private String username; private String password; @ApiModelProperty(value = "头像") private String icon; @ApiModelProperty(value = "邮箱") private String email; @ApiModelProperty(value = "昵称") private String nickName; @ApiModelProperty(value = "备注信息") private String note; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "最后登录时间") private Date loginTime; @ApiModelProperty(value = "帐号启用状态:0->禁用;1->启用") private Integer status; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getIcon() { return icon; } public void setIcon(String icon) { this.icon = icon; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public String getNote() { return note; } public void setNote(String note) { this.note = note; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Date getLoginTime() { return loginTime; } public void setLoginTime(Date loginTime) { this.loginTime = loginTime; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", username=").append(username); sb.append(", password=").append(password); sb.append(", icon=").append(icon); sb.append(", email=").append(email); sb.append(", nickName=").append(nickName); sb.append(", note=").append(note); sb.append(", createTime=").append(createTime); sb.append(", loginTime=").append(loginTime); sb.append(", status=").append(status); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/model/UmsAdminExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.Date; import java.util.List; public class UmsAdminExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public UmsAdminExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andUsernameIsNull() { addCriterion("username is null"); return (Criteria) this; } public Criteria andUsernameIsNotNull() { addCriterion("username is not null"); return (Criteria) this; } public Criteria andUsernameEqualTo(String value) { addCriterion("username =", value, "username"); return (Criteria) this; } public Criteria andUsernameNotEqualTo(String value) { addCriterion("username <>", value, "username"); return (Criteria) this; } public Criteria andUsernameGreaterThan(String value) { addCriterion("username >", value, "username"); return (Criteria) this; } public Criteria andUsernameGreaterThanOrEqualTo(String value) { addCriterion("username >=", value, "username"); return (Criteria) this; } public Criteria andUsernameLessThan(String value) { addCriterion("username <", value, "username"); return (Criteria) this; } public Criteria andUsernameLessThanOrEqualTo(String value) { addCriterion("username <=", value, "username"); return (Criteria) this; } public Criteria andUsernameLike(String value) { addCriterion("username like", value, "username"); return (Criteria) this; } public Criteria andUsernameNotLike(String value) { addCriterion("username not like", value, "username"); return (Criteria) this; } public Criteria andUsernameIn(List values) { addCriterion("username in", values, "username"); return (Criteria) this; } public Criteria andUsernameNotIn(List values) { addCriterion("username not in", values, "username"); return (Criteria) this; } public Criteria andUsernameBetween(String value1, String value2) { addCriterion("username between", value1, value2, "username"); return (Criteria) this; } public Criteria andUsernameNotBetween(String value1, String value2) { addCriterion("username not between", value1, value2, "username"); return (Criteria) this; } public Criteria andPasswordIsNull() { addCriterion("password is null"); return (Criteria) this; } public Criteria andPasswordIsNotNull() { addCriterion("password is not null"); return (Criteria) this; } public Criteria andPasswordEqualTo(String value) { addCriterion("password =", value, "password"); return (Criteria) this; } public Criteria andPasswordNotEqualTo(String value) { addCriterion("password <>", value, "password"); return (Criteria) this; } public Criteria andPasswordGreaterThan(String value) { addCriterion("password >", value, "password"); return (Criteria) this; } public Criteria andPasswordGreaterThanOrEqualTo(String value) { addCriterion("password >=", value, "password"); return (Criteria) this; } public Criteria andPasswordLessThan(String value) { addCriterion("password <", value, "password"); return (Criteria) this; } public Criteria andPasswordLessThanOrEqualTo(String value) { addCriterion("password <=", value, "password"); return (Criteria) this; } public Criteria andPasswordLike(String value) { addCriterion("password like", value, "password"); return (Criteria) this; } public Criteria andPasswordNotLike(String value) { addCriterion("password not like", value, "password"); return (Criteria) this; } public Criteria andPasswordIn(List values) { addCriterion("password in", values, "password"); return (Criteria) this; } public Criteria andPasswordNotIn(List values) { addCriterion("password not in", values, "password"); return (Criteria) this; } public Criteria andPasswordBetween(String value1, String value2) { addCriterion("password between", value1, value2, "password"); return (Criteria) this; } public Criteria andPasswordNotBetween(String value1, String value2) { addCriterion("password not between", value1, value2, "password"); return (Criteria) this; } public Criteria andIconIsNull() { addCriterion("icon is null"); return (Criteria) this; } public Criteria andIconIsNotNull() { addCriterion("icon is not null"); return (Criteria) this; } public Criteria andIconEqualTo(String value) { addCriterion("icon =", value, "icon"); return (Criteria) this; } public Criteria andIconNotEqualTo(String value) { addCriterion("icon <>", value, "icon"); return (Criteria) this; } public Criteria andIconGreaterThan(String value) { addCriterion("icon >", value, "icon"); return (Criteria) this; } public Criteria andIconGreaterThanOrEqualTo(String value) { addCriterion("icon >=", value, "icon"); return (Criteria) this; } public Criteria andIconLessThan(String value) { addCriterion("icon <", value, "icon"); return (Criteria) this; } public Criteria andIconLessThanOrEqualTo(String value) { addCriterion("icon <=", value, "icon"); return (Criteria) this; } public Criteria andIconLike(String value) { addCriterion("icon like", value, "icon"); return (Criteria) this; } public Criteria andIconNotLike(String value) { addCriterion("icon not like", value, "icon"); return (Criteria) this; } public Criteria andIconIn(List values) { addCriterion("icon in", values, "icon"); return (Criteria) this; } public Criteria andIconNotIn(List values) { addCriterion("icon not in", values, "icon"); return (Criteria) this; } public Criteria andIconBetween(String value1, String value2) { addCriterion("icon between", value1, value2, "icon"); return (Criteria) this; } public Criteria andIconNotBetween(String value1, String value2) { addCriterion("icon not between", value1, value2, "icon"); return (Criteria) this; } public Criteria andEmailIsNull() { addCriterion("email is null"); return (Criteria) this; } public Criteria andEmailIsNotNull() { addCriterion("email is not null"); return (Criteria) this; } public Criteria andEmailEqualTo(String value) { addCriterion("email =", value, "email"); return (Criteria) this; } public Criteria andEmailNotEqualTo(String value) { addCriterion("email <>", value, "email"); return (Criteria) this; } public Criteria andEmailGreaterThan(String value) { addCriterion("email >", value, "email"); return (Criteria) this; } public Criteria andEmailGreaterThanOrEqualTo(String value) { addCriterion("email >=", value, "email"); return (Criteria) this; } public Criteria andEmailLessThan(String value) { addCriterion("email <", value, "email"); return (Criteria) this; } public Criteria andEmailLessThanOrEqualTo(String value) { addCriterion("email <=", value, "email"); return (Criteria) this; } public Criteria andEmailLike(String value) { addCriterion("email like", value, "email"); return (Criteria) this; } public Criteria andEmailNotLike(String value) { addCriterion("email not like", value, "email"); return (Criteria) this; } public Criteria andEmailIn(List values) { addCriterion("email in", values, "email"); return (Criteria) this; } public Criteria andEmailNotIn(List values) { addCriterion("email not in", values, "email"); return (Criteria) this; } public Criteria andEmailBetween(String value1, String value2) { addCriterion("email between", value1, value2, "email"); return (Criteria) this; } public Criteria andEmailNotBetween(String value1, String value2) { addCriterion("email not between", value1, value2, "email"); return (Criteria) this; } public Criteria andNickNameIsNull() { addCriterion("nick_name is null"); return (Criteria) this; } public Criteria andNickNameIsNotNull() { addCriterion("nick_name is not null"); return (Criteria) this; } public Criteria andNickNameEqualTo(String value) { addCriterion("nick_name =", value, "nickName"); return (Criteria) this; } public Criteria andNickNameNotEqualTo(String value) { addCriterion("nick_name <>", value, "nickName"); return (Criteria) this; } public Criteria andNickNameGreaterThan(String value) { addCriterion("nick_name >", value, "nickName"); return (Criteria) this; } public Criteria andNickNameGreaterThanOrEqualTo(String value) { addCriterion("nick_name >=", value, "nickName"); return (Criteria) this; } public Criteria andNickNameLessThan(String value) { addCriterion("nick_name <", value, "nickName"); return (Criteria) this; } public Criteria andNickNameLessThanOrEqualTo(String value) { addCriterion("nick_name <=", value, "nickName"); return (Criteria) this; } public Criteria andNickNameLike(String value) { addCriterion("nick_name like", value, "nickName"); return (Criteria) this; } public Criteria andNickNameNotLike(String value) { addCriterion("nick_name not like", value, "nickName"); return (Criteria) this; } public Criteria andNickNameIn(List values) { addCriterion("nick_name in", values, "nickName"); return (Criteria) this; } public Criteria andNickNameNotIn(List values) { addCriterion("nick_name not in", values, "nickName"); return (Criteria) this; } public Criteria andNickNameBetween(String value1, String value2) { addCriterion("nick_name between", value1, value2, "nickName"); return (Criteria) this; } public Criteria andNickNameNotBetween(String value1, String value2) { addCriterion("nick_name not between", value1, value2, "nickName"); return (Criteria) this; } public Criteria andNoteIsNull() { addCriterion("note is null"); return (Criteria) this; } public Criteria andNoteIsNotNull() { addCriterion("note is not null"); return (Criteria) this; } public Criteria andNoteEqualTo(String value) { addCriterion("note =", value, "note"); return (Criteria) this; } public Criteria andNoteNotEqualTo(String value) { addCriterion("note <>", value, "note"); return (Criteria) this; } public Criteria andNoteGreaterThan(String value) { addCriterion("note >", value, "note"); return (Criteria) this; } public Criteria andNoteGreaterThanOrEqualTo(String value) { addCriterion("note >=", value, "note"); return (Criteria) this; } public Criteria andNoteLessThan(String value) { addCriterion("note <", value, "note"); return (Criteria) this; } public Criteria andNoteLessThanOrEqualTo(String value) { addCriterion("note <=", value, "note"); return (Criteria) this; } public Criteria andNoteLike(String value) { addCriterion("note like", value, "note"); return (Criteria) this; } public Criteria andNoteNotLike(String value) { addCriterion("note not like", value, "note"); return (Criteria) this; } public Criteria andNoteIn(List values) { addCriterion("note in", values, "note"); return (Criteria) this; } public Criteria andNoteNotIn(List values) { addCriterion("note not in", values, "note"); return (Criteria) this; } public Criteria andNoteBetween(String value1, String value2) { addCriterion("note between", value1, value2, "note"); return (Criteria) this; } public Criteria andNoteNotBetween(String value1, String value2) { addCriterion("note not between", value1, value2, "note"); return (Criteria) this; } public Criteria andCreateTimeIsNull() { addCriterion("create_time is null"); return (Criteria) this; } public Criteria andCreateTimeIsNotNull() { addCriterion("create_time is not null"); return (Criteria) this; } public Criteria andCreateTimeEqualTo(Date value) { addCriterion("create_time =", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotEqualTo(Date value) { addCriterion("create_time <>", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeGreaterThan(Date value) { addCriterion("create_time >", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeGreaterThanOrEqualTo(Date value) { addCriterion("create_time >=", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeLessThan(Date value) { addCriterion("create_time <", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeLessThanOrEqualTo(Date value) { addCriterion("create_time <=", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeIn(List values) { addCriterion("create_time in", values, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotIn(List values) { addCriterion("create_time not in", values, "createTime"); return (Criteria) this; } public Criteria andCreateTimeBetween(Date value1, Date value2) { addCriterion("create_time between", value1, value2, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotBetween(Date value1, Date value2) { addCriterion("create_time not between", value1, value2, "createTime"); return (Criteria) this; } public Criteria andLoginTimeIsNull() { addCriterion("login_time is null"); return (Criteria) this; } public Criteria andLoginTimeIsNotNull() { addCriterion("login_time is not null"); return (Criteria) this; } public Criteria andLoginTimeEqualTo(Date value) { addCriterion("login_time =", value, "loginTime"); return (Criteria) this; } public Criteria andLoginTimeNotEqualTo(Date value) { addCriterion("login_time <>", value, "loginTime"); return (Criteria) this; } public Criteria andLoginTimeGreaterThan(Date value) { addCriterion("login_time >", value, "loginTime"); return (Criteria) this; } public Criteria andLoginTimeGreaterThanOrEqualTo(Date value) { addCriterion("login_time >=", value, "loginTime"); return (Criteria) this; } public Criteria andLoginTimeLessThan(Date value) { addCriterion("login_time <", value, "loginTime"); return (Criteria) this; } public Criteria andLoginTimeLessThanOrEqualTo(Date value) { addCriterion("login_time <=", value, "loginTime"); return (Criteria) this; } public Criteria andLoginTimeIn(List values) { addCriterion("login_time in", values, "loginTime"); return (Criteria) this; } public Criteria andLoginTimeNotIn(List values) { addCriterion("login_time not in", values, "loginTime"); return (Criteria) this; } public Criteria andLoginTimeBetween(Date value1, Date value2) { addCriterion("login_time between", value1, value2, "loginTime"); return (Criteria) this; } public Criteria andLoginTimeNotBetween(Date value1, Date value2) { addCriterion("login_time not between", value1, value2, "loginTime"); return (Criteria) this; } public Criteria andStatusIsNull() { addCriterion("status is null"); return (Criteria) this; } public Criteria andStatusIsNotNull() { addCriterion("status is not null"); return (Criteria) this; } public Criteria andStatusEqualTo(Integer value) { addCriterion("status =", value, "status"); return (Criteria) this; } public Criteria andStatusNotEqualTo(Integer value) { addCriterion("status <>", value, "status"); return (Criteria) this; } public Criteria andStatusGreaterThan(Integer value) { addCriterion("status >", value, "status"); return (Criteria) this; } public Criteria andStatusGreaterThanOrEqualTo(Integer value) { addCriterion("status >=", value, "status"); return (Criteria) this; } public Criteria andStatusLessThan(Integer value) { addCriterion("status <", value, "status"); return (Criteria) this; } public Criteria andStatusLessThanOrEqualTo(Integer value) { addCriterion("status <=", value, "status"); return (Criteria) this; } public Criteria andStatusIn(List values) { addCriterion("status in", values, "status"); return (Criteria) this; } public Criteria andStatusNotIn(List values) { addCriterion("status not in", values, "status"); return (Criteria) this; } public Criteria andStatusBetween(Integer value1, Integer value2) { addCriterion("status between", value1, value2, "status"); return (Criteria) this; } public Criteria andStatusNotBetween(Integer value1, Integer value2) { addCriterion("status not between", value1, value2, "status"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/model/UmsAdminRoleRelation.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class UmsAdminRoleRelation implements Serializable { private Long id; private Long adminId; private Long roleId; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getAdminId() { return adminId; } public void setAdminId(Long adminId) { this.adminId = adminId; } public Long getRoleId() { return roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", adminId=").append(adminId); sb.append(", roleId=").append(roleId); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/model/UmsAdminRoleRelationExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class UmsAdminRoleRelationExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public UmsAdminRoleRelationExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andAdminIdIsNull() { addCriterion("admin_id is null"); return (Criteria) this; } public Criteria andAdminIdIsNotNull() { addCriterion("admin_id is not null"); return (Criteria) this; } public Criteria andAdminIdEqualTo(Long value) { addCriterion("admin_id =", value, "adminId"); return (Criteria) this; } public Criteria andAdminIdNotEqualTo(Long value) { addCriterion("admin_id <>", value, "adminId"); return (Criteria) this; } public Criteria andAdminIdGreaterThan(Long value) { addCriterion("admin_id >", value, "adminId"); return (Criteria) this; } public Criteria andAdminIdGreaterThanOrEqualTo(Long value) { addCriterion("admin_id >=", value, "adminId"); return (Criteria) this; } public Criteria andAdminIdLessThan(Long value) { addCriterion("admin_id <", value, "adminId"); return (Criteria) this; } public Criteria andAdminIdLessThanOrEqualTo(Long value) { addCriterion("admin_id <=", value, "adminId"); return (Criteria) this; } public Criteria andAdminIdIn(List values) { addCriterion("admin_id in", values, "adminId"); return (Criteria) this; } public Criteria andAdminIdNotIn(List values) { addCriterion("admin_id not in", values, "adminId"); return (Criteria) this; } public Criteria andAdminIdBetween(Long value1, Long value2) { addCriterion("admin_id between", value1, value2, "adminId"); return (Criteria) this; } public Criteria andAdminIdNotBetween(Long value1, Long value2) { addCriterion("admin_id not between", value1, value2, "adminId"); return (Criteria) this; } public Criteria andRoleIdIsNull() { addCriterion("role_id is null"); return (Criteria) this; } public Criteria andRoleIdIsNotNull() { addCriterion("role_id is not null"); return (Criteria) this; } public Criteria andRoleIdEqualTo(Long value) { addCriterion("role_id =", value, "roleId"); return (Criteria) this; } public Criteria andRoleIdNotEqualTo(Long value) { addCriterion("role_id <>", value, "roleId"); return (Criteria) this; } public Criteria andRoleIdGreaterThan(Long value) { addCriterion("role_id >", value, "roleId"); return (Criteria) this; } public Criteria andRoleIdGreaterThanOrEqualTo(Long value) { addCriterion("role_id >=", value, "roleId"); return (Criteria) this; } public Criteria andRoleIdLessThan(Long value) { addCriterion("role_id <", value, "roleId"); return (Criteria) this; } public Criteria andRoleIdLessThanOrEqualTo(Long value) { addCriterion("role_id <=", value, "roleId"); return (Criteria) this; } public Criteria andRoleIdIn(List values) { addCriterion("role_id in", values, "roleId"); return (Criteria) this; } public Criteria andRoleIdNotIn(List values) { addCriterion("role_id not in", values, "roleId"); return (Criteria) this; } public Criteria andRoleIdBetween(Long value1, Long value2) { addCriterion("role_id between", value1, value2, "roleId"); return (Criteria) this; } public Criteria andRoleIdNotBetween(Long value1, Long value2) { addCriterion("role_id not between", value1, value2, "roleId"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/model/UmsResource.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Date; public class UmsResource implements Serializable { private Long id; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "资源名称") private String name; @ApiModelProperty(value = "资源URL") private String url; @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "资源分类ID") private Long categoryId; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", createTime=").append(createTime); sb.append(", name=").append(name); sb.append(", url=").append(url); sb.append(", description=").append(description); sb.append(", categoryId=").append(categoryId); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/model/UmsResourceCategory.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Date; public class UmsResourceCategory implements Serializable { private Long id; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "分类名称") private String name; @ApiModelProperty(value = "排序") private Integer sort; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", createTime=").append(createTime); sb.append(", name=").append(name); sb.append(", sort=").append(sort); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/model/UmsResourceCategoryExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.Date; import java.util.List; public class UmsResourceCategoryExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public UmsResourceCategoryExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andCreateTimeIsNull() { addCriterion("create_time is null"); return (Criteria) this; } public Criteria andCreateTimeIsNotNull() { addCriterion("create_time is not null"); return (Criteria) this; } public Criteria andCreateTimeEqualTo(Date value) { addCriterion("create_time =", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotEqualTo(Date value) { addCriterion("create_time <>", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeGreaterThan(Date value) { addCriterion("create_time >", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeGreaterThanOrEqualTo(Date value) { addCriterion("create_time >=", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeLessThan(Date value) { addCriterion("create_time <", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeLessThanOrEqualTo(Date value) { addCriterion("create_time <=", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeIn(List values) { addCriterion("create_time in", values, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotIn(List values) { addCriterion("create_time not in", values, "createTime"); return (Criteria) this; } public Criteria andCreateTimeBetween(Date value1, Date value2) { addCriterion("create_time between", value1, value2, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotBetween(Date value1, Date value2) { addCriterion("create_time not between", value1, value2, "createTime"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/model/UmsResourceExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.Date; import java.util.List; public class UmsResourceExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public UmsResourceExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andCreateTimeIsNull() { addCriterion("create_time is null"); return (Criteria) this; } public Criteria andCreateTimeIsNotNull() { addCriterion("create_time is not null"); return (Criteria) this; } public Criteria andCreateTimeEqualTo(Date value) { addCriterion("create_time =", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotEqualTo(Date value) { addCriterion("create_time <>", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeGreaterThan(Date value) { addCriterion("create_time >", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeGreaterThanOrEqualTo(Date value) { addCriterion("create_time >=", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeLessThan(Date value) { addCriterion("create_time <", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeLessThanOrEqualTo(Date value) { addCriterion("create_time <=", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeIn(List values) { addCriterion("create_time in", values, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotIn(List values) { addCriterion("create_time not in", values, "createTime"); return (Criteria) this; } public Criteria andCreateTimeBetween(Date value1, Date value2) { addCriterion("create_time between", value1, value2, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotBetween(Date value1, Date value2) { addCriterion("create_time not between", value1, value2, "createTime"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andUrlIsNull() { addCriterion("url is null"); return (Criteria) this; } public Criteria andUrlIsNotNull() { addCriterion("url is not null"); return (Criteria) this; } public Criteria andUrlEqualTo(String value) { addCriterion("url =", value, "url"); return (Criteria) this; } public Criteria andUrlNotEqualTo(String value) { addCriterion("url <>", value, "url"); return (Criteria) this; } public Criteria andUrlGreaterThan(String value) { addCriterion("url >", value, "url"); return (Criteria) this; } public Criteria andUrlGreaterThanOrEqualTo(String value) { addCriterion("url >=", value, "url"); return (Criteria) this; } public Criteria andUrlLessThan(String value) { addCriterion("url <", value, "url"); return (Criteria) this; } public Criteria andUrlLessThanOrEqualTo(String value) { addCriterion("url <=", value, "url"); return (Criteria) this; } public Criteria andUrlLike(String value) { addCriterion("url like", value, "url"); return (Criteria) this; } public Criteria andUrlNotLike(String value) { addCriterion("url not like", value, "url"); return (Criteria) this; } public Criteria andUrlIn(List values) { addCriterion("url in", values, "url"); return (Criteria) this; } public Criteria andUrlNotIn(List values) { addCriterion("url not in", values, "url"); return (Criteria) this; } public Criteria andUrlBetween(String value1, String value2) { addCriterion("url between", value1, value2, "url"); return (Criteria) this; } public Criteria andUrlNotBetween(String value1, String value2) { addCriterion("url not between", value1, value2, "url"); return (Criteria) this; } public Criteria andDescriptionIsNull() { addCriterion("description is null"); return (Criteria) this; } public Criteria andDescriptionIsNotNull() { addCriterion("description is not null"); return (Criteria) this; } public Criteria andDescriptionEqualTo(String value) { addCriterion("description =", value, "description"); return (Criteria) this; } public Criteria andDescriptionNotEqualTo(String value) { addCriterion("description <>", value, "description"); return (Criteria) this; } public Criteria andDescriptionGreaterThan(String value) { addCriterion("description >", value, "description"); return (Criteria) this; } public Criteria andDescriptionGreaterThanOrEqualTo(String value) { addCriterion("description >=", value, "description"); return (Criteria) this; } public Criteria andDescriptionLessThan(String value) { addCriterion("description <", value, "description"); return (Criteria) this; } public Criteria andDescriptionLessThanOrEqualTo(String value) { addCriterion("description <=", value, "description"); return (Criteria) this; } public Criteria andDescriptionLike(String value) { addCriterion("description like", value, "description"); return (Criteria) this; } public Criteria andDescriptionNotLike(String value) { addCriterion("description not like", value, "description"); return (Criteria) this; } public Criteria andDescriptionIn(List values) { addCriterion("description in", values, "description"); return (Criteria) this; } public Criteria andDescriptionNotIn(List values) { addCriterion("description not in", values, "description"); return (Criteria) this; } public Criteria andDescriptionBetween(String value1, String value2) { addCriterion("description between", value1, value2, "description"); return (Criteria) this; } public Criteria andDescriptionNotBetween(String value1, String value2) { addCriterion("description not between", value1, value2, "description"); return (Criteria) this; } public Criteria andCategoryIdIsNull() { addCriterion("category_id is null"); return (Criteria) this; } public Criteria andCategoryIdIsNotNull() { addCriterion("category_id is not null"); return (Criteria) this; } public Criteria andCategoryIdEqualTo(Long value) { addCriterion("category_id =", value, "categoryId"); return (Criteria) this; } public Criteria andCategoryIdNotEqualTo(Long value) { addCriterion("category_id <>", value, "categoryId"); return (Criteria) this; } public Criteria andCategoryIdGreaterThan(Long value) { addCriterion("category_id >", value, "categoryId"); return (Criteria) this; } public Criteria andCategoryIdGreaterThanOrEqualTo(Long value) { addCriterion("category_id >=", value, "categoryId"); return (Criteria) this; } public Criteria andCategoryIdLessThan(Long value) { addCriterion("category_id <", value, "categoryId"); return (Criteria) this; } public Criteria andCategoryIdLessThanOrEqualTo(Long value) { addCriterion("category_id <=", value, "categoryId"); return (Criteria) this; } public Criteria andCategoryIdIn(List values) { addCriterion("category_id in", values, "categoryId"); return (Criteria) this; } public Criteria andCategoryIdNotIn(List values) { addCriterion("category_id not in", values, "categoryId"); return (Criteria) this; } public Criteria andCategoryIdBetween(Long value1, Long value2) { addCriterion("category_id between", value1, value2, "categoryId"); return (Criteria) this; } public Criteria andCategoryIdNotBetween(Long value1, Long value2) { addCriterion("category_id not between", value1, value2, "categoryId"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/model/UmsRole.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Date; public class UmsRole implements Serializable { private Long id; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "后台用户数量") private Integer adminCount; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "启用状态:0->禁用;1->启用") private Integer status; private Integer sort; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Integer getAdminCount() { return adminCount; } public void setAdminCount(Integer adminCount) { this.adminCount = adminCount; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", description=").append(description); sb.append(", adminCount=").append(adminCount); sb.append(", createTime=").append(createTime); sb.append(", status=").append(status); sb.append(", sort=").append(sort); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/mbg/model/UmsRoleExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.Date; import java.util.List; public class UmsRoleExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public UmsRoleExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andDescriptionIsNull() { addCriterion("description is null"); return (Criteria) this; } public Criteria andDescriptionIsNotNull() { addCriterion("description is not null"); return (Criteria) this; } public Criteria andDescriptionEqualTo(String value) { addCriterion("description =", value, "description"); return (Criteria) this; } public Criteria andDescriptionNotEqualTo(String value) { addCriterion("description <>", value, "description"); return (Criteria) this; } public Criteria andDescriptionGreaterThan(String value) { addCriterion("description >", value, "description"); return (Criteria) this; } public Criteria andDescriptionGreaterThanOrEqualTo(String value) { addCriterion("description >=", value, "description"); return (Criteria) this; } public Criteria andDescriptionLessThan(String value) { addCriterion("description <", value, "description"); return (Criteria) this; } public Criteria andDescriptionLessThanOrEqualTo(String value) { addCriterion("description <=", value, "description"); return (Criteria) this; } public Criteria andDescriptionLike(String value) { addCriterion("description like", value, "description"); return (Criteria) this; } public Criteria andDescriptionNotLike(String value) { addCriterion("description not like", value, "description"); return (Criteria) this; } public Criteria andDescriptionIn(List values) { addCriterion("description in", values, "description"); return (Criteria) this; } public Criteria andDescriptionNotIn(List values) { addCriterion("description not in", values, "description"); return (Criteria) this; } public Criteria andDescriptionBetween(String value1, String value2) { addCriterion("description between", value1, value2, "description"); return (Criteria) this; } public Criteria andDescriptionNotBetween(String value1, String value2) { addCriterion("description not between", value1, value2, "description"); return (Criteria) this; } public Criteria andAdminCountIsNull() { addCriterion("admin_count is null"); return (Criteria) this; } public Criteria andAdminCountIsNotNull() { addCriterion("admin_count is not null"); return (Criteria) this; } public Criteria andAdminCountEqualTo(Integer value) { addCriterion("admin_count =", value, "adminCount"); return (Criteria) this; } public Criteria andAdminCountNotEqualTo(Integer value) { addCriterion("admin_count <>", value, "adminCount"); return (Criteria) this; } public Criteria andAdminCountGreaterThan(Integer value) { addCriterion("admin_count >", value, "adminCount"); return (Criteria) this; } public Criteria andAdminCountGreaterThanOrEqualTo(Integer value) { addCriterion("admin_count >=", value, "adminCount"); return (Criteria) this; } public Criteria andAdminCountLessThan(Integer value) { addCriterion("admin_count <", value, "adminCount"); return (Criteria) this; } public Criteria andAdminCountLessThanOrEqualTo(Integer value) { addCriterion("admin_count <=", value, "adminCount"); return (Criteria) this; } public Criteria andAdminCountIn(List values) { addCriterion("admin_count in", values, "adminCount"); return (Criteria) this; } public Criteria andAdminCountNotIn(List values) { addCriterion("admin_count not in", values, "adminCount"); return (Criteria) this; } public Criteria andAdminCountBetween(Integer value1, Integer value2) { addCriterion("admin_count between", value1, value2, "adminCount"); return (Criteria) this; } public Criteria andAdminCountNotBetween(Integer value1, Integer value2) { addCriterion("admin_count not between", value1, value2, "adminCount"); return (Criteria) this; } public Criteria andCreateTimeIsNull() { addCriterion("create_time is null"); return (Criteria) this; } public Criteria andCreateTimeIsNotNull() { addCriterion("create_time is not null"); return (Criteria) this; } public Criteria andCreateTimeEqualTo(Date value) { addCriterion("create_time =", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotEqualTo(Date value) { addCriterion("create_time <>", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeGreaterThan(Date value) { addCriterion("create_time >", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeGreaterThanOrEqualTo(Date value) { addCriterion("create_time >=", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeLessThan(Date value) { addCriterion("create_time <", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeLessThanOrEqualTo(Date value) { addCriterion("create_time <=", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeIn(List values) { addCriterion("create_time in", values, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotIn(List values) { addCriterion("create_time not in", values, "createTime"); return (Criteria) this; } public Criteria andCreateTimeBetween(Date value1, Date value2) { addCriterion("create_time between", value1, value2, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotBetween(Date value1, Date value2) { addCriterion("create_time not between", value1, value2, "createTime"); return (Criteria) this; } public Criteria andStatusIsNull() { addCriterion("status is null"); return (Criteria) this; } public Criteria andStatusIsNotNull() { addCriterion("status is not null"); return (Criteria) this; } public Criteria andStatusEqualTo(Integer value) { addCriterion("status =", value, "status"); return (Criteria) this; } public Criteria andStatusNotEqualTo(Integer value) { addCriterion("status <>", value, "status"); return (Criteria) this; } public Criteria andStatusGreaterThan(Integer value) { addCriterion("status >", value, "status"); return (Criteria) this; } public Criteria andStatusGreaterThanOrEqualTo(Integer value) { addCriterion("status >=", value, "status"); return (Criteria) this; } public Criteria andStatusLessThan(Integer value) { addCriterion("status <", value, "status"); return (Criteria) this; } public Criteria andStatusLessThanOrEqualTo(Integer value) { addCriterion("status <=", value, "status"); return (Criteria) this; } public Criteria andStatusIn(List values) { addCriterion("status in", values, "status"); return (Criteria) this; } public Criteria andStatusNotIn(List values) { addCriterion("status not in", values, "status"); return (Criteria) this; } public Criteria andStatusBetween(Integer value1, Integer value2) { addCriterion("status between", value1, value2, "status"); return (Criteria) this; } public Criteria andStatusNotBetween(Integer value1, Integer value2) { addCriterion("status not between", value1, value2, "status"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/service/UmsAdminService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.domain.AdminRoleDto; import com.macro.mall.tiny.domain.ResourceWithCateDto; import com.macro.mall.tiny.domain.RoleStatDto; import com.macro.mall.tiny.mbg.model.UmsAdmin; import java.util.List; /** * @auther macrozheng * @description 后台用户管理Service * @date 2020/12/8 * @github https://github.com/macrozheng */ public interface UmsAdminService { void create(UmsAdmin entity); void update(UmsAdmin entity); void delete(Long id); UmsAdmin select(Long id); List listAll(Integer pageNum, Integer pageSize); List list(Integer pageNum, Integer pageSize, String username, List statusList); List subList(Long roleId); List groupList(); void deleteByUsername(String username); void updateByIds(List ids,Integer status); AdminRoleDto selectWithRoleList(Long id); ResourceWithCateDto selectResourceWithCate(Long id); } ================================================ FILE: mall-tiny-generator/src/main/java/com/macro/mall/tiny/service/impl/UmsAdminServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.util.StrUtil; import com.github.pagehelper.PageHelper; import com.macro.mall.tiny.dao.UmsAdminDao; import com.macro.mall.tiny.domain.AdminRoleDto; import com.macro.mall.tiny.domain.ResourceWithCateDto; import com.macro.mall.tiny.domain.RoleStatDto; 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.service.UmsAdminService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * @auther macrozheng * @description 后台用户管理Service实现类 * @date 2020/12/8 * @github https://github.com/macrozheng */ @Service public class UmsAdminServiceImpl implements UmsAdminService { @Autowired private UmsAdminMapper adminMapper; @Autowired private UmsAdminDao adminDao; @Override public void create(UmsAdmin entity) { adminMapper.insert(entity); } @Override public void update(UmsAdmin entity) { adminMapper.updateByPrimaryKeySelective(entity); } @Override public void delete(Long id) { adminMapper.deleteByPrimaryKey(id); } @Override public UmsAdmin select(Long id) { return adminMapper.selectByPrimaryKey(id); } @Override public List listAll(Integer pageNum, Integer pageSize) { PageHelper.startPage(pageNum, pageSize); return adminMapper.selectByExample(new UmsAdminExample()); } @Override public List list(Integer pageNum, Integer pageSize, String username, List statusList) { PageHelper.startPage(pageNum, pageSize); UmsAdminExample umsAdminExample = new UmsAdminExample(); UmsAdminExample.Criteria criteria = umsAdminExample.createCriteria(); if(StrUtil.isNotEmpty(username)){ criteria.andUsernameEqualTo(username); } criteria.andStatusIn(statusList); umsAdminExample.setOrderByClause("create_time desc"); return adminMapper.selectByExample(umsAdminExample); } @Override public List subList(Long roleId) { return adminDao.subList(roleId); } @Override public List groupList() { return adminDao.groupList(); } @Override public void deleteByUsername(String username) { UmsAdminExample example = new UmsAdminExample(); example.createCriteria().andUsernameEqualTo(username); adminMapper.deleteByExample(example); } @Override public void updateByIds(List ids, Integer status) { UmsAdmin record = new UmsAdmin(); record.setStatus(status); UmsAdminExample example = new UmsAdminExample(); example.createCriteria().andIdIn(ids); adminMapper.updateByExampleSelective(record,example); } @Override public AdminRoleDto selectWithRoleList(Long id) { return adminDao.selectWithRoleList(id); } @Override public ResourceWithCateDto selectResourceWithCate(Long id) { return adminDao.selectResourceWithCate(id); } } ================================================ FILE: mall-tiny-generator/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ant_path_matcher mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml logging: level: root: info com.macro.mall: debug ================================================ FILE: mall-tiny-generator/src/main/resources/com/macro/mall/tiny/mbg/mapper/UmsAdminMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, username, password, icon, email, nick_name, note, create_time, login_time, status delete from ums_admin where id = #{id,jdbcType=BIGINT} delete from ums_admin SELECT LAST_INSERT_ID() insert into ums_admin (username, password, icon, email, nick_name, note, create_time, login_time, status ) values (#{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{icon,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{nickName,jdbcType=VARCHAR}, #{note,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, #{loginTime,jdbcType=TIMESTAMP}, #{status,jdbcType=INTEGER} ) SELECT LAST_INSERT_ID() insert into ums_admin username, password, icon, email, nick_name, note, create_time, login_time, status, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{icon,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{nickName,jdbcType=VARCHAR}, #{note,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, #{loginTime,jdbcType=TIMESTAMP}, #{status,jdbcType=INTEGER}, update ums_admin id = #{row.id,jdbcType=BIGINT}, username = #{row.username,jdbcType=VARCHAR}, password = #{row.password,jdbcType=VARCHAR}, icon = #{row.icon,jdbcType=VARCHAR}, email = #{row.email,jdbcType=VARCHAR}, nick_name = #{row.nickName,jdbcType=VARCHAR}, note = #{row.note,jdbcType=VARCHAR}, create_time = #{row.createTime,jdbcType=TIMESTAMP}, login_time = #{row.loginTime,jdbcType=TIMESTAMP}, status = #{row.status,jdbcType=INTEGER}, update ums_admin set id = #{row.id,jdbcType=BIGINT}, username = #{row.username,jdbcType=VARCHAR}, password = #{row.password,jdbcType=VARCHAR}, icon = #{row.icon,jdbcType=VARCHAR}, email = #{row.email,jdbcType=VARCHAR}, nick_name = #{row.nickName,jdbcType=VARCHAR}, note = #{row.note,jdbcType=VARCHAR}, create_time = #{row.createTime,jdbcType=TIMESTAMP}, login_time = #{row.loginTime,jdbcType=TIMESTAMP}, status = #{row.status,jdbcType=INTEGER} update ums_admin username = #{username,jdbcType=VARCHAR}, password = #{password,jdbcType=VARCHAR}, icon = #{icon,jdbcType=VARCHAR}, email = #{email,jdbcType=VARCHAR}, nick_name = #{nickName,jdbcType=VARCHAR}, note = #{note,jdbcType=VARCHAR}, create_time = #{createTime,jdbcType=TIMESTAMP}, login_time = #{loginTime,jdbcType=TIMESTAMP}, status = #{status,jdbcType=INTEGER}, where id = #{id,jdbcType=BIGINT} update ums_admin set username = #{username,jdbcType=VARCHAR}, password = #{password,jdbcType=VARCHAR}, icon = #{icon,jdbcType=VARCHAR}, email = #{email,jdbcType=VARCHAR}, nick_name = #{nickName,jdbcType=VARCHAR}, note = #{note,jdbcType=VARCHAR}, create_time = #{createTime,jdbcType=TIMESTAMP}, login_time = #{loginTime,jdbcType=TIMESTAMP}, status = #{status,jdbcType=INTEGER} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-generator/src/main/resources/com/macro/mall/tiny/mbg/mapper/UmsAdminRoleRelationMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, admin_id, role_id delete from ums_admin_role_relation where id = #{id,jdbcType=BIGINT} delete from ums_admin_role_relation SELECT LAST_INSERT_ID() insert into ums_admin_role_relation (admin_id, role_id) values (#{adminId,jdbcType=BIGINT}, #{roleId,jdbcType=BIGINT}) SELECT LAST_INSERT_ID() insert into ums_admin_role_relation admin_id, role_id, #{adminId,jdbcType=BIGINT}, #{roleId,jdbcType=BIGINT}, update ums_admin_role_relation id = #{row.id,jdbcType=BIGINT}, admin_id = #{row.adminId,jdbcType=BIGINT}, role_id = #{row.roleId,jdbcType=BIGINT}, update ums_admin_role_relation set id = #{row.id,jdbcType=BIGINT}, admin_id = #{row.adminId,jdbcType=BIGINT}, role_id = #{row.roleId,jdbcType=BIGINT} update ums_admin_role_relation admin_id = #{adminId,jdbcType=BIGINT}, role_id = #{roleId,jdbcType=BIGINT}, where id = #{id,jdbcType=BIGINT} update ums_admin_role_relation set admin_id = #{adminId,jdbcType=BIGINT}, role_id = #{roleId,jdbcType=BIGINT} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-generator/src/main/resources/com/macro/mall/tiny/mbg/mapper/UmsResourceCategoryMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, create_time, name, sort delete from ums_resource_category where id = #{id,jdbcType=BIGINT} delete from ums_resource_category SELECT LAST_INSERT_ID() insert into ums_resource_category (create_time, name, sort ) values (#{createTime,jdbcType=TIMESTAMP}, #{name,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER} ) SELECT LAST_INSERT_ID() insert into ums_resource_category create_time, name, sort, #{createTime,jdbcType=TIMESTAMP}, #{name,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, update ums_resource_category id = #{row.id,jdbcType=BIGINT}, create_time = #{row.createTime,jdbcType=TIMESTAMP}, name = #{row.name,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, update ums_resource_category set id = #{row.id,jdbcType=BIGINT}, create_time = #{row.createTime,jdbcType=TIMESTAMP}, name = #{row.name,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER} update ums_resource_category create_time = #{createTime,jdbcType=TIMESTAMP}, name = #{name,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, where id = #{id,jdbcType=BIGINT} update ums_resource_category set create_time = #{createTime,jdbcType=TIMESTAMP}, name = #{name,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-generator/src/main/resources/com/macro/mall/tiny/mbg/mapper/UmsResourceMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, create_time, name, url, description, category_id delete from ums_resource where id = #{id,jdbcType=BIGINT} delete from ums_resource SELECT LAST_INSERT_ID() insert into ums_resource (create_time, name, url, description, category_id) values (#{createTime,jdbcType=TIMESTAMP}, #{name,jdbcType=VARCHAR}, #{url,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}, #{categoryId,jdbcType=BIGINT}) SELECT LAST_INSERT_ID() insert into ums_resource create_time, name, url, description, category_id, #{createTime,jdbcType=TIMESTAMP}, #{name,jdbcType=VARCHAR}, #{url,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}, #{categoryId,jdbcType=BIGINT}, update ums_resource id = #{row.id,jdbcType=BIGINT}, create_time = #{row.createTime,jdbcType=TIMESTAMP}, name = #{row.name,jdbcType=VARCHAR}, url = #{row.url,jdbcType=VARCHAR}, description = #{row.description,jdbcType=VARCHAR}, category_id = #{row.categoryId,jdbcType=BIGINT}, update ums_resource set id = #{row.id,jdbcType=BIGINT}, create_time = #{row.createTime,jdbcType=TIMESTAMP}, name = #{row.name,jdbcType=VARCHAR}, url = #{row.url,jdbcType=VARCHAR}, description = #{row.description,jdbcType=VARCHAR}, category_id = #{row.categoryId,jdbcType=BIGINT} update ums_resource create_time = #{createTime,jdbcType=TIMESTAMP}, name = #{name,jdbcType=VARCHAR}, url = #{url,jdbcType=VARCHAR}, description = #{description,jdbcType=VARCHAR}, category_id = #{categoryId,jdbcType=BIGINT}, where id = #{id,jdbcType=BIGINT} update ums_resource set create_time = #{createTime,jdbcType=TIMESTAMP}, name = #{name,jdbcType=VARCHAR}, url = #{url,jdbcType=VARCHAR}, description = #{description,jdbcType=VARCHAR}, category_id = #{categoryId,jdbcType=BIGINT} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-generator/src/main/resources/com/macro/mall/tiny/mbg/mapper/UmsRoleMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, description, admin_count, create_time, status, sort delete from ums_role where id = #{id,jdbcType=BIGINT} delete from ums_role SELECT LAST_INSERT_ID() insert into ums_role (name, description, admin_count, create_time, status, sort ) values (#{name,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}, #{adminCount,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP}, #{status,jdbcType=INTEGER}, #{sort,jdbcType=INTEGER} ) SELECT LAST_INSERT_ID() insert into ums_role name, description, admin_count, create_time, status, sort, #{name,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}, #{adminCount,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP}, #{status,jdbcType=INTEGER}, #{sort,jdbcType=INTEGER}, update ums_role id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, description = #{row.description,jdbcType=VARCHAR}, admin_count = #{row.adminCount,jdbcType=INTEGER}, create_time = #{row.createTime,jdbcType=TIMESTAMP}, status = #{row.status,jdbcType=INTEGER}, sort = #{row.sort,jdbcType=INTEGER}, update ums_role set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, description = #{row.description,jdbcType=VARCHAR}, admin_count = #{row.adminCount,jdbcType=INTEGER}, create_time = #{row.createTime,jdbcType=TIMESTAMP}, status = #{row.status,jdbcType=INTEGER}, sort = #{row.sort,jdbcType=INTEGER} update ums_role name = #{name,jdbcType=VARCHAR}, description = #{description,jdbcType=VARCHAR}, admin_count = #{adminCount,jdbcType=INTEGER}, create_time = #{createTime,jdbcType=TIMESTAMP}, status = #{status,jdbcType=INTEGER}, sort = #{sort,jdbcType=INTEGER}, where id = #{id,jdbcType=BIGINT} update ums_role set name = #{name,jdbcType=VARCHAR}, description = #{description,jdbcType=VARCHAR}, admin_count = #{adminCount,jdbcType=INTEGER}, create_time = #{createTime,jdbcType=TIMESTAMP}, status = #{status,jdbcType=INTEGER}, sort = #{sort,jdbcType=INTEGER} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-generator/src/main/resources/dao/UmsAdminDao.xml ================================================ ================================================ FILE: mall-tiny-generator/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-generator/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-generator/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-hutool/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-hutool/pom.xml ================================================ 4.0.0 mall-tiny-hutool 1.0-SNAPSHOT mall-tiny-hutool Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test io.springfox springfox-boot-starter ${springfox-swagger.version} cn.hutool hutool-all ${hutool.version} org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-hutool/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-hutool/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * 通用返回对象 * Created by macro on 2019/4/19. */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-hutool/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * 封装API的错误码 * Created by macro on 2019/4/19. */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-hutool/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * 枚举了一些常用API操作码 * Created by macro on 2019/4/19. */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-hutool/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger文档的配置 * @date 2022/11/22 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("SwaggerUI演示") .description("mall-tiny") .contact(new Contact("macro", null, null)) .version("1.0") .build(); } @Bean public BeanPostProcessor generateBeanPostProcessor(){ return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-hutool/src/main/java/com/macro/mall/tiny/controller/HutoolController.java ================================================ package com.macro.mall.tiny.controller; import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.LineCaptcha; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DateField; import cn.hutool.core.date.DateUnit; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.resource.ClassPathResource; import cn.hutool.core.lang.Validator; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.digest.BCrypt; import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONUtil; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.domain.PmsBrand; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.*; /** * @auther macrozheng * @description Hutool工具类测试 * @date 2019/9/1 * @github https://github.com/macrozheng */ @RestController @Api(tags = "HutoolController") @Tag(name = "HutoolController", description = "Hutool工具类测试") @RequestMapping("/hutool") public class HutoolController { private static Logger LOGGER = LoggerFactory.getLogger(HutoolController.class); @ApiOperation("Convert使用:类型转换工具类") @GetMapping(value = "/covert") public CommonResult covert() { //转换为字符串 int a = 1; String aStr = Convert.toStr(a); //转换为指定类型数组 String[] b = {"1", "2", "3", "4"}; Integer[] bArr = Convert.toIntArray(b); //转换为日期对象 String dateStr = "2017-05-06"; Date date = Convert.toDate(dateStr); //转换为列表 String[] strArr = {"a", "b", "c", "d"}; List strList = Convert.toList(String.class, strArr); return CommonResult.success(null, "操作成功"); } @ApiOperation("DateUtil使用:日期时间工具") @GetMapping(value = "/dateUtil") public CommonResult dateUtil() { //Date、long、Calendar之间的相互转换 //当前时间 Date date = DateUtil.date(); //Calendar转Date date = DateUtil.date(Calendar.getInstance()); //时间戳转Date date = DateUtil.date(System.currentTimeMillis()); //自动识别格式转换 String dateStr = "2017-03-01"; date = DateUtil.parse(dateStr); //自定义格式化转换 date = DateUtil.parse(dateStr, "yyyy-MM-dd"); //格式化输出日期 String format = DateUtil.format(date, "yyyy-MM-dd"); //获得年的部分 int year = DateUtil.year(date); //获得月份,从0开始计数 int month = DateUtil.month(date); //获取某天的开始、结束时间 Date beginOfDay = DateUtil.beginOfDay(date); Date endOfDay = DateUtil.endOfDay(date); //计算偏移后的日期时间 Date newDate = DateUtil.offset(date, DateField.DAY_OF_MONTH, 2); //计算日期时间之间的偏移量 long betweenDay = DateUtil.between(date, newDate, DateUnit.DAY); return CommonResult.success(null, "操作成功"); } @ApiOperation("StrUtil使用:字符串工具") @GetMapping(value = "/strUtil") public CommonResult strUtil() { //判断是否为空字符串 String str = "test"; StrUtil.isEmpty(str); StrUtil.isNotEmpty(str); //去除字符串的前后缀 StrUtil.removeSuffix("a.jpg", ".jpg"); StrUtil.removePrefix("a.jpg", "a."); //格式化字符串 String template = "这只是个占位符:{}"; String str2 = StrUtil.format(template, "我是占位符"); LOGGER.info("strUtil format:{}", str2); return CommonResult.success(null, "操作成功"); } @ApiOperation("ClassPath单一资源访问类:在classPath下查找文件") @GetMapping("/classPath") public CommonResult classPath() throws IOException { //获取定义在src/main/resources文件夹中的配置文件 ClassPathResource resource = new ClassPathResource("generator.properties"); Properties properties = new Properties(); properties.load(resource.getStream()); LOGGER.info("classPath:{}", properties); return CommonResult.success(null, "操作成功"); } @ApiOperation("ReflectUtil使用:Java反射工具类") @GetMapping("/reflectUtil") public CommonResult reflectUtil() { //获取某个类的所有方法 Method[] methods = ReflectUtil.getMethods(PmsBrand.class); //获取某个类的指定方法 Method method = ReflectUtil.getMethod(PmsBrand.class, "getId"); //使用反射来创建对象 PmsBrand pmsBrand = ReflectUtil.newInstance(PmsBrand.class); //反射执行对象的方法 ReflectUtil.invoke(pmsBrand, "setId", 1L); return CommonResult.success(null, "操作成功"); } @ApiOperation("NumberUtil使用:数字处理工具类") @GetMapping("/numberUtil") public CommonResult numberUtil() { double n1 = 1.234; double n2 = 1.234; double result; //对float、double、BigDecimal做加减乘除操作 result = NumberUtil.add(n1, n2); result = NumberUtil.sub(n1, n2); result = NumberUtil.mul(n1, n2); result = NumberUtil.div(n1, n2); //保留两位小数 BigDecimal roundNum = NumberUtil.round(n1, 2); String n3 = "1.234"; //判断是否为数字、整数、浮点数 NumberUtil.isNumber(n3); NumberUtil.isInteger(n3); NumberUtil.isDouble(n3); return CommonResult.success(null, "操作成功"); } @ApiOperation("BeanUtil使用:JavaBean的工具类") @GetMapping("/beanUtil") public CommonResult beanUtil() { PmsBrand brand = new PmsBrand(); brand.setId(1L); brand.setName("小米"); brand.setShowStatus(0); //Bean转Map Map map = BeanUtil.beanToMap(brand); LOGGER.info("beanUtil bean to map:{}", map); //Map转Bean PmsBrand mapBrand = BeanUtil.toBean(map, PmsBrand.class); LOGGER.info("beanUtil map to bean:{}", mapBrand); //Bean属性拷贝 PmsBrand copyBrand = new PmsBrand(); BeanUtil.copyProperties(brand, copyBrand); LOGGER.info("beanUtil copy properties:{}", copyBrand); return CommonResult.success(null, "操作成功"); } @ApiOperation("CollUtil使用:集合工具类") @GetMapping("/collUtil") public CommonResult collUtil() { //数组转换为列表 String[] array = new String[]{"a", "b", "c", "d", "e"}; List list = CollUtil.newArrayList(array); //join:数组转字符串时添加连接符号 String joinStr = CollUtil.join(list, ","); LOGGER.info("collUtil join:{}", joinStr); //将以连接符号分隔的字符串再转换为列表 List splitList = StrUtil.split(joinStr, ','); LOGGER.info("collUtil split:{}", splitList); //创建新的Set、List HashSet newHashSet = CollUtil.newHashSet(); ArrayList newList = CollUtil.newArrayList(); //判断列表是否为空 CollUtil.isEmpty(list); CollUtil.isNotEmpty(list); return CommonResult.success(null, "操作成功"); } @ApiOperation("MapUtil使用:Map工具类") @GetMapping("/mapUtil") public CommonResult mapUtil() { //将多个键值对加入到Map中 Map map = MapUtil.of(new String[][]{ {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"} }); //判断Map是否为空 MapUtil.isEmpty(map); MapUtil.isNotEmpty(map); return CommonResult.success(null, "操作成功"); } @ApiOperation("AnnotationUtil使用:注解工具类") @GetMapping("/annotationUtil") public CommonResult annotationUtil() { //获取指定类、方法、字段、构造器上的注解列表 Annotation[] annotationList = AnnotationUtil.getAnnotations(HutoolController.class, false); LOGGER.info("annotationUtil annotations:{}", annotationList); //获取指定类型注解 Tag tag = AnnotationUtil.getAnnotation(HutoolController.class, Tag.class); LOGGER.info("annotationUtil tag value:{}", tag.description()); //获取指定类型注解的值 Object annotationValue = AnnotationUtil.getAnnotationValue(HutoolController.class, RequestMapping.class); LOGGER.info("annotationUtil annotationValue:{}", annotationValue); return CommonResult.success(null, "操作成功"); } @ApiOperation("JSONUtil使用:JSON解析工具类") @GetMapping("/jsonUtil") public CommonResult jsonUtil() { PmsBrand brand = new PmsBrand(); brand.setId(1L); brand.setName("小米"); brand.setShowStatus(1); //对象转化为JSON字符串 String jsonStr = JSONUtil.parse(brand).toString(); LOGGER.info("jsonUtil parse:{}", jsonStr); //JSON字符串转化为对象 PmsBrand brandBean = JSONUtil.toBean(jsonStr, PmsBrand.class); LOGGER.info("jsonUtil toBean:{}", brandBean); List brandList = new ArrayList<>(); brandList.add(brand); String jsonListStr = JSONUtil.parse(brandList).toString(); //JSON字符串转化为列表 brandList = JSONUtil.toList(new JSONArray(jsonListStr), PmsBrand.class); LOGGER.info("jsonUtil toList:{}", brandList); return CommonResult.success(null, "操作成功"); } @ApiOperation("SecureUtil使用:加密解密工具类") @GetMapping("/secureUtil") public CommonResult secureUtil() { //MD5加密 String str = "123456"; String md5Str = SecureUtil.md5(str); LOGGER.info("secureUtil md5:{}", md5Str); return CommonResult.success(null, "操作成功"); } @ApiOperation("BCrypt使用:BCrypt加密工具类") @GetMapping("/bCrypt") public CommonResult bCrypt() { //BCrypt加密 String str = "123456"; String bCryptStr = BCrypt.hashpw(str); LOGGER.info("bCrypt hashpw:{}", bCryptStr); //BCrypt校验 boolean result = BCrypt.checkpw(str, bCryptStr); LOGGER.info("bCrypt checkpw:{}", result); return CommonResult.success(null, "操作成功"); } @ApiOperation("CaptchaUtil使用:图形验证码") @GetMapping("/captchaUtil") public void captchaUtil(HttpServletRequest request, HttpServletResponse response) { //生成验证码图片 LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100); try { request.getSession().setAttribute("CAPTCHA_KEY", lineCaptcha.getCode()); response.setContentType("image/png");//告诉浏览器输出内容为图片 response.setHeader("Pragma", "No-cache");//禁止浏览器缓存 response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expire", 0); lineCaptcha.write(response.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } } @ApiOperation("Validator使用:字段验证器") @GetMapping("/validator") public CommonResult validator() { //判断是否为邮箱地址 boolean result = Validator.isEmail("macro@qq.com"); LOGGER.info("Validator isEmail:{}", result); //判断是否为手机号码 result = Validator.isMobile("18911111111"); LOGGER.info("Validator isMobile:{}", result); //判断是否为IPV4地址 result = Validator.isIpv4("192.168.3.101"); LOGGER.info("Validator isIpv4:{}", result); //判断是否为汉字 result = Validator.isChinese("你好"); LOGGER.info("Validator isChinese:{}", result); //判断是否为身份证号码(18位中国) result = Validator.isCitizenId("123456"); LOGGER.info("Validator isCitizenId:{}", result); //判断是否为URL result = Validator.isUrl("http://www.baidu.com"); LOGGER.info("Validator isUrl:{}", result); //判断是否为生日 result = Validator.isBirthday("2020-02-01"); LOGGER.info("Validator isBirthday:{}", result); return CommonResult.success(null, "操作成功"); } @ApiOperation("DigestUtil使用:摘要算法工具类") @GetMapping("/digestUtil") public CommonResult digestUtil() { String password = "123456"; //计算MD5摘要值,并转为16进制字符串 String result = DigestUtil.md5Hex(password); LOGGER.info("DigestUtil md5Hex:{}", result); //计算SHA-256摘要值,并转为16进制字符串 result = DigestUtil.sha256Hex(password); LOGGER.info("DigestUtil sha256Hex:{}", result); //生成Bcrypt加密后的密文,并校验 String hashPwd = DigestUtil.bcrypt(password); boolean check = DigestUtil.bcryptCheck(password,hashPwd); LOGGER.info("DigestUtil bcryptCheck:{}", check); return CommonResult.success(null, "操作成功"); } @ApiOperation("HttpUtil使用:Http请求工具类") @GetMapping("/httpUtil") public CommonResult httpUtil() { String response = HttpUtil.get("http://localhost:8080/hutool/covert"); LOGGER.info("HttpUtil get:{}", response); return CommonResult.success(null, "操作成功"); } } ================================================ FILE: mall-tiny-hutool/src/main/java/com/macro/mall/tiny/domain/PmsBrand.java ================================================ package com.macro.mall.tiny.domain; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { private Long id; private String name; @ApiModelProperty(value = "首字母") private String firstLetter; private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-hutool/src/main/resources/application.yml ================================================ server: port: 8080 spring: mvc: pathmatch: matching-strategy: ant_path_matcher ================================================ FILE: mall-tiny-hutool/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-hutool/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-jenkins/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-jenkins/pom.xml ================================================ 4.0.0 com.macro.mall mall-tiny-jenkins 1.0-SNAPSHOT mall-tiny-jenkins Demo project for Spring Boot UTF-8 UTF-8 1.8 true http://192.168.3.101:2375 0.40.2 1.4.5 5.3.2 1.2.14 5.8.9 3.0.0 1.6.0 1.6.0 1.4.1 3.5.10 2.2.2 8.0.29 2.7.5 0.9.1 2.5.0 7.2 8.4.5 2.3.1 org.springframework.boot spring-boot-starter-parent 2.7.5 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} io.springfox springfox-boot-starter ${springfox-swagger.version} javax.xml.bind jaxb-api ${jaxb-api.version} org.springframework.boot spring-boot-maven-plugin io.fabric8 docker-maven-plugin ${docker.maven.plugin.version} build-image package build ${docker.host} mall-tiny/${project.name}:${project.version} openjdk:8 ${project.build.finalName}.jar / artifact ["java", "-jar","-Dspring.profiles.active=prod","/${project.build.finalName}.jar"] macrozheng ================================================ FILE: mall-tiny-jenkins/src/main/docker/Dockerfile ================================================ # 该镜像需要依赖的基础镜像 FROM openjdk:8 # 将当前目录下的jar包复制到docker容器的/目录下 ADD mall-tiny-docker-1.0-SNAPSHOT.jar /mall-tiny-docker-1.0-SNAPSHOT.jar # 声明服务运行在8080端口 EXPOSE 8080 # 指定docker容器启动时运行jar包 ENTRYPOINT ["java", "-jar","-Dspring.profiles.active=prod","/mall-tiny-docker-1.0-SNAPSHOT.jar"] # 指定维护者的名字 MAINTAINER macrozheng ================================================ FILE: mall-tiny-jenkins/src/main/docker/docker-compose.yml ================================================ version: '3' services: # 指定服务名称 db: # 指定服务使用的镜像 image: mysql:5.7 # 指定容器名称 container_name: mysql # 指定服务运行的端口 ports: - 3306:3306 # 指定容器中需要挂载的文件 volumes: - /mydata/mysql/log:/var/log/mysql - /mydata/mysql/data:/var/lib/mysql - /mydata/mysql/conf:/etc/mysql # 指定容器的环境变量 environment: - MYSQL_ROOT_PASSWORD=root # 指定服务名称 mall-tiny-docker: # 指定服务使用的镜像 image: mall-tiny/mall-tiny-docker:1.0-SNAPSHOT # 指定容器名称 container_name: mall-tiny-docker # 指定服务运行的端口 ports: - 8080:8080 # 指定容器中需要挂载的文件 volumes: - /etc/localtime:/etc/localtime - /mydata/app/mall-tiny-docker/logs:/var/logs ================================================ FILE: mall-tiny-jenkins/src/main/docker/mall-tiny-jenkins.sh ================================================ #!/usr/bin/env bash app_name='mall-tiny-jenkins' docker stop ${app_name} echo '----stop container----' docker rm ${app_name} echo '----rm container----' docker rmi `docker images | grep none | awk '{print $3}'` echo '----rm none images----' docker run -p 8088:8088 --name ${app_name} \ --link mysql:db \ -v /etc/localtime:/etc/localtime \ -v /mydata/app/${app_name}/logs:/var/logs \ -d mall-tiny/${app_name}:1.0-SNAPSHOT echo '----start container----' ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan("com.macro.mall.tiny.mbg.mapper") public class MyBatisConfig { } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger相关配置 * @date 2022/11/23 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("SwaggerUI演示") .description("mall-tiny") .contact(new Contact("macro", null, null)) .version("1.0") .build(); } @Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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)); } } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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 MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { long countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand row); int insertSelective(PmsBrand row); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExample(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand row); int updateByPrimaryKeyWithBLOBs(PmsBrand row); int updateByPrimaryKey(PmsBrand row); } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { @ApiModelProperty(value = "主键ID") private Long id; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "首字母") private String firstLetter; @ApiModelProperty(value = "排序") private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; @ApiModelProperty(value = "是否显示") private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-jenkins/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-jenkins/src/main/resources/application-prod.yml ================================================ server: port: 8088 spring: datasource: url: jdbc:mysql://db:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root ================================================ FILE: mall-tiny-jenkins/src/main/resources/application.yml ================================================ server: port: 8088 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml ================================================ FILE: mall-tiny-jenkins/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-jenkins/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-jenkins/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-jenkins/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-lombok/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-lombok/pom.xml ================================================ 4.0.0 mall-tiny-lombok 1.0-SNAPSHOT mall-tiny-lombok Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 org.projectlombok lombok true org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/BuilderExample.java ================================================ package com.macro.mall.tiny.example; import lombok.Builder; import lombok.ToString; /** * @auther macrozheng * @description @Builder注解使用示例 * @date 2020/12/17 * @github https://github.com/macrozheng */ @Builder @ToString public class BuilderExample { private Long id; private String name; private Integer age; public static void main(String[] args) { BuilderExample example = BuilderExample.builder() .id(1L) .name("test") .age(20) .build(); System.out.println(example); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/CleanupExample.java ================================================ package com.macro.mall.tiny.example; import lombok.Cleanup; import java.io.*; /** * @auther macrozheng * @description @Cleanup注解使用示例 * @date 2020/12/16 * @github https://github.com/macrozheng */ public class CleanupExample { public static void main(String[] args) throws IOException { String inStr = "Hello World!"; //使用输入输出流自动关闭,无需编写try catch和调用close()方法 @Cleanup ByteArrayInputStream in = new ByteArrayInputStream(inStr.getBytes("UTF-8")); @Cleanup ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] b = new byte[1024]; while (true) { int r = in.read(b); if (r == -1) break; out.write(b, 0, r); } String outStr = out.toString("UTF-8"); System.out.println(outStr); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/ConstructorExample.java ================================================ package com.macro.mall.tiny.example; import lombok.*; /** * @auther macrozheng * @description @XxConstructor注解使用示例 * @date 2020/12/17 * @github https://github.com/macrozheng */ @NoArgsConstructor @RequiredArgsConstructor(staticName = "of") @AllArgsConstructor public class ConstructorExample { @NonNull private Long id; private String name; private Integer age; public static void main(String[] args) { //无参构造器 ConstructorExample example1 = new ConstructorExample(); //全部参数构造器 ConstructorExample example2 = new ConstructorExample(1L,"test",20); //@NonNull注解的必须参数构造器 ConstructorExample example3 = ConstructorExample.of(1L); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/DataExample.java ================================================ package com.macro.mall.tiny.example; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NonNull; /** * @auther macrozheng * @description @Data注解使用示例 * @date 2020/12/17 * @github https://github.com/macrozheng */ @Data public class DataExample { @NonNull private Long id; @EqualsAndHashCode.Exclude private String name; @EqualsAndHashCode.Exclude private Integer age; public static void main(String[] args) { //@RequiredArgsConstructor已生效 DataExample example1 = new DataExample(1L); //@Getter @Setter已生效 example1.setName("test"); example1.setAge(20); //@ToString已生效 System.out.println(example1); DataExample example2 = new DataExample(1L); //@EqualsAndHashCode已生效 System.out.println(example1.equals(example2)); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/EqualsAndHashCodeExample.java ================================================ package com.macro.mall.tiny.example; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; /** * @auther macrozheng * @description @EqualsAndHashCode使用示例 * @date 2020/12/17 * @github https://github.com/macrozheng */ @Getter @Setter @EqualsAndHashCode public class EqualsAndHashCodeExample { private Long id; @EqualsAndHashCode.Exclude private String name; @EqualsAndHashCode.Exclude private Integer age; public static void main(String[] args) { EqualsAndHashCodeExample example1 = new EqualsAndHashCodeExample(); example1.setId(1L); example1.setName("test"); example1.setAge(20); EqualsAndHashCodeExample example2 = new EqualsAndHashCodeExample(); example2.setId(1L); //equals方法只对比id,返回true System.out.println(example1.equals(example2)); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/GetterLazyExample.java ================================================ package com.macro.mall.tiny.example; import lombok.Getter; /** * @auther macrozheng * @description @Getter注解实现属性懒加载 * @date 2020/12/17 * @github https://github.com/macrozheng */ public class GetterLazyExample { @Getter(lazy = true) private final double[] cached = expensive(); private double[] expensive() { double[] result = new double[1000000]; for (int i = 0; i < result.length; i++) { result[i] = Math.asin(i); } return result; } public static void main(String[] args) { //使用Double Check Lock 样板代码对属性进行懒加载 GetterLazyExample example = new GetterLazyExample(); System.out.println(example.getCached().length); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/GetterSetterExample.java ================================================ package com.macro.mall.tiny.example; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; /** * @auther macrozheng * @description @Getter @Setter注解使用示例 * @date 2020/12/17 * @github https://github.com/macrozheng */ public class GetterSetterExample { @Getter @Setter private String name; @Getter @Setter(AccessLevel.PROTECTED) private Integer age; public static void main(String[] args) { GetterSetterExample example = new GetterSetterExample(); example.setName("test"); example.setAge(20); System.out.printf("name:%s age:%d",example.getName(),example.getAge()); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/LogExample.java ================================================ package com.macro.mall.tiny.example; import lombok.extern.java.Log; /** * @auther macrozheng * @description @Log注解使用示例 * @date 2020/12/17 * @github https://github.com/macrozheng */ @Log public class LogExample { public static void main(String[] args) { log.info("level info"); log.warning("level warning"); log.severe("level severe"); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/LogSlf4jExample.java ================================================ package com.macro.mall.tiny.example; import lombok.extern.slf4j.Slf4j; /** * @auther macrozheng * @description @Slf4j注解使用示例 * @date 2020/12/17 * @github https://github.com/macrozheng */ @Slf4j public class LogSlf4jExample { public static void main(String[] args) { log.info("level:{}","info"); log.warn("level:{}","warn"); log.error("level:{}", "error"); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/NonNullExample.java ================================================ package com.macro.mall.tiny.example; import lombok.NonNull; /** * @auther macrozheng * @description @NonNull注解使用示例 * @date 2020/12/16 * @github https://github.com/macrozheng */ public class NonNullExample { private String name; public NonNullExample(@NonNull String name){ this.name = name; } public static void main(String[] args) { new NonNullExample("test"); //会抛出NullPointerException new NonNullExample(null); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/SneakyThrowsExample.java ================================================ package com.macro.mall.tiny.example; import lombok.SneakyThrows; import java.io.UnsupportedEncodingException; /** * @auther macrozheng * @description @SneakyThrows注解使用示例 * @date 2020/12/17 * @github https://github.com/macrozheng */ public class SneakyThrowsExample { //自动抛出异常,无需处理 @SneakyThrows(UnsupportedEncodingException.class) public static byte[] str2byte(String str){ return str.getBytes("UTF-8"); } public static void main(String[] args) { String str = "Hello World!"; System.out.println(str2byte(str).length); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/SynchronizedExample.java ================================================ package com.macro.mall.tiny.example; import lombok.*; /** * @auther macrozheng * @description @Synchronized注解使用示例 * @date 2020/12/17 * @github https://github.com/macrozheng */ @Data public class SynchronizedExample { @NonNull private Integer count; @Synchronized @SneakyThrows public void reduceCount(Integer id) { if (count > 0) { Thread.sleep(500); count--; System.out.println(String.format("thread-%d count:%d", id, count)); } } public static void main(String[] args) { //添加@Synchronized三个线程可以同步调用reduceCount方法 SynchronizedExample example = new SynchronizedExample(20); new ReduceThread(1, example).start(); new ReduceThread(2, example).start(); new ReduceThread(3, example).start(); } @RequiredArgsConstructor static class ReduceThread extends Thread { @NonNull private Integer id; @NonNull private SynchronizedExample example; @Override public void run() { while (example.getCount() > 0) { example.reduceCount(id); } } } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/ToStringExample.java ================================================ package com.macro.mall.tiny.example; import lombok.ToString; /** * @auther macrozheng * @description @ToString注解使用示例 * @date 2020/12/17 * @github https://github.com/macrozheng */ @ToString public class ToStringExample { @ToString.Exclude private Long id; private String name; private Integer age; public ToStringExample(Long id,String name,Integer age){ this.id =id; this.name = name; this.age = age; } public static void main(String[] args) { ToStringExample example = new ToStringExample(1L,"test",20); //自动实现toString方法,输出ToStringExample(name=test, age=20) System.out.println(example); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/ValExample.java ================================================ package com.macro.mall.tiny.example; import lombok.val; import java.util.ArrayList; import java.util.HashMap; /** * @auther macrozheng * @description val注解使用示例 * @date 2020/12/16 * @github https://github.com/macrozheng */ public class ValExample { public static void example() { //val代替ArrayList和String类型 val example = new ArrayList(); example.add("Hello World!"); val foo = example.get(0); System.out.println(foo.toLowerCase()); } public static void example2() { //val代替Map.Entry类型 val map = new HashMap(); map.put(0, "zero"); map.put(5, "five"); for (val entry : map.entrySet()) { System.out.printf("%d: %s\n", entry.getKey(), entry.getValue()); } } public static void main(String[] args) { example(); example2(); } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/ValueExample.java ================================================ package com.macro.mall.tiny.example; import lombok.Value; /** * @auther macrozheng * @description @Value注解使用示例 * @date 2020/12/17 * @github https://github.com/macrozheng */ @Value public class ValueExample { private Long id; private String name; private Integer age; public static void main(String[] args) { //只能使用全参构造器 ValueExample example = new ValueExample(1L,"test",20); // example.setName("andy") //没有生成setter方法,会报错 // example.name="andy" //字段被设置为final类型,会报错 } } ================================================ FILE: mall-tiny-lombok/src/main/java/com/macro/mall/tiny/example/WithExample.java ================================================ package com.macro.mall.tiny.example; import lombok.AllArgsConstructor; import lombok.With; /** * @auther macrozheng * @description @With注解使用示例 * @date 2020/12/17 * @github https://github.com/macrozheng */ @With @AllArgsConstructor public class WithExample { private Long id; private String name; private Integer age; public static void main(String[] args) { WithExample example1 = new WithExample(1L, "test", 20); WithExample example2 = example1.withAge(22); //将原对象进行clone并设置age,返回false System.out.println(example1.equals(example2)); } } ================================================ FILE: mall-tiny-lombok/src/main/resources/application.yml ================================================ server: port: 8080 ================================================ FILE: mall-tiny-lombok/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-mybatis/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-mybatis/pom.xml ================================================ 4.0.0 mall-tiny-mybatis 1.0-SNAPSHOT mall-tiny-mybatis Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 cn.hutool hutool-all ${hutool.version} org.mybatis.spring.boot mybatis-spring-boot-starter ${mybatis-starter.version} com.github.pagehelper pagehelper-spring-boot-starter ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.projectlombok lombok true org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan("com.macro.mall.tiny.dao") public class MyBatisConfig { } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger2API文档的配置 * @date 2022/11/21 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .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(new Contact("macro", null, null)) .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; } @Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/dao/UmsAdminDao.java ================================================ package com.macro.mall.tiny.dao; import com.macro.mall.tiny.model.UmsAdmin; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.util.List; /** * @auther macrozheng * @description 自定义UmsAdmin表查询 * @date 2022/10/20 * @github https://github.com/macrozheng */ @Repository public interface UmsAdminDao { /** * 根据ID查询用户 */ UmsAdmin selectByIdSimple(Long id); /** * 根据ID查询用户 */ UmsAdmin selectById(Long id); /** * 根据用户ID批量查询 */ List selectByIds(@Param("ids") List ids); /** * 插入用户 */ int insert(UmsAdmin entity); /** * 批量插入用户 */ int insertBatch(@Param("entityList") List adminList); /** * 根据ID修改用户信息 */ int updateById(UmsAdmin entity); /** * 根据ID选择性修改用户信息 */ int updateByIdSelective(UmsAdmin entity); /** * 根据ID删除用户 */ int deleteById(Long id); /** * 根据用户名和Email模糊查询用户 * 不输入查询所有 */ List selectByUsernameAndEmailLike(@Param("username") String username,@Param("email") String email); /** * 根据用户名和Email模糊查询用户 * 不输入不返回数据 */ List selectByUsernameAndEmailLike2(@Param("username") String username,@Param("email") String email); /** * 根据用户名和Email模糊查询用户 * 不输入查询所有 */ List selectByUsernameAndEmailLike3(@Param("username") String username,@Param("email") String email); } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/dao/UmsResourceCategoryDao.java ================================================ package com.macro.mall.tiny.dao; import com.macro.mall.tiny.domain.UmsResourceCategoryExt; import org.springframework.stereotype.Repository; /** * @auther macrozheng * @description 自定义UmsResourceCategory表查询 * @date 2022/10/20 * @github https://github.com/macrozheng */ @Repository public interface UmsResourceCategoryDao { /** * 根据分类ID获取分类及对应资源 */ UmsResourceCategoryExt selectCategoryWithResource(Long id); } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/dao/UmsResourceDao.java ================================================ package com.macro.mall.tiny.dao; import com.macro.mall.tiny.domain.UmsResourceExt; import com.macro.mall.tiny.model.UmsResource; import org.springframework.stereotype.Repository; import java.util.List; /** * @auther macrozheng * @description 自定义UmsResource表查询 * @date 2022/10/20 * @github https://github.com/macrozheng */ @Repository public interface UmsResourceDao { /** * 根据资源ID获取资源及分类信息 */ UmsResourceExt selectResourceWithCategory(Long id); /** * 根据资源ID获取资源及分类信息 */ UmsResourceExt selectResourceWithCategory2(Long id); /** * 根据分类ID查询资源 */ List selectListByCategoryId(Long categoryId); } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/domain/UmsResourceCategoryExt.java ================================================ package com.macro.mall.tiny.domain; import com.macro.mall.tiny.model.UmsResource; import com.macro.mall.tiny.model.UmsResourceCategory; import lombok.Data; import java.util.List; /** * @auther macrozheng * @description UmsResourceCategory扩展类 * @date 2022/10/20 * @github https://github.com/macrozheng */ @Data public class UmsResourceCategoryExt extends UmsResourceCategory { private List resourceList; } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/domain/UmsResourceExt.java ================================================ package com.macro.mall.tiny.domain; import com.macro.mall.tiny.model.UmsResource; import com.macro.mall.tiny.model.UmsResourceCategory; import lombok.Data; /** * @auther macrozheng * @description UmsResource扩展类 * @date 2022/10/20 * @github https://github.com/macrozheng */ @Data public class UmsResourceExt extends UmsResource { private UmsResourceCategory category; } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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)){ //数据库中特殊字符需要转义 if(remarks.contains("\"")){ remarks = remarks.replace("\"","'"); } //给model的字段添加swagger注解 field.addJavaDocLine("@ApiModelProperty(value = \""+remarks+"\")"); } } @Override public void addJavaFileComment(CompilationUnit compilationUnit) { super.addJavaFileComment(compilationUnit); //只在model中添加swagger注解类的导入 String fullyQualifiedName = compilationUnit.getType().getFullyQualifiedName(); if(!fullyQualifiedName.contains(MAPPER_SUFFIX)&&!fullyQualifiedName.contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/model/UmsAdmin.java ================================================ package com.macro.mall.tiny.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Date; public class UmsAdmin implements Serializable { private Long id; private String username; private String password; @ApiModelProperty(value = "头像") private String icon; @ApiModelProperty(value = "邮箱") private String email; @ApiModelProperty(value = "昵称") private String nickName; @ApiModelProperty(value = "备注信息") private String note; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "最后登录时间") private Date loginTime; @ApiModelProperty(value = "帐号启用状态:0->禁用;1->启用") private Integer status; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getIcon() { return icon; } public void setIcon(String icon) { this.icon = icon; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public String getNote() { return note; } public void setNote(String note) { this.note = note; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Date getLoginTime() { return loginTime; } public void setLoginTime(Date loginTime) { this.loginTime = loginTime; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", username=").append(username); sb.append(", password=").append(password); sb.append(", icon=").append(icon); sb.append(", email=").append(email); sb.append(", nickName=").append(nickName); sb.append(", note=").append(note); sb.append(", createTime=").append(createTime); sb.append(", loginTime=").append(loginTime); sb.append(", status=").append(status); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/model/UmsAdminLoginLog.java ================================================ package com.macro.mall.tiny.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Date; public class UmsAdminLoginLog implements Serializable { private Long id; private Long adminId; private Date createTime; private String ip; private String address; @ApiModelProperty(value = "浏览器登录类型") private String userAgent; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getAdminId() { return adminId; } public void setAdminId(Long adminId) { this.adminId = adminId; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getUserAgent() { return userAgent; } public void setUserAgent(String userAgent) { this.userAgent = userAgent; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", adminId=").append(adminId); sb.append(", createTime=").append(createTime); sb.append(", ip=").append(ip); sb.append(", address=").append(address); sb.append(", userAgent=").append(userAgent); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/model/UmsAdminRoleRelation.java ================================================ package com.macro.mall.tiny.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class UmsAdminRoleRelation implements Serializable { private Long id; private Long adminId; private Long roleId; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getAdminId() { return adminId; } public void setAdminId(Long adminId) { this.adminId = adminId; } public Long getRoleId() { return roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", adminId=").append(adminId); sb.append(", roleId=").append(roleId); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/model/UmsMenu.java ================================================ package com.macro.mall.tiny.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Date; public class UmsMenu implements Serializable { private Long id; @ApiModelProperty(value = "父级ID") private Long parentId; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "菜单名称") private String title; @ApiModelProperty(value = "菜单级数") private Integer level; @ApiModelProperty(value = "菜单排序") private Integer sort; @ApiModelProperty(value = "前端名称") private String name; @ApiModelProperty(value = "前端图标") private String icon; @ApiModelProperty(value = "前端隐藏") private Integer hidden; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getParentId() { return parentId; } public void setParentId(Long parentId) { this.parentId = parentId; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Integer getLevel() { return level; } public void setLevel(Integer level) { this.level = level; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getIcon() { return icon; } public void setIcon(String icon) { this.icon = icon; } public Integer getHidden() { return hidden; } public void setHidden(Integer hidden) { this.hidden = hidden; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", parentId=").append(parentId); sb.append(", createTime=").append(createTime); sb.append(", title=").append(title); sb.append(", level=").append(level); sb.append(", sort=").append(sort); sb.append(", name=").append(name); sb.append(", icon=").append(icon); sb.append(", hidden=").append(hidden); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/model/UmsResource.java ================================================ package com.macro.mall.tiny.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Date; public class UmsResource implements Serializable { private Long id; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "资源名称") private String name; @ApiModelProperty(value = "资源URL") private String url; @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "资源分类ID") private Long categoryId; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", createTime=").append(createTime); sb.append(", name=").append(name); sb.append(", url=").append(url); sb.append(", description=").append(description); sb.append(", categoryId=").append(categoryId); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/model/UmsResourceCategory.java ================================================ package com.macro.mall.tiny.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Date; public class UmsResourceCategory implements Serializable { private Long id; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "分类名称") private String name; @ApiModelProperty(value = "排序") private Integer sort; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", createTime=").append(createTime); sb.append(", name=").append(name); sb.append(", sort=").append(sort); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/model/UmsRole.java ================================================ package com.macro.mall.tiny.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Date; public class UmsRole implements Serializable { private Long id; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "描述") private String description; @ApiModelProperty(value = "后台用户数量") private Integer adminCount; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "启用状态:0->禁用;1->启用") private Integer status; private Integer sort; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Integer getAdminCount() { return adminCount; } public void setAdminCount(Integer adminCount) { this.adminCount = adminCount; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", description=").append(description); sb.append(", adminCount=").append(adminCount); sb.append(", createTime=").append(createTime); sb.append(", status=").append(status); sb.append(", sort=").append(sort); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/model/UmsRoleMenuRelation.java ================================================ package com.macro.mall.tiny.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class UmsRoleMenuRelation implements Serializable { private Long id; @ApiModelProperty(value = "角色ID") private Long roleId; @ApiModelProperty(value = "菜单ID") private Long menuId; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getRoleId() { return roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } public Long getMenuId() { return menuId; } public void setMenuId(Long menuId) { this.menuId = menuId; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", roleId=").append(roleId); sb.append(", menuId=").append(menuId); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/model/UmsRoleResourceRelation.java ================================================ package com.macro.mall.tiny.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class UmsRoleResourceRelation implements Serializable { private Long id; @ApiModelProperty(value = "角色ID") private Long roleId; @ApiModelProperty(value = "资源ID") private Long resourceId; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getRoleId() { return roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } public Long getResourceId() { return resourceId; } public void setResourceId(Long resourceId) { this.resourceId = resourceId; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", roleId=").append(roleId); sb.append(", resourceId=").append(resourceId); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/service/UmsResourceService.java ================================================ package com.macro.mall.tiny.service; import com.github.pagehelper.PageInfo; import com.macro.mall.tiny.model.UmsResource; /** * @auther macrozheng * @description UmsResource的Service接口 * @date 2022/10/20 * @github https://github.com/macrozheng */ public interface UmsResourceService { PageInfo page(Integer pageNum, Integer pageSize,Long categoryId); } ================================================ FILE: mall-tiny-mybatis/src/main/java/com/macro/mall/tiny/service/impl/UmsResourceServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.macro.mall.tiny.dao.UmsResourceDao; import com.macro.mall.tiny.model.UmsResource; import com.macro.mall.tiny.service.UmsResourceService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * @auther macrozheng * @description UmsResource的Service接口实现类 * @date 2022/10/20 * @github https://github.com/macrozheng */ @Service public class UmsResourceServiceImpl implements UmsResourceService { @Autowired private UmsResourceDao umsResourceDao; @Override public PageInfo page(Integer pageNum, Integer pageSize,Long categoryId) { PageHelper.startPage(pageNum,pageSize); List resourceList = umsResourceDao.selectListByCategoryId(categoryId); PageInfo pageInfo = new PageInfo<>(resourceList); return pageInfo; } } ================================================ FILE: mall-tiny-mybatis/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER mybatis: mapper-locations: - classpath:dao/*.xml configuration: # 下划线自动转驼峰 map-underscore-to-camel-case: true logging: level: root: info com.macro.mall: debug ================================================ FILE: mall-tiny-mybatis/src/main/resources/dao/UmsAdminDao.xml ================================================ insert into ums_admin(username, password, icon, email, nick_name, note, create_time, login_time) values (#{username}, #{password}, #{icon}, #{email}, #{nickName}, #{note}, #{createTime}, #{loginTime}) SELECT LAST_INSERT_ID() insert into ums_admin(username, password, icon, email, nick_name, note, create_time, login_time) values (#{item.username}, #{item.password}, #{item.icon}, #{item.email}, #{item.nickName}, #{item.note}, #{item.createTime}, #{item.loginTime}) update ums_admin set username = #{username}, password = #{password}, icon = #{icon}, email = #{email}, nick_name = #{nickName}, note = #{note}, create_time = #{createTime}, login_time = #{loginTime} where id = #{id} update ums_admin username = #{username}, password = #{password}, icon = #{icon}, email = #{email}, nick_name = #{nickName}, note = #{note}, create_time = #{createTime}, login_time = #{loginTime}, where id = #{id} delete from ums_admin where id = #{id} ================================================ FILE: mall-tiny-mybatis/src/main/resources/dao/UmsResourceCategoryDao.xml ================================================ ================================================ FILE: mall-tiny-mybatis/src/main/resources/dao/UmsResourceDao.xml ================================================ ================================================ FILE: mall-tiny-mybatis/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-mybatis/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-mybatis/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit4.SpringRunner; @ExtendWith(SpringExtension.class) @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-mybatis/src/test/java/com/macro/mall/tiny/test/MyBatisAdvanceTest.java ================================================ package com.macro.mall.tiny.test; import com.github.pagehelper.PageInfo; import com.macro.mall.tiny.dao.UmsResourceCategoryDao; import com.macro.mall.tiny.dao.UmsResourceDao; import com.macro.mall.tiny.domain.UmsResourceCategoryExt; import com.macro.mall.tiny.domain.UmsResourceExt; import com.macro.mall.tiny.model.UmsResource; import com.macro.mall.tiny.service.UmsResourceService; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; /** * @auther macrozheng * @description MyBatis高级查询测试 * @date 2022/10/20 * @github https://github.com/macrozheng */ @SpringBootTest public class MyBatisAdvanceTest { private static final Logger LOGGER = LoggerFactory.getLogger(MyBatisAdvanceTest.class); @Autowired private UmsResourceDao umsResourceDao; @Autowired private UmsResourceCategoryDao umsResourceCategoryDao; @Autowired private UmsResourceService umsResourceService; @Test void testOneToOne(){ UmsResourceExt umsResourceExt = umsResourceDao.selectResourceWithCategory(1L); LOGGER.info("testOneToOne category={}",umsResourceExt.getCategory()); } @Test void testAssociation(){ UmsResourceExt umsResourceExt = umsResourceDao.selectResourceWithCategory2(1L); LOGGER.info("testAssociation category={}",umsResourceExt.getCategory()); } @Test void testOneToMany(){ UmsResourceCategoryExt umsResourceCategoryExt = umsResourceCategoryDao.selectCategoryWithResource(1L); LOGGER.info("testOneToMany resourceList={}",umsResourceCategoryExt.getResourceList()); } @Test void testPage() { int pageNum = 1; int pageSize = 5; PageInfo pageInfo = umsResourceService.page(pageNum, pageSize, 1L); LOGGER.info("testPage total={},pages={},resourceList={}", pageInfo.getTotal(), pageInfo.getPages(), pageInfo.getList()); } } ================================================ FILE: mall-tiny-mybatis/src/test/java/com/macro/mall/tiny/test/MyBatisBaseTest.java ================================================ package com.macro.mall.tiny.test; import cn.hutool.crypto.digest.BCrypt; import com.macro.mall.tiny.dao.UmsAdminDao; import com.macro.mall.tiny.model.UmsAdmin; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.Date; /** * @auther macrozheng * @description MyBatis基本操作测试 * @date 2022/10/20 * @github https://github.com/macrozheng */ @SpringBootTest public class MyBatisBaseTest { private static final Logger LOGGER = LoggerFactory.getLogger(MyBatisBaseTest.class); @Autowired private UmsAdminDao umsAdminDao; @Test void testSelectByIdSimple(){ UmsAdmin umsAdmin = umsAdminDao.selectByIdSimple(1L); LOGGER.info("testSelectByIdSimple result={}",umsAdmin); } @Test void testSelectById(){ UmsAdmin umsAdmin = umsAdminDao.selectById(1L); LOGGER.info("testSelectById result={}",umsAdmin); } @Test void testInsert(){ UmsAdmin admin = new UmsAdmin(); admin.setUsername("newTest"); admin.setPassword(BCrypt.hashpw("123456")); admin.setEmail("newTest@qq.com"); admin.setNickName("tester"); admin.setCreateTime(new Date()); admin.setStatus(1); int result = umsAdminDao.insert(admin); LOGGER.info("testInsert id={},result={}",admin.getId(),result); } @Test void testUpdateById(){ UmsAdmin admin = new UmsAdmin(); admin.setId(1L); admin.setUsername("test666"); admin.setPassword(BCrypt.hashpw("123456")); admin.setEmail("test666@qq.com"); admin.setNickName("tester"); admin.setCreateTime(new Date()); admin.setStatus(1); int result = umsAdminDao.updateById(admin); LOGGER.info("testUpdateById result={}",result); } @Test void testDeleteById(){ int result = umsAdminDao.deleteById(14L); LOGGER.info("testDeleteById result={}",result); } } ================================================ FILE: mall-tiny-mybatis/src/test/java/com/macro/mall/tiny/test/MyBatisTagTest.java ================================================ package com.macro.mall.tiny.test; import cn.hutool.core.util.RandomUtil; import com.macro.mall.tiny.dao.UmsAdminDao; import com.macro.mall.tiny.model.UmsAdmin; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.ArrayList; import java.util.List; /** * @auther macrozheng * @description MyBatis标签测试 * @date 2022/10/20 * @github https://github.com/macrozheng */ @SpringBootTest public class MyBatisTagTest { private static final Logger LOGGER = LoggerFactory.getLogger(MyBatisTagTest.class); @Autowired private UmsAdminDao umsAdminDao; @Test void testIf(){ List userList = umsAdminDao.selectByUsernameAndEmailLike("test","test"); LOGGER.info("testIf result={}",userList); } @Test void testChoose(){ List userList = umsAdminDao.selectByUsernameAndEmailLike2(null,null); LOGGER.info("testChoose result={}",userList); } @Test void testWhere(){ List userList = umsAdminDao.selectByUsernameAndEmailLike3("test","test"); LOGGER.info("testWhere result={}",userList); } @Test void testSet(){ UmsAdmin umsAdmin = new UmsAdmin(); umsAdmin.setId(1L); umsAdmin.setUsername("test"); umsAdmin.setNickName("new test"); int result = umsAdminDao.updateByIdSelective(umsAdmin); LOGGER.info("testSet result={}",result); } @Test void testForeach(){ List adminList = new ArrayList<>(); adminList.add(randomAdmin()); adminList.add(randomAdmin()); int result = umsAdminDao.insertBatch(adminList); LOGGER.info("testForeach result={}",result); } @Test void testForeach2(){ List ids = new ArrayList<>(); ids.add(1L); ids.add(3L); List adminList = umsAdminDao.selectByIds(ids); LOGGER.info("testForeach2 result={}",adminList); } UmsAdmin randomAdmin() { UmsAdmin admin = new UmsAdmin(); String randomNumbers = RandomUtil.randomNumbers(5); admin.setUsername("test" + randomNumbers); admin.setNickName("test" + randomNumbers); admin.setStatus(1); return admin; } } ================================================ FILE: mall-tiny-plus/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-plus/pom.xml ================================================ 4.0.0 mall-tiny-plus 1.0-SNAPSHOT mall-tiny-plus Demo project for MyBatis-Plus com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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.alibaba druid-spring-boot-starter ${druid.version} mysql mysql-connector-java ${mysql-connector.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.projectlombok lombok true cn.hutool hutool-all ${hutool.version} com.baomidou mybatis-plus-boot-starter 3.5.3 com.baomidou mybatis-plus-generator 3.5.3 org.apache.velocity velocity-engine-core 2.3 org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import cn.hutool.core.convert.Convert; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将MyBatis Plus 分页结果转化为通用结果 */ public static CommonPage restPage(Page pageResult) { CommonPage result = new CommonPage<>(); result.setPageNum(Convert.toInt(pageResult.getCurrent())); result.setPageSize(Convert.toInt(pageResult.getSize())); result.setTotal(pageResult.getTotal()); result.setTotalPage(Convert.toInt(pageResult.getTotal()/pageResult.getSize()+1)); result.setList(pageResult.getRecords()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @EnableTransactionManagement @MapperScan({"com.macro.mall.tiny.modules.*.mapper"}) public class MyBatisConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } } ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger文档的配置 * @date 2022/11/22 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.modules")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("SwaggerUI演示") .description("mall-tiny") .contact(new Contact("macro", null, null)) .version("1.0") .build(); } @Bean public BeanPostProcessor generateBeanPostProcessor(){ return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/generator/MyBatisPlusGenerator.java ================================================ package com.macro.mall.tiny.generator; import cn.hutool.core.util.StrUtil; import cn.hutool.setting.dialect.Props; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.*; import com.baomidou.mybatisplus.generator.config.po.LikeTable; import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery; import com.baomidou.mybatisplus.generator.config.rules.DateType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine; import com.baomidou.mybatisplus.generator.query.SQLQuery; import java.util.Collections; import java.util.Scanner; /** * @auther macrozheng * @description MyBatisPlus代码生成器 * @date 2020/8/20 * @github https://github.com/macrozheng */ public class MyBatisPlusGenerator { public static void main(String[] args) { String projectPath = System.getProperty("user.dir") + "/mall-tiny-plus"; String moduleName = scanner("模块名"); String[] tableNames = scanner("表名,多个英文逗号分割").split(","); // 代码生成器 AutoGenerator autoGenerator = new AutoGenerator(initDataSourceConfig()); autoGenerator.global(initGlobalConfig(projectPath)); autoGenerator.packageInfo(initPackageConfig(projectPath,moduleName)); autoGenerator.injection(initInjectionConfig(projectPath, moduleName)); autoGenerator.template(initTemplateConfig()); autoGenerator.strategy(initStrategyConfig(tableNames)); autoGenerator.execute(new VelocityTemplateEngine()); } /** * 读取控制台内容信息 */ private static String scanner(String tip) { Scanner scanner = new Scanner(System.in); System.out.println(("请输入" + tip + ":")); if (scanner.hasNext()) { String next = scanner.next(); if (StrUtil.isNotEmpty(next)) { return next; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } /** * 初始化全局配置 */ private static GlobalConfig initGlobalConfig(String projectPath) { return new GlobalConfig.Builder() .outputDir(projectPath + "/src/main/java") .author("macro") .disableOpenDir() .enableSwagger() .dateType(DateType.ONLY_DATE) .build(); } /** * 初始化数据源配置 */ private static DataSourceConfig initDataSourceConfig() { Props props = new Props("generator.properties"); String url = props.getStr("dataSource.url"); String username = props.getStr("dataSource.username"); String password = props.getStr("dataSource.password"); return new DataSourceConfig.Builder(url,username,password) .dbQuery(new MySqlQuery()) .databaseQueryClass(SQLQuery.class) .build(); } /** * 初始化包配置 */ private static PackageConfig initPackageConfig(String projectPath,String moduleName) { Props props = new Props("generator.properties"); return new PackageConfig.Builder() .moduleName(moduleName) .parent(props.getStr("package.base")) .entity("model") .pathInfo(Collections.singletonMap(OutputFile.xml, projectPath + "/src/main/resources/mapper/" + moduleName)) .build(); } // /** // * 初始化模板配置 // */ // private static TemplateConfig initTemplateConfig() { // //可以对controller、service、entity模板进行配置 // return new TemplateConfig.Builder().build(); // } /** * 初始化模板配置 */ private static TemplateConfig initTemplateConfig() { //可以对controller、service、entity模板进行配置 return new TemplateConfig.Builder() .entity("templates/entity.java") .mapper("templates/mapper.java") .controller("templates/controller.java") .service("templates/service.java") .serviceImpl("templates/serviceImpl.java") .xml("/templates/mapper.xml") .build(); } /** * 初始化策略配置 */ private static StrategyConfig initStrategyConfig(String[] tableNames) { StrategyConfig.Builder builder = new StrategyConfig.Builder(); builder.entityBuilder() .enableFileOverride() .naming(NamingStrategy.underline_to_camel) .columnNaming(NamingStrategy.underline_to_camel) .enableLombok() .formatFileName("%s") .mapperBuilder() .enableFileOverride() .enableBaseResultMap() .formatMapperFileName("%sMapper") .formatXmlFileName("%sMapper") .serviceBuilder() .enableFileOverride() .formatServiceFileName("%sService") .formatServiceImplFileName("%sServiceImpl") .controllerBuilder() .enableRestStyle() .formatFileName("%sController"); //当表名中带*号时可以启用通配符模式 if (tableNames.length == 1 && tableNames[0].contains("*")) { String[] likeStr = tableNames[0].split("_"); String likePrefix = likeStr[0] + "_"; builder.likeTable(new LikeTable(likePrefix)); } else { builder.addInclude(tableNames); } return builder.build(); } /** * 初始化自定义配置 */ private static InjectionConfig initInjectionConfig(String projectPath, String moduleName) { // 自定义配置 return new InjectionConfig.Builder().build(); } } ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/modules/pms/controller/PmsBrandController.java ================================================ package com.macro.mall.tiny.modules.pms.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.macro.mall.tiny.common.api.CommonPage; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.modules.pms.model.PmsBrand; import com.macro.mall.tiny.modules.pms.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.web.bind.annotation.*; import java.util.List; /** *

* 品牌表 前端控制器 *

* * @author macro * @since 2020-08-20 */ @Api(tags = "PmsBrandController", description = "商品品牌管理") @RestController @RequestMapping("/brand") public class PmsBrandController { private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class); @Autowired private PmsBrandService brandService; @ApiOperation("获取所有品牌列表") @RequestMapping(value = "/listAll", method = RequestMethod.GET) @ResponseBody public CommonResult> getBrandList() { return CommonResult.success(brandService.list()); } @ApiOperation("添加品牌") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) { CommonResult commonResult; boolean result = brandService.save(pmsBrand); if (result) { 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", method = RequestMethod.POST) @ResponseBody public CommonResult updateBrand(@RequestBody PmsBrand pmsBrand) { CommonResult commonResult; boolean result = brandService.updateById(pmsBrand); if (result) { commonResult = CommonResult.success(pmsBrand); LOGGER.debug("updateBrand success:{}", pmsBrand); } else { commonResult = CommonResult.failed("操作失败"); LOGGER.debug("updateBrand failed:{}", pmsBrand); } return commonResult; } @ApiOperation("删除指定id的品牌") @RequestMapping(value = "/delete/{id}", method = RequestMethod.GET) @ResponseBody public CommonResult deleteBrand(@PathVariable("id") Long id) { boolean result = brandService.removeById(id); if (result) { 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) { Page page = new Page<>(pageNum, pageSize); Page pageResult = brandService.page(page); return CommonResult.success(CommonPage.restPage(pageResult)); } @ApiOperation("获取指定id的品牌详情") @RequestMapping(value = "/{id}", method = RequestMethod.GET) @ResponseBody public CommonResult brand(@PathVariable("id") Long id) { return CommonResult.success(brandService.getById(id)); } } ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/modules/pms/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.modules.pms.mapper; import com.macro.mall.tiny.modules.pms.model.PmsBrand; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** *

* 品牌表 Mapper 接口 *

* * @author macro * @since 2023-02-07 */ public interface PmsBrandMapper extends BaseMapper { } ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/modules/pms/model/PmsBrand.java ================================================ package com.macro.mall.tiny.modules.pms.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import java.io.Serializable; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; /** *

* 品牌表 *

* * @author macro * @since 2023-02-07 */ @Getter @Setter @TableName("pms_brand") @ApiModel(value = "PmsBrand对象", description = "品牌表") public class PmsBrand implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Long id; private String name; @ApiModelProperty("首字母") private String firstLetter; private Integer sort; @ApiModelProperty("是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; private Integer showStatus; @ApiModelProperty("产品数量") private Integer productCount; @ApiModelProperty("产品评论数量") private Integer productCommentCount; @ApiModelProperty("品牌logo") private String logo; @ApiModelProperty("专区大图") private String bigPic; @ApiModelProperty("品牌故事") private String brandStory; } ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/modules/pms/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.modules.pms.service; import com.macro.mall.tiny.modules.pms.model.PmsBrand; import com.baomidou.mybatisplus.extension.service.IService; /** *

* 品牌表 服务类 *

* * @author macro * @since 2023-02-07 */ public interface PmsBrandService extends IService { } ================================================ FILE: mall-tiny-plus/src/main/java/com/macro/mall/tiny/modules/pms/service/impl/PmsBrandServiceImpl.java ================================================ package com.macro.mall.tiny.modules.pms.service.impl; import com.macro.mall.tiny.modules.pms.model.PmsBrand; import com.macro.mall.tiny.modules.pms.mapper.PmsBrandMapper; import com.macro.mall.tiny.modules.pms.service.PmsBrandService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; /** *

* 品牌表 服务实现类 *

* * @author macro * @since 2023-02-07 */ @Service public class PmsBrandServiceImpl extends ServiceImpl implements PmsBrandService { } ================================================ FILE: mall-tiny-plus/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ant_path_matcher mybatis-plus: mapper-locations: classpath:/mapper/**/*.xml #指定mapper.xml路径 global-config: db-config: id-type: auto #全局默认主键类型设置为自增 configuration: auto-mapping-behavior: partial #只对非嵌套的 resultMap 进行自动映射 map-underscore-to-camel-case: true #开启自动驼峰命名规则映射 ================================================ FILE: mall-tiny-plus/src/main/resources/generator.properties ================================================ dataSource.url=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai dataSource.driverName=com.mysql.cj.jdbc.Driver dataSource.username=root dataSource.password=root package.base=com.macro.mall.tiny.modules ================================================ FILE: mall-tiny-plus/src/main/resources/mapper/pms/PmsBrandMapper.xml ================================================ ================================================ FILE: mall-tiny-plus/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-rabbit/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-rabbit/pom.xml ================================================ 4.0.0 mall-tiny-rabbit 1.0-SNAPSHOT mall-tiny-rabbit Demo project for RabbitMQ com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 io.springfox springfox-boot-starter ${springfox-swagger.version} cn.hutool hutool-all ${hutool.version} org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger2API文档的配置 * @date 2022/11/21 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .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(new Contact("macro", null, null)) .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; } @Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/controller/RabbitController.java ================================================ package com.macro.mall.tiny.controller; import cn.hutool.core.thread.ThreadUtil; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.direct.DirectSender; import com.macro.mall.tiny.fanout.FanoutSender; import com.macro.mall.tiny.simple.SimpleSender; import com.macro.mall.tiny.topic.TopicSender; import com.macro.mall.tiny.work.WorkSender; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; 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.ResponseBody; /** * @auther macrozheng * @description RabbitMQ功能测试Controller * @date 2020/5/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "RabbitController") @Tag(name = "RabbitController", description = "RabbitMQ功能测试") @RequestMapping("/rabbit") public class RabbitController { @Autowired private SimpleSender simpleSender; @Autowired private WorkSender workSender; @Autowired private FanoutSender fanoutSender; @Autowired private DirectSender directSender; @Autowired private TopicSender topicSender; @ApiOperation("简单模式") @RequestMapping(value = "/simple", method = RequestMethod.GET) @ResponseBody public CommonResult simpleTest() { for(int i=0;i<10;i++){ simpleSender.send(); ThreadUtil.sleep(1000); } return CommonResult.success(null); } @ApiOperation("工作模式") @RequestMapping(value = "/work", method = RequestMethod.GET) @ResponseBody public CommonResult workTest() { for(int i=0;i<10;i++){ workSender.send(i); ThreadUtil.sleep(1000); } return CommonResult.success(null); } @ApiOperation("发布/订阅模式") @RequestMapping(value = "/fanout", method = RequestMethod.GET) @ResponseBody public CommonResult fanoutTest() { for(int i=0;i<10;i++){ fanoutSender.send(i); ThreadUtil.sleep(1000); } return CommonResult.success(null); } @ApiOperation("路由模式") @RequestMapping(value = "/direct", method = RequestMethod.GET) @ResponseBody public CommonResult directTest() { for(int i=0;i<10;i++){ directSender.send(i); ThreadUtil.sleep(1000); } return CommonResult.success(null); } @ApiOperation("通配符模式") @RequestMapping(value = "/topic", method = RequestMethod.GET) @ResponseBody public CommonResult topicTest() { for(int i=0;i<10;i++){ topicSender.send(i); ThreadUtil.sleep(1000); } return CommonResult.success(null); } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/direct/DirectRabbitConfig.java ================================================ /* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.direct; import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; /** * @auther macrozheng * @description 路由模式消息队列配置 * @date 2020/5/19 * @github https://github.com/macrozheng */ @Configuration public class DirectRabbitConfig { @Bean public DirectExchange direct() { return new DirectExchange("exchange.direct"); } @Bean public Queue directQueue1() { return new AnonymousQueue(); } @Bean public Queue directQueue2() { return new AnonymousQueue(); } @Bean public Binding directBinding1a(DirectExchange direct, Queue directQueue1) { return BindingBuilder.bind(directQueue1).to(direct).with("orange"); } @Bean public Binding directBinding2a(DirectExchange direct, Queue directQueue2) { return BindingBuilder.bind(directQueue2).to(direct).with("green"); } @Bean public Binding directBinding2b(DirectExchange direct, Queue directQueue2) { return BindingBuilder.bind(directQueue2).to(direct).with("black"); } @Bean public DirectReceiver receiver() { return new DirectReceiver(); } @Bean public DirectSender directSender() { return new DirectSender(); } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/direct/DirectReceiver.java ================================================ /* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.direct; import cn.hutool.core.thread.ThreadUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.util.StopWatch; /** * @auther macrozheng * @description 路由模式消费者 * @date 2020/5/19 * @github https://github.com/macrozheng */ public class DirectReceiver { private static final Logger LOGGER = LoggerFactory.getLogger(DirectReceiver.class); @RabbitListener(queues = "#{directQueue1.name}") public void receive1(String in){ receive(in, 1); } @RabbitListener(queues = "#{directQueue2.name}") public void receive2(String in){ receive(in, 2); } private void receive(String in, int receiver){ StopWatch watch = new StopWatch(); watch.start(); LOGGER.info("instance {} [x] Received '{}'", receiver, in); doWork(in); watch.stop(); LOGGER.info("instance {} [x] Done in {}s", receiver, watch.getTotalTimeSeconds()); } private void doWork(String in){ for (char ch : in.toCharArray()) { if (ch == '.') { ThreadUtil.sleep(1000); } } } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/direct/DirectSender.java ================================================ /* * Copyright 2015-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.direct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; /** * @auther macrozheng * @description 路由模式生产者 * @date 2020/5/19 * @github https://github.com/macrozheng */ public class DirectSender { @Autowired private RabbitTemplate template; private static final String exchangeName = "exchange.direct"; private final String[] keys = {"orange", "black", "green"}; private static final Logger LOGGER = LoggerFactory.getLogger(DirectSender.class); public void send(int index) { StringBuilder builder = new StringBuilder("Hello to "); int limitIndex = index % 3; String key = keys[limitIndex]; builder.append(key).append(' '); builder.append(index+1); for (int i = 0; i <= limitIndex; i++) { builder.append('.'); } String message = builder.toString(); template.convertAndSend(exchangeName, key, message); LOGGER.info(" [x] Sent '{}'", message); } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/fanout/FanoutRabbitConfig.java ================================================ /* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.fanout; import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; /** * @auther macrozheng * @description 发布/订阅模式消息队列配置 * @date 2020/5/19 * @github https://github.com/macrozheng */ @Configuration public class FanoutRabbitConfig { @Bean public FanoutExchange fanout() { return new FanoutExchange("exchange.fanout"); } @Bean public Queue fanoutQueue1() { return new AnonymousQueue(); } @Bean public Queue fanoutQueue2() { return new AnonymousQueue(); } @Bean public Binding fanoutBinding1(FanoutExchange fanout, Queue fanoutQueue1) { return BindingBuilder.bind(fanoutQueue1).to(fanout); } @Bean public Binding fanoutBinding2(FanoutExchange fanout, Queue fanoutQueue2) { return BindingBuilder.bind(fanoutQueue2).to(fanout); } @Bean public FanoutReceiver fanoutReceiver() { return new FanoutReceiver(); } @Bean public FanoutSender fanoutSender() { return new FanoutSender(); } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/fanout/FanoutReceiver.java ================================================ /* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.fanout; import cn.hutool.core.thread.ThreadUtil; import com.macro.mall.tiny.work.WorkSender; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.util.StopWatch; /** * @auther macrozheng * @description 发布/订阅模式消费者 * @date 2020/5/19 * @github https://github.com/macrozheng */ public class FanoutReceiver { private static final Logger LOGGER = LoggerFactory.getLogger(FanoutReceiver.class); @RabbitListener(queues = "#{fanoutQueue1.name}") public void receive1(String in) { receive(in, 1); } @RabbitListener(queues = "#{fanoutQueue2.name}") public void receive2(String in) { receive(in, 2); } private void receive(String in, int receiver) { StopWatch watch = new StopWatch(); watch.start(); LOGGER.info("instance {} [x] Received '{}'", receiver, in); doWork(in); watch.stop(); LOGGER.info("instance {} [x] Done in {}s", receiver, watch.getTotalTimeSeconds()); } private void doWork(String in) { for (char ch : in.toCharArray()) { if (ch == '.') { ThreadUtil.sleep(1000); } } } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/fanout/FanoutSender.java ================================================ /* * Copyright 2015-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.fanout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; /** * @auther macrozheng * @description 发布/订阅模式生产者 * @date 2020/5/19 * @github https://github.com/macrozheng */ public class FanoutSender { private static final Logger LOGGER = LoggerFactory.getLogger(FanoutSender.class); @Autowired private RabbitTemplate template; private static final String exchangeName = "exchange.fanout"; public void send(int index) { StringBuilder builder = new StringBuilder("Hello"); int limitIndex = index % 3 + 1; for (int i = 0; i < limitIndex; i++) { builder.append('.'); } builder.append(index + 1); String message = builder.toString(); template.convertAndSend(exchangeName, "", message); LOGGER.info(" [x] Sent '{}'", message); } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/simple/SimpleRabbitConfig.java ================================================ /* * Copyright 2015-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.simple; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; /** * @auther macrozheng * @description 简单模式消息队列配置 * @date 2020/5/19 * @github https://github.com/macrozheng */ @Configuration public class SimpleRabbitConfig { @Bean public Queue hello() { return new Queue("simple.hello"); } @Bean public SimpleSender simpleSender(){ return new SimpleSender(); } @Bean public SimpleReceiver simpleReceiver(){ return new SimpleReceiver(); } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/simple/SimpleReceiver.java ================================================ /* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.simple; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; /** * @auther macrozheng * @description 简单模式消费者 * @date 2020/5/19 * @github https://github.com/macrozheng */ @RabbitListener(queues = "simple.hello") public class SimpleReceiver { private static final Logger LOGGER = LoggerFactory.getLogger(SimpleReceiver.class); @RabbitHandler public void receive(String in) { LOGGER.info(" [x] Received '{}'", in); } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/simple/SimpleSender.java ================================================ /* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.simple; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; /** * @auther macrozheng * @description 简单模式生产者 * @date 2020/5/19 * @github https://github.com/macrozheng */ public class SimpleSender { private static final Logger LOGGER = LoggerFactory.getLogger(SimpleSender.class); @Autowired private RabbitTemplate template; private static final String queueName="simple.hello"; public void send() { String message = "Hello World!"; this.template.convertAndSend(queueName, message); LOGGER.info(" [x] Sent '{}'", message); } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/topic/TopicRabbitConfig.java ================================================ /* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.topic; import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; /** * @auther macrozheng * @description 通配符模式消息队列配置 * @date 2020/5/19 * @github https://github.com/macrozheng */ @Configuration public class TopicRabbitConfig { @Bean public TopicExchange topic() { return new TopicExchange("exchange.topic"); } @Bean public Queue topicQueue1() { return new AnonymousQueue(); } @Bean public Queue topicQueue2() { return new AnonymousQueue(); } @Bean public Binding topicBinding1a(TopicExchange topic, Queue topicQueue1) { return BindingBuilder.bind(topicQueue1).to(topic).with("*.orange.*"); } @Bean public Binding topicBinding2a(TopicExchange topic, Queue topicQueue2) { return BindingBuilder.bind(topicQueue2).to(topic).with("*.*.rabbit"); } @Bean public Binding topicBinding2b(TopicExchange topic, Queue topicQueue2) { return BindingBuilder.bind(topicQueue2).to(topic).with("lazy.#"); } @Bean public TopicReceiver topicReceiver() { return new TopicReceiver(); } @Bean public TopicSender topicSender() { return new TopicSender(); } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/topic/TopicReceiver.java ================================================ /* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.topic; import cn.hutool.core.thread.ThreadUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.util.StopWatch; /** * @auther macrozheng * @description 通配符模式消费者 * @date 2020/5/19 * @github https://github.com/macrozheng */ public class TopicReceiver { private static final Logger LOGGER = LoggerFactory.getLogger(TopicReceiver.class); @RabbitListener(queues = "#{topicQueue1.name}") public void receive1(String in){ receive(in, 1); } @RabbitListener(queues = "#{topicQueue2.name}") public void receive2(String in){ receive(in, 2); } public void receive(String in, int receiver){ StopWatch watch = new StopWatch(); watch.start(); LOGGER.info("instance {} [x] Received '{}'", receiver, in); doWork(in); watch.stop(); LOGGER.info("instance {} [x] Done in {}s", receiver, watch.getTotalTimeSeconds()); } private void doWork(String in){ for (char ch : in.toCharArray()) { if (ch == '.') { ThreadUtil.sleep(1000); } } } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/topic/TopicSender.java ================================================ /* * Copyright 2015-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.topic; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; /** * @auther macrozheng * @description 通配符模式生产者 * @date 2020/5/19 * @github https://github.com/macrozheng */ public class TopicSender { @Autowired private RabbitTemplate template; private static final String exchangeName = "exchange.topic"; private static final Logger LOGGER = LoggerFactory.getLogger(TopicSender.class); private final String[] keys = {"quick.orange.rabbit", "lazy.orange.elephant", "quick.orange.fox", "lazy.brown.fox", "lazy.pink.rabbit", "quick.brown.fox"}; public void send(int index) { StringBuilder builder = new StringBuilder("Hello to "); int limitIndex = index%keys.length; String key = keys[limitIndex]; builder.append(key).append(' '); builder.append(index+1); for (int i = 0; i <= limitIndex; i++) { builder.append('.'); } String message = builder.toString(); template.convertAndSend(exchangeName, key, message); LOGGER.info(" [x] Sent '{}'",message); System.out.println(" [x] Sent '" + message + "'"); } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/work/WorkRabbitConfig.java ================================================ /* * Copyright 2015-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.work; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; /** * @auther macrozheng * @description 工作模式消息队列配置 * @date 2020/5/19 * @github https://github.com/macrozheng */ @Configuration public class WorkRabbitConfig { @Bean public Queue workQueue() { return new Queue("work.hello"); } @Bean public WorkReceiver workReceiver1() { return new WorkReceiver(1); } @Bean public WorkReceiver workReceiver2() { return new WorkReceiver(2); } @Bean public WorkSender workSender() { return new WorkSender(); } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/work/WorkReceiver.java ================================================ /* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.work; import cn.hutool.core.thread.ThreadUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.util.StopWatch; /** * @auther macrozheng * @description 工作模式消费者 * @date 2020/5/19 * @github https://github.com/macrozheng */ @RabbitListener(queues = "work.hello") public class WorkReceiver { private static final Logger LOGGER = LoggerFactory.getLogger(WorkReceiver.class); private final int instance; public WorkReceiver(int i) { this.instance = i; } @RabbitHandler public void receive(String in) { StopWatch watch = new StopWatch(); watch.start(); LOGGER.info("instance {} [x] Received '{}'", this.instance, in); doWork(in); watch.stop(); LOGGER.info("instance {} [x] Done in {}s", this.instance, watch.getTotalTimeSeconds()); } private void doWork(String in) { for (char ch : in.toCharArray()) { if (ch == '.') { ThreadUtil.sleep(1000); } } } } ================================================ FILE: mall-tiny-rabbit/src/main/java/com/macro/mall/tiny/work/WorkSender.java ================================================ /* * Copyright 2015-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.macro.mall.tiny.work; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; /** * @auther macrozheng * @description 工作模式生产者 * @date 2020/5/19 * @github https://github.com/macrozheng */ public class WorkSender { private static final Logger LOGGER = LoggerFactory.getLogger(WorkSender.class); @Autowired private RabbitTemplate template; private static final String queueName = "work.hello"; public void send(int index) { StringBuilder builder = new StringBuilder("Hello"); int limitIndex = index % 3+1; for (int i = 0; i < limitIndex; i++) { builder.append('.'); } builder.append(index+1); String message = builder.toString(); template.convertAndSend(queueName, message); LOGGER.info(" [x] Sent '{}'", message); } } ================================================ FILE: mall-tiny-rabbit/src/main/resources/application.yml ================================================ server: port: 8080 spring: mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER rabbitmq: host: localhost port: 5672 virtual-host: /mall username: mall password: mall publisher-returns: true #消息发送到队列确认 publisher-confirm-type: simple #消息发送到交换器确认 ================================================ FILE: mall-tiny-rabbit/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-redis/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-redis/pom.xml ================================================ 4.0.0 mall-tiny-redis 1.0-SNAPSHOT mall-tiny-redis Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} io.springfox springfox-boot-starter ${springfox-swagger.version} cn.hutool hutool-all ${hutool.version} org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/config/GlobalCorsConfig.java ================================================ package com.macro.mall.tiny.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; /** * @auther macrozheng * @description 全局跨域配置 * @date 2019/7/27 * @github https://github.com/macrozheng */ @Configuration public class GlobalCorsConfig { /** * 允许跨域调用的过滤器 */ @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); //允许所有域名进行跨域调用 config.addAllowedOrigin("*"); //允许跨越发送cookie config.setAllowCredentials(true); //放行全部原始头信息 config.addAllowedHeader("*"); //允许所有请求方法跨域调用 config.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan("com.macro.mall.tiny.mbg.mapper") public class MyBatisConfig { } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/config/RedisConfig.java ================================================ package com.macro.mall.tiny.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /** * @auther macrozheng * @description Redis配置类 * @date 2020/3/2 * @github https://github.com/macrozheng */ @EnableCaching @Configuration public class RedisConfig { /** * redis数据库自定义key */ public static final String REDIS_KEY_DATABASE="mall"; @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisSerializer serializer = redisSerializer(); RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(serializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(serializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public RedisSerializer redisSerializer() { //创建JSON序列化器 Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //必须设置,否则无法将JSON转化为对象,会转化成Map类型 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(objectMapper); return serializer; } @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); //设置Redis缓存有效期为1天 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1)); return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger文档的配置 * @date 2022/11/22 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("SwaggerUI演示") .description("mall-tiny") .contact(new Contact("macro", null, null)) .version("1.0") .build(); } @Bean public BeanPostProcessor generateBeanPostProcessor(){ return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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 io.swagger.v3.oas.annotations.tags.Tag; 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; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @RequestMapping("/brand") public class PmsBrandController { @Autowired private PmsBrandService brandService; private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class); @ApiOperation("添加品牌") @RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody public CommonResult create(@RequestBody PmsBrand pmsBrand) { CommonResult commonResult; int count = brandService.create(pmsBrand); if (count == 1) { commonResult = CommonResult.success(pmsBrand); LOGGER.debug("create success:{}", pmsBrand); } else { commonResult = CommonResult.failed("操作失败"); LOGGER.debug("create failed:{}", pmsBrand); } return commonResult; } @ApiOperation("更新指定id品牌信息") @RequestMapping(value = "/update/{id}", method = RequestMethod.POST) @ResponseBody public CommonResult update(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDto, BindingResult result) { CommonResult commonResult; int count = brandService.update(id, pmsBrandDto); if (count == 1) { commonResult = CommonResult.success(pmsBrandDto); LOGGER.debug("update success:{}", pmsBrandDto); } else { commonResult = CommonResult.failed("操作失败"); LOGGER.debug("update failed:{}", pmsBrandDto); } return commonResult; } @ApiOperation("删除指定id的品牌") @RequestMapping(value = "/delete/{id}", method = RequestMethod.GET) @ResponseBody public CommonResult delete(@PathVariable("id") Long id) { int count = brandService.delete(id); if (count == 1) { LOGGER.debug("delete success :id={}", id); return CommonResult.success(null); } else { LOGGER.debug("delete failed :id={}", id); return CommonResult.failed("操作失败"); } } @ApiOperation("获取指定id的品牌详情") @RequestMapping(value = "/{id}", method = RequestMethod.GET) @ResponseBody public CommonResult getItem(@PathVariable("id") Long id) { return CommonResult.success(brandService.getItem(id)); } @ApiOperation("分页查询品牌列表") @RequestMapping(value = "/list", method = RequestMethod.GET) @ResponseBody public CommonResult> list(@RequestParam(value = "pageNum", defaultValue = "1") @ApiParam("页码") Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "3") @ApiParam("每页数量") Integer pageSize) { List brandList = brandService.list(pageNum, pageSize); return CommonResult.success(CommonPage.restPage(brandList)); } @ApiOperation("获取所有品牌列表") @RequestMapping(value = "listAll", method = RequestMethod.GET) @ResponseBody public CommonResult> getAll() { return CommonResult.success(brandService.ListAll()); } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/controller/RedisController.java ================================================ package com.macro.mall.tiny.controller; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.service.PmsBrandService; import com.macro.mall.tiny.service.RedisService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; 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.ResponseBody; import java.util.List; import java.util.Map; import java.util.Set; /** * @auther macrozheng * @description redis测试Controller * @date 2020/3/3 * @github https://github.com/macrozheng */ @Controller @Api(tags = "RedisController") @Tag(name = "RedisController", description = "redis测试") @RequestMapping("/redis") public class RedisController { @Autowired private RedisService redisService; @Autowired private PmsBrandService brandService; @ApiOperation("测试简单缓存") @RequestMapping(value = "/simpleTest", method = RequestMethod.GET) @ResponseBody public CommonResult simpleTest() { List brandList = brandService.list(1, 5); PmsBrand brand = brandList.get(0); String key = "redis:simple:" + brand.getId(); redisService.set(key, brand); PmsBrand cacheBrand = (PmsBrand) redisService.get(key); return CommonResult.success(cacheBrand); } @ApiOperation("测试Hash结构的缓存") @RequestMapping(value = "/hashTest", method = RequestMethod.GET) @ResponseBody public CommonResult hashTest() { List brandList = brandService.list(1, 5); PmsBrand brand = brandList.get(0); String key = "redis:hash:" + brand.getId(); Map value = BeanUtil.beanToMap(brand); redisService.hSetAll(key, value); Map cacheValue = redisService.hGetAll(key); PmsBrand cacheBrand = BeanUtil.toBean(cacheValue, PmsBrand.class); return CommonResult.success(cacheBrand); } @ApiOperation("测试Set结构的缓存") @RequestMapping(value = "/setTest", method = RequestMethod.GET) @ResponseBody public CommonResult> setTest() { List brandList = brandService.list(1, 5); String key = "redis:set:all"; redisService.sAdd(key, (Object[]) ArrayUtil.toArray(brandList, PmsBrand.class)); redisService.sRemove(key, brandList.get(0)); Set cachedBrandList = redisService.sMembers(key); return CommonResult.success(cachedBrandList); } @ApiOperation("测试List结构的缓存") @RequestMapping(value = "/listTest", method = RequestMethod.GET) @ResponseBody public CommonResult> listTest() { List brandList = brandService.list(1, 5); String key = "redis:list:all"; redisService.lPushAll(key, (Object[]) ArrayUtil.toArray(brandList, PmsBrand.class)); redisService.lRemove(key, 1, brandList.get(0)); List cachedBrandList = redisService.lRange(key, 0, 3); return CommonResult.success(cachedBrandList); } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { int countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand record); int insertSelective(PmsBrand record); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByExample(@Param("record") PmsBrand record, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand record); int updateByPrimaryKeyWithBLOBs(PmsBrand record); int updateByPrimaryKey(PmsBrand record); } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { private Long id; private String name; @ApiModelProperty(value = "首字母") private String firstLetter; private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface PmsBrandService { int create(PmsBrand brand); int update(Long id, PmsBrand brand); int delete(Long id); PmsBrand getItem(Long id); List list(Integer pageNum, Integer pageSize); List ListAll(); } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/service/RedisService.java ================================================ package com.macro.mall.tiny.service; import java.util.List; import java.util.Map; import java.util.Set; /** * @auther macrozheng * @description redis操作Service * @date 2020/3/3 * @github https://github.com/macrozheng */ public interface RedisService { /** * 保存属性 */ void set(String key, Object value, long time); /** * 保存属性 */ void set(String key, Object value); /** * 获取属性 */ Object get(String key); /** * 删除属性 */ Boolean del(String key); /** * 批量删除属性 */ Long del(List keys); /** * 设置过期时间 */ Boolean expire(String key, long time); /** * 获取过期时间 */ Long getExpire(String key); /** * 判断是否有该属性 */ Boolean hasKey(String key); /** * 按delta递增 */ Long incr(String key, long delta); /** * 按delta递减 */ Long decr(String key, long delta); /** * 获取Hash结构中的属性 */ Object hGet(String key, String hashKey); /** * 向Hash结构中放入一个属性 */ Boolean hSet(String key, String hashKey, Object value, long time); /** * 向Hash结构中放入一个属性 */ void hSet(String key, String hashKey, Object value); /** * 直接获取整个Hash结构 */ Map hGetAll(String key); /** * 直接设置整个Hash结构 */ Boolean hSetAll(String key, Map map, long time); /** * 直接设置整个Hash结构 */ void hSetAll(String key, Map map); /** * 删除Hash结构中的属性 */ void hDel(String key, Object... hashKey); /** * 判断Hash结构中是否有该属性 */ Boolean hHasKey(String key, String hashKey); /** * Hash结构中属性递增 */ Long hIncr(String key, String hashKey, Long delta); /** * Hash结构中属性递减 */ Long hDecr(String key, String hashKey, Long delta); /** * 获取Set结构 */ Set sMembers(String key); /** * 向Set结构中添加属性 */ Long sAdd(String key, Object... values); /** * 向Set结构中添加属性 */ Long sAdd(String key, long time, Object... values); /** * 是否为Set中的属性 */ Boolean sIsMember(String key, Object value); /** * 获取Set结构的长度 */ Long sSize(String key); /** * 删除Set结构中的属性 */ Long sRemove(String key, Object... values); /** * 获取List结构中的属性 */ List lRange(String key, long start, long end); /** * 获取List结构的长度 */ Long lSize(String key); /** * 根据索引获取List中的属性 */ Object lIndex(String key, long index); /** * 向List结构中添加属性 */ Long lPush(String key, Object value); /** * 向List结构中添加属性 */ Long lPush(String key, Object value, long time); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Object... values); /** * 向List结构中批量添加属性 */ Long lPushAll(String key, Long time, Object... values); /** * 从List结构中移除属性 */ Long lRemove(String key, long count, Object value); } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import com.github.pagehelper.PageHelper; import com.macro.mall.tiny.config.RedisConfig; 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.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.List; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @Service public class PmsBrandServiceImpl implements PmsBrandService { @Autowired private PmsBrandMapper brandMapper; @Override public int create(PmsBrand brand) { return brandMapper.insertSelective(brand); } @CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id") @Override public int update(Long id, PmsBrand brand) { brand.setId(id); return brandMapper.updateByPrimaryKeySelective(brand); } @CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id") @Override public int delete(Long id) { return brandMapper.deleteByPrimaryKey(id); } @Cacheable(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id", unless = "#result==null") @Override public PmsBrand getItem(Long id) { return brandMapper.selectByPrimaryKey(id); } @Override public List list(Integer pageNum, Integer pageSize) { PageHelper.startPage(pageNum, pageSize); return brandMapper.selectByExample(new PmsBrandExample()); } @Override public List ListAll() { return brandMapper.selectByExample(new PmsBrandExample()); } } ================================================ FILE: mall-tiny-redis/src/main/java/com/macro/mall/tiny/service/impl/RedisServiceImpl.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.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @auther macrozheng * @description redis操作实现类 * @date 2020/3/3 * @github https://github.com/macrozheng */ @Service public class RedisServiceImpl implements RedisService { @Autowired private RedisTemplate redisTemplate; @Override public void set(String key, Object value, long time) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } @Override public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); } @Override public Object get(String key) { return redisTemplate.opsForValue().get(key); } @Override public Boolean del(String key) { return redisTemplate.delete(key); } @Override public Long del(List keys) { return redisTemplate.delete(keys); } @Override public Boolean expire(String key, long time) { return redisTemplate.expire(key, time, TimeUnit.SECONDS); } @Override public Long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } @Override public Boolean hasKey(String key) { return redisTemplate.hasKey(key); } @Override public Long incr(String key, long delta) { return redisTemplate.opsForValue().increment(key, delta); } @Override public Long decr(String key, long delta) { return redisTemplate.opsForValue().increment(key, -delta); } @Override public Object hGet(String key, String hashKey) { return redisTemplate.opsForHash().get(key, hashKey); } @Override public Boolean hSet(String key, String hashKey, Object value, long time) { redisTemplate.opsForHash().put(key, hashKey, value); return expire(key, time); } @Override public void hSet(String key, String hashKey, Object value) { redisTemplate.opsForHash().put(key, hashKey, value); } @Override public Map hGetAll(String key) { return redisTemplate.opsForHash().entries(key); } @Override public Boolean hSetAll(String key, Map map, long time) { redisTemplate.opsForHash().putAll(key, map); return expire(key, time); } @Override public void hSetAll(String key, Map map) { redisTemplate.opsForHash().putAll(key, map); } @Override public void hDel(String key, Object... hashKey) { redisTemplate.opsForHash().delete(key, hashKey); } @Override public Boolean hHasKey(String key, String hashKey) { return redisTemplate.opsForHash().hasKey(key, hashKey); } @Override public Long hIncr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, delta); } @Override public Long hDecr(String key, String hashKey, Long delta) { return redisTemplate.opsForHash().increment(key, hashKey, -delta); } @Override public Set sMembers(String key) { return redisTemplate.opsForSet().members(key); } @Override public Long sAdd(String key, Object... values) { return redisTemplate.opsForSet().add(key, values); } @Override public Long sAdd(String key, long time, Object... values) { Long count = redisTemplate.opsForSet().add(key, values); expire(key, time); return count; } @Override public Boolean sIsMember(String key, Object value) { return redisTemplate.opsForSet().isMember(key, value); } @Override public Long sSize(String key) { return redisTemplate.opsForSet().size(key); } @Override public Long sRemove(String key, Object... values) { return redisTemplate.opsForSet().remove(key, values); } @Override public List lRange(String key, long start, long end) { return redisTemplate.opsForList().range(key, start, end); } @Override public Long lSize(String key) { return redisTemplate.opsForList().size(key); } @Override public Object lIndex(String key, long index) { return redisTemplate.opsForList().index(key, index); } @Override public Long lPush(String key, Object value) { return redisTemplate.opsForList().rightPush(key, value); } @Override public Long lPush(String key, Object value, long time) { Long index = redisTemplate.opsForList().rightPush(key, value); expire(key, time); return index; } @Override public Long lPushAll(String key, Object... values) { return redisTemplate.opsForList().rightPushAll(key, values); } @Override public Long lPushAll(String key, Long time, Object... values) { Long count = redisTemplate.opsForList().rightPushAll(key, values); expire(key, time); return count; } @Override public Long lRemove(String key, long count, Object value) { return redisTemplate.opsForList().remove(key, count, value); } } ================================================ FILE: mall-tiny-redis/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root redis: host: localhost # Redis服务器地址 database: 0 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 password: # Redis服务器连接密码(默认为空) timeout: 3000ms # 连接超时时间 lettuce: pool: max-active: 8 # 连接池最大连接数 max-idle: 8 # 连接池最大空闲连接数 min-idle: 0 # 连接池最小空闲连接数 max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制 mvc: pathmatch: matching-strategy: ant_path_matcher mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml ================================================ FILE: mall-tiny-redis/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR}, brand_story = #{record.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{record.id,jdbcType=BIGINT}, name = #{record.name,jdbcType=VARCHAR}, first_letter = #{record.firstLetter,jdbcType=VARCHAR}, sort = #{record.sort,jdbcType=INTEGER}, factory_status = #{record.factoryStatus,jdbcType=INTEGER}, show_status = #{record.showStatus,jdbcType=INTEGER}, product_count = #{record.productCount,jdbcType=INTEGER}, product_comment_count = #{record.productCommentCount,jdbcType=INTEGER}, logo = #{record.logo,jdbcType=VARCHAR}, big_pic = #{record.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-redis/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-redis/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-redis/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-stream/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-stream/pom.xml ================================================ 4.0.0 mall-tiny-stream 1.0-SNAPSHOT mall-tiny-stream Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.projectlombok lombok true org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan("com.macro.mall.tiny.mbg.mapper") public class MyBatisConfig { } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger相关配置 * @date 2022/11/23 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("SwaggerUI演示") .description("mall-tiny") .contact(new Contact("macro", null, null)) .version("1.0") .build(); } @Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @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)); } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/controller/UmsMenuController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.dto.UmsMenuNode; import com.macro.mall.tiny.service.UmsMenuService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.tags.Tag; 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.ResponseBody; import java.util.List; /** * @auther macrozheng * @description 后台用户权限管理 * @date 2018/9/29 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsMenuController") @Tag(name = "UmsMenuController",description = "后台菜单管理") @RequestMapping("/menu") public class UmsMenuController { @Autowired private UmsMenuService menuService; @ApiOperation("树形结构返回所有菜单列表") @RequestMapping(value = "/treeList", method = RequestMethod.GET) @ResponseBody public CommonResult> treeList() { List menuNodeList = menuService.treeList(); return CommonResult.success(menuNodeList); } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/dto/UmsMenuNode.java ================================================ package com.macro.mall.tiny.dto; import com.macro.mall.tiny.mbg.model.UmsMenu; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import java.util.List; /** * @auther macrozheng * @description 后台菜单节点封装 * @date 2020/2/4 * @github https://github.com/macrozheng */ @Getter @Setter public class UmsMenuNode extends UmsMenu { @ApiModelProperty(value = "子级菜单") private List children; } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { long countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand row); int insertSelective(PmsBrand row); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExample(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand row); int updateByPrimaryKeyWithBLOBs(PmsBrand row); int updateByPrimaryKey(PmsBrand row); } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/mbg/mapper/UmsMenuMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.UmsMenu; import com.macro.mall.tiny.mbg.model.UmsMenuExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface UmsMenuMapper { long countByExample(UmsMenuExample example); int deleteByExample(UmsMenuExample example); int deleteByPrimaryKey(Long id); int insert(UmsMenu row); int insertSelective(UmsMenu row); List selectByExample(UmsMenuExample example); UmsMenu selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") UmsMenu row, @Param("example") UmsMenuExample example); int updateByExample(@Param("row") UmsMenu row, @Param("example") UmsMenuExample example); int updateByPrimaryKeySelective(UmsMenu row); int updateByPrimaryKey(UmsMenu row); } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; public class PmsBrand implements Serializable { private Long id; private String name; @ApiModelProperty(value = "首字母") private String firstLetter; private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/mbg/model/UmsMenu.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Date; public class UmsMenu implements Serializable { private Long id; @ApiModelProperty(value = "父级ID") private Long parentId; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "菜单名称") private String title; @ApiModelProperty(value = "菜单级数") private Integer level; @ApiModelProperty(value = "菜单排序") private Integer sort; @ApiModelProperty(value = "前端名称") private String name; @ApiModelProperty(value = "前端图标") private String icon; @ApiModelProperty(value = "前端隐藏") private Integer hidden; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getParentId() { return parentId; } public void setParentId(Long parentId) { this.parentId = parentId; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Integer getLevel() { return level; } public void setLevel(Integer level) { this.level = level; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getIcon() { return icon; } public void setIcon(String icon) { this.icon = icon; } public Integer getHidden() { return hidden; } public void setHidden(Integer hidden) { this.hidden = hidden; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", parentId=").append(parentId); sb.append(", createTime=").append(createTime); sb.append(", title=").append(title); sb.append(", level=").append(level); sb.append(", sort=").append(sort); sb.append(", name=").append(name); sb.append(", icon=").append(icon); sb.append(", hidden=").append(hidden); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/mbg/model/UmsMenuExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.Date; import java.util.List; public class UmsMenuExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public UmsMenuExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andParentIdIsNull() { addCriterion("parent_id is null"); return (Criteria) this; } public Criteria andParentIdIsNotNull() { addCriterion("parent_id is not null"); return (Criteria) this; } public Criteria andParentIdEqualTo(Long value) { addCriterion("parent_id =", value, "parentId"); return (Criteria) this; } public Criteria andParentIdNotEqualTo(Long value) { addCriterion("parent_id <>", value, "parentId"); return (Criteria) this; } public Criteria andParentIdGreaterThan(Long value) { addCriterion("parent_id >", value, "parentId"); return (Criteria) this; } public Criteria andParentIdGreaterThanOrEqualTo(Long value) { addCriterion("parent_id >=", value, "parentId"); return (Criteria) this; } public Criteria andParentIdLessThan(Long value) { addCriterion("parent_id <", value, "parentId"); return (Criteria) this; } public Criteria andParentIdLessThanOrEqualTo(Long value) { addCriterion("parent_id <=", value, "parentId"); return (Criteria) this; } public Criteria andParentIdIn(List values) { addCriterion("parent_id in", values, "parentId"); return (Criteria) this; } public Criteria andParentIdNotIn(List values) { addCriterion("parent_id not in", values, "parentId"); return (Criteria) this; } public Criteria andParentIdBetween(Long value1, Long value2) { addCriterion("parent_id between", value1, value2, "parentId"); return (Criteria) this; } public Criteria andParentIdNotBetween(Long value1, Long value2) { addCriterion("parent_id not between", value1, value2, "parentId"); return (Criteria) this; } public Criteria andCreateTimeIsNull() { addCriterion("create_time is null"); return (Criteria) this; } public Criteria andCreateTimeIsNotNull() { addCriterion("create_time is not null"); return (Criteria) this; } public Criteria andCreateTimeEqualTo(Date value) { addCriterion("create_time =", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotEqualTo(Date value) { addCriterion("create_time <>", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeGreaterThan(Date value) { addCriterion("create_time >", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeGreaterThanOrEqualTo(Date value) { addCriterion("create_time >=", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeLessThan(Date value) { addCriterion("create_time <", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeLessThanOrEqualTo(Date value) { addCriterion("create_time <=", value, "createTime"); return (Criteria) this; } public Criteria andCreateTimeIn(List values) { addCriterion("create_time in", values, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotIn(List values) { addCriterion("create_time not in", values, "createTime"); return (Criteria) this; } public Criteria andCreateTimeBetween(Date value1, Date value2) { addCriterion("create_time between", value1, value2, "createTime"); return (Criteria) this; } public Criteria andCreateTimeNotBetween(Date value1, Date value2) { addCriterion("create_time not between", value1, value2, "createTime"); return (Criteria) this; } public Criteria andTitleIsNull() { addCriterion("title is null"); return (Criteria) this; } public Criteria andTitleIsNotNull() { addCriterion("title is not null"); return (Criteria) this; } public Criteria andTitleEqualTo(String value) { addCriterion("title =", value, "title"); return (Criteria) this; } public Criteria andTitleNotEqualTo(String value) { addCriterion("title <>", value, "title"); return (Criteria) this; } public Criteria andTitleGreaterThan(String value) { addCriterion("title >", value, "title"); return (Criteria) this; } public Criteria andTitleGreaterThanOrEqualTo(String value) { addCriterion("title >=", value, "title"); return (Criteria) this; } public Criteria andTitleLessThan(String value) { addCriterion("title <", value, "title"); return (Criteria) this; } public Criteria andTitleLessThanOrEqualTo(String value) { addCriterion("title <=", value, "title"); return (Criteria) this; } public Criteria andTitleLike(String value) { addCriterion("title like", value, "title"); return (Criteria) this; } public Criteria andTitleNotLike(String value) { addCriterion("title not like", value, "title"); return (Criteria) this; } public Criteria andTitleIn(List values) { addCriterion("title in", values, "title"); return (Criteria) this; } public Criteria andTitleNotIn(List values) { addCriterion("title not in", values, "title"); return (Criteria) this; } public Criteria andTitleBetween(String value1, String value2) { addCriterion("title between", value1, value2, "title"); return (Criteria) this; } public Criteria andTitleNotBetween(String value1, String value2) { addCriterion("title not between", value1, value2, "title"); return (Criteria) this; } public Criteria andLevelIsNull() { addCriterion("level is null"); return (Criteria) this; } public Criteria andLevelIsNotNull() { addCriterion("level is not null"); return (Criteria) this; } public Criteria andLevelEqualTo(Integer value) { addCriterion("level =", value, "level"); return (Criteria) this; } public Criteria andLevelNotEqualTo(Integer value) { addCriterion("level <>", value, "level"); return (Criteria) this; } public Criteria andLevelGreaterThan(Integer value) { addCriterion("level >", value, "level"); return (Criteria) this; } public Criteria andLevelGreaterThanOrEqualTo(Integer value) { addCriterion("level >=", value, "level"); return (Criteria) this; } public Criteria andLevelLessThan(Integer value) { addCriterion("level <", value, "level"); return (Criteria) this; } public Criteria andLevelLessThanOrEqualTo(Integer value) { addCriterion("level <=", value, "level"); return (Criteria) this; } public Criteria andLevelIn(List values) { addCriterion("level in", values, "level"); return (Criteria) this; } public Criteria andLevelNotIn(List values) { addCriterion("level not in", values, "level"); return (Criteria) this; } public Criteria andLevelBetween(Integer value1, Integer value2) { addCriterion("level between", value1, value2, "level"); return (Criteria) this; } public Criteria andLevelNotBetween(Integer value1, Integer value2) { addCriterion("level not between", value1, value2, "level"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andIconIsNull() { addCriterion("icon is null"); return (Criteria) this; } public Criteria andIconIsNotNull() { addCriterion("icon is not null"); return (Criteria) this; } public Criteria andIconEqualTo(String value) { addCriterion("icon =", value, "icon"); return (Criteria) this; } public Criteria andIconNotEqualTo(String value) { addCriterion("icon <>", value, "icon"); return (Criteria) this; } public Criteria andIconGreaterThan(String value) { addCriterion("icon >", value, "icon"); return (Criteria) this; } public Criteria andIconGreaterThanOrEqualTo(String value) { addCriterion("icon >=", value, "icon"); return (Criteria) this; } public Criteria andIconLessThan(String value) { addCriterion("icon <", value, "icon"); return (Criteria) this; } public Criteria andIconLessThanOrEqualTo(String value) { addCriterion("icon <=", value, "icon"); return (Criteria) this; } public Criteria andIconLike(String value) { addCriterion("icon like", value, "icon"); return (Criteria) this; } public Criteria andIconNotLike(String value) { addCriterion("icon not like", value, "icon"); return (Criteria) this; } public Criteria andIconIn(List values) { addCriterion("icon in", values, "icon"); return (Criteria) this; } public Criteria andIconNotIn(List values) { addCriterion("icon not in", values, "icon"); return (Criteria) this; } public Criteria andIconBetween(String value1, String value2) { addCriterion("icon between", value1, value2, "icon"); return (Criteria) this; } public Criteria andIconNotBetween(String value1, String value2) { addCriterion("icon not between", value1, value2, "icon"); return (Criteria) this; } public Criteria andHiddenIsNull() { addCriterion("hidden is null"); return (Criteria) this; } public Criteria andHiddenIsNotNull() { addCriterion("hidden is not null"); return (Criteria) this; } public Criteria andHiddenEqualTo(Integer value) { addCriterion("hidden =", value, "hidden"); return (Criteria) this; } public Criteria andHiddenNotEqualTo(Integer value) { addCriterion("hidden <>", value, "hidden"); return (Criteria) this; } public Criteria andHiddenGreaterThan(Integer value) { addCriterion("hidden >", value, "hidden"); return (Criteria) this; } public Criteria andHiddenGreaterThanOrEqualTo(Integer value) { addCriterion("hidden >=", value, "hidden"); return (Criteria) this; } public Criteria andHiddenLessThan(Integer value) { addCriterion("hidden <", value, "hidden"); return (Criteria) this; } public Criteria andHiddenLessThanOrEqualTo(Integer value) { addCriterion("hidden <=", value, "hidden"); return (Criteria) this; } public Criteria andHiddenIn(List values) { addCriterion("hidden in", values, "hidden"); return (Criteria) this; } public Criteria andHiddenNotIn(List values) { addCriterion("hidden not in", values, "hidden"); return (Criteria) this; } public Criteria andHiddenBetween(Integer value1, Integer value2) { addCriterion("hidden between", value1, value2, "hidden"); return (Criteria) this; } public Criteria andHiddenNotBetween(Integer value1, Integer value2) { addCriterion("hidden not between", value1, value2, "hidden"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/service/UmsMenuService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.dto.UmsMenuNode; import java.util.List; /** * @auther macrozheng * @description 后台用户权限管理Service * @date 2018/9/29 * @github https://github.com/macrozheng */ public interface UmsMenuService { /** * 树形结构返回所有菜单列表 */ List treeList(); } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-stream/src/main/java/com/macro/mall/tiny/service/impl/UmsMenuServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import com.macro.mall.tiny.dto.UmsMenuNode; import com.macro.mall.tiny.mbg.mapper.UmsMenuMapper; import com.macro.mall.tiny.mbg.model.UmsMenuExample; import com.macro.mall.tiny.mbg.model.UmsMenu; import com.macro.mall.tiny.mbg.model.UmsMenuExample; import com.macro.mall.tiny.service.UmsMenuService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * @auther macrozheng * @description 后台菜单管理Service实现类 * @date 2020/2/2 * @github https://github.com/macrozheng */ @Service public class UmsMenuServiceImpl implements UmsMenuService { private Logger LOGGER = LoggerFactory.getLogger(this.getClass().getName()); @Autowired private UmsMenuMapper menuMapper; @Override public List treeList() { List menuList = menuMapper.selectByExample(new UmsMenuExample()); List result = menuList.stream() .filter(menu -> menu.getParentId().equals(0L)) .map(menu -> covertMenuNode(menu, menuList)).collect(Collectors.toList()); return result; } /** * 将UmsMenu转化为UmsMenuNode并设置children属性 */ private UmsMenuNode covertMenuNode(UmsMenu menu, List menuList) { UmsMenuNode node = new UmsMenuNode(); BeanUtils.copyProperties(menu, node); List children = menuList.stream() .filter(subMenu -> subMenu.getParentId().equals(menu.getId())) .map(subMenu -> covertMenuNode(subMenu, menuList)).collect(Collectors.toList()); node.setChildren(children); return node; } } ================================================ FILE: mall-tiny-stream/src/main/resources/application.yml ================================================ server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER mybatis: mapper-locations: - classpath:mapper/*.xml - classpath*:com/**/mapper/*.xml ================================================ FILE: mall-tiny-stream/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-stream/src/main/resources/com/macro/mall/tiny/mbg/mapper/UmsMenuMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, parent_id, create_time, title, level, sort, name, icon, hidden delete from ums_menu where id = #{id,jdbcType=BIGINT} delete from ums_menu SELECT LAST_INSERT_ID() insert into ums_menu (parent_id, create_time, title, level, sort, name, icon, hidden) values (#{parentId,jdbcType=BIGINT}, #{createTime,jdbcType=TIMESTAMP}, #{title,jdbcType=VARCHAR}, #{level,jdbcType=INTEGER}, #{sort,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{icon,jdbcType=VARCHAR}, #{hidden,jdbcType=INTEGER}) SELECT LAST_INSERT_ID() insert into ums_menu parent_id, create_time, title, level, sort, name, icon, hidden, #{parentId,jdbcType=BIGINT}, #{createTime,jdbcType=TIMESTAMP}, #{title,jdbcType=VARCHAR}, #{level,jdbcType=INTEGER}, #{sort,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{icon,jdbcType=VARCHAR}, #{hidden,jdbcType=INTEGER}, update ums_menu id = #{row.id,jdbcType=BIGINT}, parent_id = #{row.parentId,jdbcType=BIGINT}, create_time = #{row.createTime,jdbcType=TIMESTAMP}, title = #{row.title,jdbcType=VARCHAR}, level = #{row.level,jdbcType=INTEGER}, sort = #{row.sort,jdbcType=INTEGER}, name = #{row.name,jdbcType=VARCHAR}, icon = #{row.icon,jdbcType=VARCHAR}, hidden = #{row.hidden,jdbcType=INTEGER}, update ums_menu set id = #{row.id,jdbcType=BIGINT}, parent_id = #{row.parentId,jdbcType=BIGINT}, create_time = #{row.createTime,jdbcType=TIMESTAMP}, title = #{row.title,jdbcType=VARCHAR}, level = #{row.level,jdbcType=INTEGER}, sort = #{row.sort,jdbcType=INTEGER}, name = #{row.name,jdbcType=VARCHAR}, icon = #{row.icon,jdbcType=VARCHAR}, hidden = #{row.hidden,jdbcType=INTEGER} update ums_menu parent_id = #{parentId,jdbcType=BIGINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, title = #{title,jdbcType=VARCHAR}, level = #{level,jdbcType=INTEGER}, sort = #{sort,jdbcType=INTEGER}, name = #{name,jdbcType=VARCHAR}, icon = #{icon,jdbcType=VARCHAR}, hidden = #{hidden,jdbcType=INTEGER}, where id = #{id,jdbcType=BIGINT} update ums_menu set parent_id = #{parentId,jdbcType=BIGINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, title = #{title,jdbcType=VARCHAR}, level = #{level,jdbcType=INTEGER}, sort = #{sort,jdbcType=INTEGER}, name = #{name,jdbcType=VARCHAR}, icon = #{icon,jdbcType=VARCHAR}, hidden = #{hidden,jdbcType=INTEGER} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-stream/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-stream/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-stream/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { @Test public void contextLoads() { } } ================================================ FILE: mall-tiny-stream/src/test/java/com/macro/mall/tiny/stream/StreamApiTest.java ================================================ package com.macro.mall.tiny.stream; import com.macro.mall.tiny.mbg.mapper.UmsMenuMapper; import com.macro.mall.tiny.mbg.model.UmsMenu; import com.macro.mall.tiny.mbg.model.UmsMenuExample; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @SpringBootTest public class StreamApiTest { private static final Logger LOGGER = LoggerFactory.getLogger(StreamApiTest.class); @Autowired private UmsMenuMapper menuMapper; private List menuList; @BeforeEach void eachInit(){ menuList = menuMapper.selectByExample(new UmsMenuExample()); LOGGER.info("eachInit():在测试方法前执行,每个测试方法前都执行"); } @Test public void filterTest(){ //filter操作:获取所有一级菜单 List oneLevelList = menuList.stream() .filter(menu -> menu.getParentId() == 0L) .collect(Collectors.toList()); LOGGER.info("filter操作:{}",oneLevelList); } @Test public void mapTest(){ //map操作:获取所有菜单的id List idList = menuList.stream() .map(menu -> menu.getId()) .collect(Collectors.toList()); LOGGER.info("map操作:{}",idList); } @Test public void limitTest(){ //limit操作:获取前5个菜单 List firstFiveList = menuList.stream() .limit(5) .collect(Collectors.toList()); LOGGER.info("limit操作:{}",firstFiveList); } @Test public void countTest(){ //count操作:获取所有一级菜单的个数 long count = menuList.stream() .filter(menu -> menu.getParentId() == 0L) .count(); LOGGER.info("count操作:{}",count); } @Test public void sortedTest(){ //sorted操作:将所有菜单按照sort字段进行排序 List sortedList = menuList.stream() .sorted((menu1,menu2)->{return menu2.getSort().compareTo(menu1.getSort());}) .collect(Collectors.toList()); LOGGER.info("sorted操作:{}",sortedList); } @Test public void skipTest(){ //skip操作:跳过前5个元素,返回后面的 List skipList = menuList.stream() .skip(5) .collect(Collectors.toList()); LOGGER.info("skip操作:{}",skipList); } @Test public void collect2mapTest(){ //collect转map操作:将菜单列表以id为key,以菜单对象为值转换成map Map menuMap = menuList.stream() .collect(Collectors.toMap(menu -> menu.getId(), menu -> menu)); LOGGER.info("collect转map操作:{}",menuMap); } @Test public void forEachTest(){ //forEach操作,对集合中的元素进行迭代 menuList.stream().forEach(menu-> LOGGER.info("forEach操作{}",menu)); } } ================================================ FILE: mall-tiny-swagger/.gitignore ================================================ HELP.md /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ ================================================ FILE: mall-tiny-swagger/pom.xml ================================================ 4.0.0 mall-tiny-swagger 1.0-SNAPSHOT mall-tiny-swagger Demo project for Spring Boot com.macro.mall mall-learning-teach 1.0-SNAPSHOT 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 ${pagehelper-starter.version} com.alibaba druid-spring-boot-starter ${druid.version} org.mybatis.generator mybatis-generator-core ${mybatis-generator.version} mysql mysql-connector-java ${mysql-connector.version} org.projectlombok lombok true org.springframework.boot spring-boot-starter-security cn.hutool hutool-all ${hutool.version} io.jsonwebtoken jjwt ${jjwt.version} io.springfox springfox-boot-starter ${springfox-swagger.version} org.springframework.boot spring-boot-maven-plugin ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/MallTinyApplication.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MallTinyApplication { public static void main(String[] args) { SpringApplication.run(MallTinyApplication.class, args); } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/common/api/CommonPage.java ================================================ package com.macro.mall.tiny.common.api; import com.github.pagehelper.PageInfo; import java.util.List; /** * @auther macrozheng * @description 分页数据封装类 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonPage { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List list; /** * 将PageHelper分页后的list转为分页信息 */ public static CommonPage restPage(List list) { CommonPage result = new CommonPage(); PageInfo pageInfo = new PageInfo(list); result.setTotalPage(pageInfo.getPages()); result.setPageNum(pageInfo.getPageNum()); result.setPageSize(pageInfo.getPageSize()); result.setTotal(pageInfo.getTotal()); result.setList(pageInfo.getList()); return result; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/common/api/CommonResult.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 通用返回对象 * @date 2019/4/19 * @github https://github.com/macrozheng */ public class CommonResult { private long code; private String message; private T data; protected CommonResult() { } protected CommonResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 成功返回结果 * * @param data 获取的数据 */ public static CommonResult success(T data) { return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } /** * 成功返回结果 * * @param data 获取的数据 * @param message 提示信息 */ public static CommonResult success(T data, String message) { return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 * @param errorCode 错误码 */ public static CommonResult failed(IErrorCode errorCode) { return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * @param message 提示信息 */ public static CommonResult failed(String message) { return new CommonResult(ResultCode.FAILED.getCode(), message, null); } /** * 失败返回结果 */ public static CommonResult failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static CommonResult validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static CommonResult validateFailed(String message) { return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static CommonResult unauthorized(T data) { return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static CommonResult forbidden(T data) { return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 封装API的错误码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public interface IErrorCode { long getCode(); String getMessage(); } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/common/api/ResultCode.java ================================================ package com.macro.mall.tiny.common.api; /** * @auther macrozheng * @description 枚举了一些常用API操作码 * @date 2019/4/19 * @github https://github.com/macrozheng */ public enum ResultCode implements IErrorCode { SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message) { this.code = code; this.message = message; } public long getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/common/utils/JwtTokenUtil.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生成的工具类 * JWT token的格式:header.payload.signature * header的格式(算法、token的类型): * {"alg": "HS512","typ": "JWT"} * payload的格式(用户名、创建时间、生成时间): * {"sub":"wang","created":1489079981393,"exp":1489684781} * signature的生成算法: * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) * 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); } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/component/JwtAuthenticationTokenFilter.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; /** * @auther macrozheng * @description JWT登录授权过滤器 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当未登录或者token失效访问接口时,自定义的返回结果 * @date 2018/5/14 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/component/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; /** * @auther macrozheng * @description 当访问接口没有权限时,自定义的返回结果 * @date 2018/4/26 * @github https://github.com/macrozheng */ @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(); } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java ================================================ package com.macro.mall.tiny.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @auther macrozheng * @description MyBatis配置类 * @date 2019/4/8 * @github https://github.com/macrozheng */ @Configuration @MapperScan("com.macro.mall.tiny.mbg.mapper") public class MyBatisConfig { } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/config/SecurityConfig.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.domain.AdminUserDetails; 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.context.annotation.Lazy; 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.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * @auther macrozheng * @description SpringSecurity的配置 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig { @Lazy @Autowired private UmsAdminService adminService; @Autowired private RestfulAccessDeniedHandler restfulAccessDeniedHandler; @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Bean SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf .disable() .sessionManagement()// 基于token,所以不需要session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(HttpMethod.GET, // 允许对于网站静态资源的无授权访问 "/", "/swagger-ui/", "/*.html", "/favicon.ico", "/**/*.html", "/**/*.css", "/**/*.js", "/swagger-resources/**", "/v2/api-docs/**" ) .permitAll() .antMatchers("/admin/login")// 对登录注册要允许匿名访问 .permitAll() .antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求 .permitAll() .anyRequest()// 除上面外的所有请求全部需要鉴权认证 .authenticated(); // 禁用缓存 httpSecurity.headers().cacheControl(); // 添加JWT filter httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); //添加自定义未授权和未登录结果返回 httpSecurity.exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler) .authenticationEntryPoint(restAuthenticationEntryPoint); return httpSecurity.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public UserDetailsService userDetailsService() { //获取登录用户信息 return username -> { AdminUserDetails admin = adminService.getAdminByUsername(username); if (admin != null) { return admin; } throw new UsernameNotFoundException("用户名或密码错误"); }; } @Bean public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() { return new JwtAuthenticationTokenFilter(); } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/config/Swagger2Config.java ================================================ package com.macro.mall.tiny.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @auther macrozheng * @description Swagger相关配置(带认证) * @date 2022/11/23 * @github https://github.com/macrozheng */ @Configuration public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .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(new Contact("macro", null, null)) .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; } @Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/controller/PmsBrandController.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.*; import io.swagger.v3.oas.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @auther macrozheng * @description 品牌管理Controller * @date 2019/4/19 * @github https://github.com/macrozheng */ @Controller @Api(tags = "PmsBrandController") @Tag(name = "PmsBrandController", description = "商品品牌管理") @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 @PreAuthorize("hasRole('ADMIN')") 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 @PreAuthorize("hasRole('ADMIN')") public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDto) { 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 @PreAuthorize("hasRole('ADMIN')") 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 @PreAuthorize("hasRole('ADMIN')") public CommonResult> listBrand(@RequestParam(value = "pageNum", defaultValue = "1") @ApiParam("页码") Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "5") @ApiParam("每页数量") Integer pageSize) { List brandList = brandService.listBrand(pageNum, pageSize); return CommonResult.success(CommonPage.restPage(brandList)); } @ApiOperation("获取指定id的品牌详情") @RequestMapping(value = "/{id}", method = RequestMethod.GET) @ResponseBody @PreAuthorize("hasRole('ADMIN')") public CommonResult brand(@PathVariable("id") Long id) { return CommonResult.success(brandService.getBrand(id)); } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/controller/UmsAdminController.java ================================================ package com.macro.mall.tiny.controller; import com.macro.mall.tiny.common.api.CommonResult; import com.macro.mall.tiny.service.UmsAdminService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; 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; import java.util.HashMap; import java.util.Map; /** * @auther macrozheng * @description 后台用户管理 * @date 2018/4/26 * @github https://github.com/macrozheng */ @Controller @Api(tags = "UmsAdminController") @Tag(name = "UmsAdminController", description = "后台用户管理") @RequestMapping("/admin") public class UmsAdminController { @Autowired private UmsAdminService adminService; @Value("${jwt.tokenHeader}") private String tokenHeader; @Value("${jwt.tokenHead}") private String tokenHead; @RequestMapping(value = "/login", method = RequestMethod.POST) @ApiOperation(value = "登录以后返回token") @ApiImplicitParams({ @ApiImplicitParam(name = "username", value = "用户名", required = true, dataTypeClass = String.class), @ApiImplicitParam(name = "password", value = "密码", required = true, dataTypeClass = String.class) }) @ApiResponses({ @ApiResponse(responseCode = "401", description = "暂未登录或token已经过期"), @ApiResponse(responseCode = "403", description = "没有相关权限"), }) @ResponseBody public CommonResult login(@RequestParam String username, @RequestParam String password) { String token = adminService.login(username, password); if (token == null) { return CommonResult.validateFailed("用户名或密码错误"); } Map tokenMap = new HashMap<>(); tokenMap.put("token", token); tokenMap.put("tokenHead", tokenHead); return CommonResult.success(tokenMap); } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/domain/AdminUserDetails.java ================================================ package com.macro.mall.tiny.domain; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; 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; /** * @auther macrozheng * @description SpringSecurity用户信息封装类 * @date 2020/10/15 * @github https://github.com/macrozheng */ @Data @EqualsAndHashCode(callSuper = false) @Builder public class AdminUserDetails implements UserDetails { private String username; private String password; private List authorityList; @Override public Collection getAuthorities() { return this.authorityList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/mbg/CommentGenerator.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; /** * @auther macrozheng * @description 自定义注释生成器 * @date 2018/4/26 * @github https://github.com/macrozheng */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; private static final String EXAMPLE_SUFFIX="Example"; private static final String MAPPER_SUFFIX="Mapper"; 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(); //根据参数和备注信息判断是否添加swagger注解信息 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.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX)&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){ compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME)); } } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/mbg/Generator.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; /** * @auther macrozheng * @description 用于生产MBG的代码 * @date 2018/4/26 * @github https://github.com/macrozheng */ 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); } } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.java ================================================ package com.macro.mall.tiny.mbg.mapper; import com.macro.mall.tiny.mbg.model.PmsBrand; import com.macro.mall.tiny.mbg.model.PmsBrandExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface PmsBrandMapper { long countByExample(PmsBrandExample example); int deleteByExample(PmsBrandExample example); int deleteByPrimaryKey(Long id); int insert(PmsBrand row); int insertSelective(PmsBrand row); List selectByExampleWithBLOBs(PmsBrandExample example); List selectByExample(PmsBrandExample example); PmsBrand selectByPrimaryKey(Long id); int updateByExampleSelective(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExampleWithBLOBs(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByExample(@Param("row") PmsBrand row, @Param("example") PmsBrandExample example); int updateByPrimaryKeySelective(PmsBrand row); int updateByPrimaryKeyWithBLOBs(PmsBrand row); int updateByPrimaryKey(PmsBrand row); } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrand.java ================================================ package com.macro.mall.tiny.mbg.model; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; @ApiModel(value = "PmsBrand",description = "商品品牌") public class PmsBrand implements Serializable { @ApiModelProperty(value = "主键ID") private Long id; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "首字母") private String firstLetter; @ApiModelProperty(value = "排序") private Integer sort; @ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是") private Integer factoryStatus; @ApiModelProperty(value = "是否显示") private Integer showStatus; @ApiModelProperty(value = "产品数量") private Integer productCount; @ApiModelProperty(value = "产品评论数量") private Integer productCommentCount; @ApiModelProperty(value = "品牌logo") private String logo; @ApiModelProperty(value = "专区大图") private String bigPic; @ApiModelProperty(value = "品牌故事") private String brandStory; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getFactoryStatus() { return factoryStatus; } public void setFactoryStatus(Integer factoryStatus) { this.factoryStatus = factoryStatus; } public Integer getShowStatus() { return showStatus; } public void setShowStatus(Integer showStatus) { this.showStatus = showStatus; } public Integer getProductCount() { return productCount; } public void setProductCount(Integer productCount) { this.productCount = productCount; } public Integer getProductCommentCount() { return productCommentCount; } public void setProductCommentCount(Integer productCommentCount) { this.productCommentCount = productCommentCount; } public String getLogo() { return logo; } public void setLogo(String logo) { this.logo = logo; } public String getBigPic() { return bigPic; } public void setBigPic(String bigPic) { this.bigPic = bigPic; } public String getBrandStory() { return brandStory; } public void setBrandStory(String brandStory) { this.brandStory = brandStory; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", firstLetter=").append(firstLetter); sb.append(", sort=").append(sort); sb.append(", factoryStatus=").append(factoryStatus); sb.append(", showStatus=").append(showStatus); sb.append(", productCount=").append(productCount); sb.append(", productCommentCount=").append(productCommentCount); sb.append(", logo=").append(logo); sb.append(", bigPic=").append(bigPic); sb.append(", brandStory=").append(brandStory); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/mbg/model/PmsBrandExample.java ================================================ package com.macro.mall.tiny.mbg.model; import java.util.ArrayList; import java.util.List; public class PmsBrandExample { protected String orderByClause; protected boolean distinct; protected List oredCriteria; public PmsBrandExample() { oredCriteria = new ArrayList<>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); } public boolean isValid() { return criteria.size() > 0; } public List getAllCriteria() { return criteria; } public List getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Long value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Long value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Long value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Long value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Long value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Long value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Long value1, Long value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Long value1, Long value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andFirstLetterIsNull() { addCriterion("first_letter is null"); return (Criteria) this; } public Criteria andFirstLetterIsNotNull() { addCriterion("first_letter is not null"); return (Criteria) this; } public Criteria andFirstLetterEqualTo(String value) { addCriterion("first_letter =", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotEqualTo(String value) { addCriterion("first_letter <>", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThan(String value) { addCriterion("first_letter >", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterGreaterThanOrEqualTo(String value) { addCriterion("first_letter >=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThan(String value) { addCriterion("first_letter <", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLessThanOrEqualTo(String value) { addCriterion("first_letter <=", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterLike(String value) { addCriterion("first_letter like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotLike(String value) { addCriterion("first_letter not like", value, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterIn(List values) { addCriterion("first_letter in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotIn(List values) { addCriterion("first_letter not in", values, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterBetween(String value1, String value2) { addCriterion("first_letter between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andFirstLetterNotBetween(String value1, String value2) { addCriterion("first_letter not between", value1, value2, "firstLetter"); return (Criteria) this; } public Criteria andSortIsNull() { addCriterion("sort is null"); return (Criteria) this; } public Criteria andSortIsNotNull() { addCriterion("sort is not null"); return (Criteria) this; } public Criteria andSortEqualTo(Integer value) { addCriterion("sort =", value, "sort"); return (Criteria) this; } public Criteria andSortNotEqualTo(Integer value) { addCriterion("sort <>", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThan(Integer value) { addCriterion("sort >", value, "sort"); return (Criteria) this; } public Criteria andSortGreaterThanOrEqualTo(Integer value) { addCriterion("sort >=", value, "sort"); return (Criteria) this; } public Criteria andSortLessThan(Integer value) { addCriterion("sort <", value, "sort"); return (Criteria) this; } public Criteria andSortLessThanOrEqualTo(Integer value) { addCriterion("sort <=", value, "sort"); return (Criteria) this; } public Criteria andSortIn(List values) { addCriterion("sort in", values, "sort"); return (Criteria) this; } public Criteria andSortNotIn(List values) { addCriterion("sort not in", values, "sort"); return (Criteria) this; } public Criteria andSortBetween(Integer value1, Integer value2) { addCriterion("sort between", value1, value2, "sort"); return (Criteria) this; } public Criteria andSortNotBetween(Integer value1, Integer value2) { addCriterion("sort not between", value1, value2, "sort"); return (Criteria) this; } public Criteria andFactoryStatusIsNull() { addCriterion("factory_status is null"); return (Criteria) this; } public Criteria andFactoryStatusIsNotNull() { addCriterion("factory_status is not null"); return (Criteria) this; } public Criteria andFactoryStatusEqualTo(Integer value) { addCriterion("factory_status =", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotEqualTo(Integer value) { addCriterion("factory_status <>", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThan(Integer value) { addCriterion("factory_status >", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusGreaterThanOrEqualTo(Integer value) { addCriterion("factory_status >=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThan(Integer value) { addCriterion("factory_status <", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusLessThanOrEqualTo(Integer value) { addCriterion("factory_status <=", value, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusIn(List values) { addCriterion("factory_status in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotIn(List values) { addCriterion("factory_status not in", values, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusBetween(Integer value1, Integer value2) { addCriterion("factory_status between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andFactoryStatusNotBetween(Integer value1, Integer value2) { addCriterion("factory_status not between", value1, value2, "factoryStatus"); return (Criteria) this; } public Criteria andShowStatusIsNull() { addCriterion("show_status is null"); return (Criteria) this; } public Criteria andShowStatusIsNotNull() { addCriterion("show_status is not null"); return (Criteria) this; } public Criteria andShowStatusEqualTo(Integer value) { addCriterion("show_status =", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotEqualTo(Integer value) { addCriterion("show_status <>", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThan(Integer value) { addCriterion("show_status >", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusGreaterThanOrEqualTo(Integer value) { addCriterion("show_status >=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThan(Integer value) { addCriterion("show_status <", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusLessThanOrEqualTo(Integer value) { addCriterion("show_status <=", value, "showStatus"); return (Criteria) this; } public Criteria andShowStatusIn(List values) { addCriterion("show_status in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotIn(List values) { addCriterion("show_status not in", values, "showStatus"); return (Criteria) this; } public Criteria andShowStatusBetween(Integer value1, Integer value2) { addCriterion("show_status between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andShowStatusNotBetween(Integer value1, Integer value2) { addCriterion("show_status not between", value1, value2, "showStatus"); return (Criteria) this; } public Criteria andProductCountIsNull() { addCriterion("product_count is null"); return (Criteria) this; } public Criteria andProductCountIsNotNull() { addCriterion("product_count is not null"); return (Criteria) this; } public Criteria andProductCountEqualTo(Integer value) { addCriterion("product_count =", value, "productCount"); return (Criteria) this; } public Criteria andProductCountNotEqualTo(Integer value) { addCriterion("product_count <>", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThan(Integer value) { addCriterion("product_count >", value, "productCount"); return (Criteria) this; } public Criteria andProductCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_count >=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThan(Integer value) { addCriterion("product_count <", value, "productCount"); return (Criteria) this; } public Criteria andProductCountLessThanOrEqualTo(Integer value) { addCriterion("product_count <=", value, "productCount"); return (Criteria) this; } public Criteria andProductCountIn(List values) { addCriterion("product_count in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountNotIn(List values) { addCriterion("product_count not in", values, "productCount"); return (Criteria) this; } public Criteria andProductCountBetween(Integer value1, Integer value2) { addCriterion("product_count between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCountNotBetween(Integer value1, Integer value2) { addCriterion("product_count not between", value1, value2, "productCount"); return (Criteria) this; } public Criteria andProductCommentCountIsNull() { addCriterion("product_comment_count is null"); return (Criteria) this; } public Criteria andProductCommentCountIsNotNull() { addCriterion("product_comment_count is not null"); return (Criteria) this; } public Criteria andProductCommentCountEqualTo(Integer value) { addCriterion("product_comment_count =", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotEqualTo(Integer value) { addCriterion("product_comment_count <>", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThan(Integer value) { addCriterion("product_comment_count >", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountGreaterThanOrEqualTo(Integer value) { addCriterion("product_comment_count >=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThan(Integer value) { addCriterion("product_comment_count <", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountLessThanOrEqualTo(Integer value) { addCriterion("product_comment_count <=", value, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountIn(List values) { addCriterion("product_comment_count in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotIn(List values) { addCriterion("product_comment_count not in", values, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountBetween(Integer value1, Integer value2) { addCriterion("product_comment_count between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andProductCommentCountNotBetween(Integer value1, Integer value2) { addCriterion("product_comment_count not between", value1, value2, "productCommentCount"); return (Criteria) this; } public Criteria andLogoIsNull() { addCriterion("logo is null"); return (Criteria) this; } public Criteria andLogoIsNotNull() { addCriterion("logo is not null"); return (Criteria) this; } public Criteria andLogoEqualTo(String value) { addCriterion("logo =", value, "logo"); return (Criteria) this; } public Criteria andLogoNotEqualTo(String value) { addCriterion("logo <>", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThan(String value) { addCriterion("logo >", value, "logo"); return (Criteria) this; } public Criteria andLogoGreaterThanOrEqualTo(String value) { addCriterion("logo >=", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThan(String value) { addCriterion("logo <", value, "logo"); return (Criteria) this; } public Criteria andLogoLessThanOrEqualTo(String value) { addCriterion("logo <=", value, "logo"); return (Criteria) this; } public Criteria andLogoLike(String value) { addCriterion("logo like", value, "logo"); return (Criteria) this; } public Criteria andLogoNotLike(String value) { addCriterion("logo not like", value, "logo"); return (Criteria) this; } public Criteria andLogoIn(List values) { addCriterion("logo in", values, "logo"); return (Criteria) this; } public Criteria andLogoNotIn(List values) { addCriterion("logo not in", values, "logo"); return (Criteria) this; } public Criteria andLogoBetween(String value1, String value2) { addCriterion("logo between", value1, value2, "logo"); return (Criteria) this; } public Criteria andLogoNotBetween(String value1, String value2) { addCriterion("logo not between", value1, value2, "logo"); return (Criteria) this; } public Criteria andBigPicIsNull() { addCriterion("big_pic is null"); return (Criteria) this; } public Criteria andBigPicIsNotNull() { addCriterion("big_pic is not null"); return (Criteria) this; } public Criteria andBigPicEqualTo(String value) { addCriterion("big_pic =", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotEqualTo(String value) { addCriterion("big_pic <>", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThan(String value) { addCriterion("big_pic >", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicGreaterThanOrEqualTo(String value) { addCriterion("big_pic >=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThan(String value) { addCriterion("big_pic <", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLessThanOrEqualTo(String value) { addCriterion("big_pic <=", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicLike(String value) { addCriterion("big_pic like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotLike(String value) { addCriterion("big_pic not like", value, "bigPic"); return (Criteria) this; } public Criteria andBigPicIn(List values) { addCriterion("big_pic in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotIn(List values) { addCriterion("big_pic not in", values, "bigPic"); return (Criteria) this; } public Criteria andBigPicBetween(String value1, String value2) { addCriterion("big_pic between", value1, value2, "bigPic"); return (Criteria) this; } public Criteria andBigPicNotBetween(String value1, String value2) { addCriterion("big_pic not between", value1, value2, "bigPic"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/service/PmsBrandService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.mbg.model.PmsBrand; import java.util.List; /** * @auther macrozheng * @description PmsBrandService * @date 2019/4/19 * @github https://github.com/macrozheng */ 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); } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/service/UmsAdminService.java ================================================ package com.macro.mall.tiny.service; import com.macro.mall.tiny.domain.AdminUserDetails; /** * @auther macrozheng * @description 后台用于管理Service * @date 2020/10/15 * @github https://github.com/macrozheng */ public interface UmsAdminService { /** * 根据用户名获取用户信息 */ AdminUserDetails getAdminByUsername(String username); /** * 用户名密码登录 */ String login(String username, String password); } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/service/impl/PmsBrandServiceImpl.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; /** * @auther macrozheng * @description PmsBrandService实现类 * @date 2019/4/19 * @github https://github.com/macrozheng */ @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); } } ================================================ FILE: mall-tiny-swagger/src/main/java/com/macro/mall/tiny/service/impl/UmsAdminServiceImpl.java ================================================ package com.macro.mall.tiny.service.impl; import cn.hutool.core.collection.CollUtil; import com.macro.mall.tiny.common.utils.JwtTokenUtil; import com.macro.mall.tiny.domain.AdminUserDetails; import com.macro.mall.tiny.service.UmsAdminService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; 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.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * Created by macro on 2020/10/15. */ @Slf4j @Service public class UmsAdminServiceImpl implements UmsAdminService { /** * 存放默认用户信息 */ private List adminUserDetailsList = new ArrayList<>(); @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private PasswordEncoder passwordEncoder; @PostConstruct private void init(){ adminUserDetailsList.add(AdminUserDetails.builder() .username("admin") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("ROLE_ADMIN")) .build()); adminUserDetailsList.add(AdminUserDetails.builder() .username("macro") .password(passwordEncoder.encode("123456")) .authorityList(CollUtil.toList("ROLE_USER")) .build()); } @Override public AdminUserDetails getAdminByUsername(String username) { List findList = adminUserDetailsList.stream().filter(item -> item.getUsername().equals(username)).collect(Collectors.toList()); if(CollUtil.isNotEmpty(findList)){ return findList.get(0); } return null; } @Override public String login(String username, String password) { String token = null; try { UserDetails userDetails = getAdminByUsername(username); if(userDetails==null){ return token; } 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) { log.warn("登录异常:{}", e.getMessage()); } return token; } } ================================================ FILE: mall-tiny-swagger/src/main/resources/application.yml ================================================ server: port: 8088 spring: datasource: url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER mybatis: mapper-locations: - classpath:dao/*.xml - classpath*:com/**/mapper/*.xml # 自定义jwt key jwt: tokenHeader: Authorization #JWT存储的请求头 secret: mySecret #JWT加解密使用的密钥 expiration: 604800 #JWT的超期限时间(60*60*24) tokenHead: Bearer #JWT负载中拿到开头 ================================================ FILE: mall-tiny-swagger/src/main/resources/com/macro/mall/tiny/mbg/mapper/PmsBrandMapper.xml ================================================ and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition} #{listItem} id, name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic brand_story delete from pms_brand where id = #{id,jdbcType=BIGINT} delete from pms_brand SELECT LAST_INSERT_ID() insert into pms_brand (name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story) values (#{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}) SELECT LAST_INSERT_ID() insert into pms_brand name, first_letter, sort, factory_status, show_status, product_count, product_comment_count, logo, big_pic, brand_story, #{name,jdbcType=VARCHAR}, #{firstLetter,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{factoryStatus,jdbcType=INTEGER}, #{showStatus,jdbcType=INTEGER}, #{productCount,jdbcType=INTEGER}, #{productCommentCount,jdbcType=INTEGER}, #{logo,jdbcType=VARCHAR}, #{bigPic,jdbcType=VARCHAR}, #{brandStory,jdbcType=LONGVARCHAR}, update pms_brand id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR}, update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR}, brand_story = #{row.brandStory,jdbcType=LONGVARCHAR} update pms_brand set id = #{row.id,jdbcType=BIGINT}, name = #{row.name,jdbcType=VARCHAR}, first_letter = #{row.firstLetter,jdbcType=VARCHAR}, sort = #{row.sort,jdbcType=INTEGER}, factory_status = #{row.factoryStatus,jdbcType=INTEGER}, show_status = #{row.showStatus,jdbcType=INTEGER}, product_count = #{row.productCount,jdbcType=INTEGER}, product_comment_count = #{row.productCommentCount,jdbcType=INTEGER}, logo = #{row.logo,jdbcType=VARCHAR}, big_pic = #{row.bigPic,jdbcType=VARCHAR} update pms_brand name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR}, where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR}, brand_story = #{brandStory,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=BIGINT} update pms_brand set name = #{name,jdbcType=VARCHAR}, first_letter = #{firstLetter,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER}, factory_status = #{factoryStatus,jdbcType=INTEGER}, show_status = #{showStatus,jdbcType=INTEGER}, product_count = #{productCount,jdbcType=INTEGER}, product_comment_count = #{productCommentCount,jdbcType=INTEGER}, logo = #{logo,jdbcType=VARCHAR}, big_pic = #{bigPic,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} ================================================ FILE: mall-tiny-swagger/src/main/resources/generator.properties ================================================ jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=root ================================================ FILE: mall-tiny-swagger/src/main/resources/generatorConfig.xml ================================================
================================================ FILE: mall-tiny-swagger/src/test/java/com/macro/mall/tiny/MallTinyApplicationTests.java ================================================ package com.macro.mall.tiny; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest public class MallTinyApplicationTests { public void contextLoads() { } } ================================================ FILE: pom.xml ================================================ 4.0.0 com.macro.mall mall-learning-teach 1.0-SNAPSHOT pom mall-tiny-boot mall-tiny-mybatis mall-tiny-generator mall-tiny-lombok mall-tiny-hutool mall-tiny-swagger mall-tiny-redis mall-tiny-rabbit mall-tiny-01 mall-tiny-02 mall-tiny-03 mall-tiny-04 mall-tiny-05 mall-tiny-06 mall-tiny-07 mall-tiny-08 mall-tiny mall-tiny-stream mall-tiny-docker mall-tiny-plus org.springframework.boot spring-boot-starter-parent 2.7.5 UTF-8 UTF-8 1.8 true http://192.168.3.101:2375 0.40.2 1.4.5 5.3.2 1.2.14 5.8.9 3.0.0 1.6.0 1.6.0 1.4.1 3.5.10 2.2.2 8.0.29 2.7.5 0.9.1 2.5.0 7.2 8.4.5 2.3.1 javax.xml.bind jaxb-api ${jaxb-api.version} aliyunmaven aliyun https://maven.aliyun.com/repository/public central2 central2 https://repo1.maven.org/maven2/