Chiriri's blog Chiriri's blog
首页
  • Java

    • JavaSE
    • JavaEE
    • 设计模式
  • Python

    • Python
    • Python模块
    • 机器学习
  • Golang

    • Golang
    • gRPC
  • 服务器

    • Linux
    • MySQL
    • NoSQL
    • Kubernetes
  • 项目

    • 传智健康
    • 畅购商城
  • Hadoop生态

    • Hadoop
    • Zookeeper
    • Hive
    • Flume
    • Kafka
    • Azkaban
    • Hbase
    • Scala
    • Spark
    • Flink
  • 大数据项目

    • 离线数仓
  • 青训营

    • 第四届青训营
  • HTML

    • HTML
    • JavaScript
  • Vue

    • Vue2
    • TypeScript
    • Vue3
    • Uni-APP
  • 数据结构与算法
  • C语言
  • 考研数据结构
  • 计算机组成原理
  • 计算机操作系统
  • Java基础

    • Java基础
    • Java集合
    • JUC
    • JVM
  • 框架

    • Spring
    • Dubbo
    • Spring Cloud
  • 数据库

    • MySQL
    • Redis
    • Elasticesearch
  • 消息队列

    • RabbitMQ
    • RocketMQ
  • 408

    • 计算机网络
    • 操作系统
    • 算法
  • 分类
  • 标签
  • 归档
  • 导航站
GitHub (opens new window)

Iekr

苦逼后端开发
首页
  • Java

    • JavaSE
    • JavaEE
    • 设计模式
  • Python

    • Python
    • Python模块
    • 机器学习
  • Golang

    • Golang
    • gRPC
  • 服务器

    • Linux
    • MySQL
    • NoSQL
    • Kubernetes
  • 项目

    • 传智健康
    • 畅购商城
  • Hadoop生态

    • Hadoop
    • Zookeeper
    • Hive
    • Flume
    • Kafka
    • Azkaban
    • Hbase
    • Scala
    • Spark
    • Flink
  • 大数据项目

    • 离线数仓
  • 青训营

    • 第四届青训营
  • HTML

    • HTML
    • JavaScript
  • Vue

    • Vue2
    • TypeScript
    • Vue3
    • Uni-APP
  • 数据结构与算法
  • C语言
  • 考研数据结构
  • 计算机组成原理
  • 计算机操作系统
  • Java基础

    • Java基础
    • Java集合
    • JUC
    • JVM
  • 框架

    • Spring
    • Dubbo
    • Spring Cloud
  • 数据库

    • MySQL
    • Redis
    • Elasticesearch
  • 消息队列

    • RabbitMQ
    • RocketMQ
  • 408

    • 计算机网络
    • 操作系统
    • 算法
  • 分类
  • 标签
  • 归档
  • 导航站
GitHub (opens new window)
  • JavaSE

  • JavaEE

  • Linux

  • MySQL

  • NoSQL

  • Python

  • Python模块

  • 机器学习

  • 设计模式

  • 传智健康

  • 畅购商城

  • 博客项目

  • JVM

  • JUC

  • Golang

  • Kubernetes

  • 硅谷课堂

  • C

  • 源码

  • 神领物流

  • RocketMQ

  • 短链平台

    • 平台业务介绍与搭建
      • 商用短链平台业务介绍和需求背景说明
      • ⽅法论
        • PEST⽅法论
        • AARRR方法论
        • SWOT态势分析法
        • SMART方法论
      • 功能需求介绍和微服务拆分讲解
        • 项目背景说明
        • 产品目标(OKR)
        • 名词解释
        • 成员职责
        • 产品需求描述
        • 产品首页
        • 产品报价页
        • 登录注册页
        • 分组管理页
        • 短链管理页
        • 创建短链页
        • 短链数据分析页
        • 流量包管理页
        • 流量包充值
        • 项目里程碑
        • 微服务拆分和技术栈版本说明
        • Maven聚合⼯程拆分
        • 微服务技术栈+前置中间件版本说明
        • 业务架构图讲解
      • 云Linux服务器选配和常⽤中间件环境安装
        • 云服务器配置和搭建
        • Docker
        • Mysql8.0+Redis6.X
        • Nacos2.x+Mysql8
        • RabbitMQ
      • 项⽬创建+git代码管理+开发分层规范
        • Maven聚合⼯程创建微服务项⽬
        • dcloud-common通⽤模块配置
        • dcloud-account账号+流量包微服务
        • git代码管理
        • 阿⾥编码规范⾥⾯Manager分层介绍-专⽤名词和POJO实体类约定
      • 统⼀接⼝响应协议-响应⼯具类封装
        • 统⼀业务状态码
        • JsonData⼯具类
      • ⾃定义全局异常+处理器handler
        • ⾃定义全局异常
        • ⾃定义异常处理器
      • common通⽤⼯具和时间格式化⼯具
        • 时间格式化⼯具类封装
        • Json序列化⼯具类封装
        • 通用工具
      • 账号微服务和流量包数据库表+索引规范
        • account
        • traffic
        • 流量包业务模型概念补充
      • Mybatis-plus-generator代码⾃动⽣成⼯具
      • 账号微服务注册Nacos+配置⽂件增加
    • 账号微服务注册、压测与架构核心技术
    • 海量数据下的分库分表
  • 后端
  • 短链平台
Iekr
2024-02-01
目录

平台业务介绍与搭建

# 平台业务介绍与搭建

# 商用短链平台业务介绍和需求背景说明

什么是短链平台,应⽤场景有哪些

image-20240201115008562

业务背景:为啥需要短链?

  • 公司电商产品推广、业务活动⻚、广告落地⻚ 缺少实时【数据反馈和渠道效果分析】

  • 老项⽬业务推广【没人维护,⽆法做埋点】需要统计效果

    例⼦ https://tongji.baidu.com/web/demo/overview/index?siteId=16847648

  • APP 和营销活动发送营销短信链接过⻓,【浪费短信发送费⽤】 image-20240201115324793

  • 国内【反垄断后】微信、抖⾳、淘宝 流量互通,很多知识付费公司需要做 私域流量、社群运营

  • 可以对外做产品输出,实现商业化能力增加公司营收

  • 积累终端数据和人群数据,为公司未来产品人群做策略助力

  • 更多。。。。

盈利点

  • ⽤户按量付费,根据流量包选择付费购买对应的套餐
  • 不同流量包权益不⼀样
    • 每天可以创建的短链次数不⼀样
    • 流量包使⽤时间限制、⽀持流量包叠加
    • 注册⽤户每天有⼀定免费使⽤次数,但是不能查看数据

短链平台产品⽬标

  • 满足公司现有业务的营销推广需求、数据分析和拉新促活能力
  • 对外进行付费商⽤,⽀持企业私有化部署
  • ⾸年⽇活⽤户: 10 万
  • ⾸年⽇新增短链数据:10 万 * 50 = 500 万
  • 年新增⽤户数:50 万 / 1 年
  • 年营收⽬标: 10 万付费⽤户 * 客单价 200 元 = 2 千万
  • 新增短链:50 条 / ⽤户每⽇
  • 年私有化部署⽤户数:1K ⽤户 * 3 万 / 单价 = 3 千万

# ⽅法论

避免⽆章法管理和做事,为什么需要学⽅法论

⽅法论:通俗来说就是【做事套路,解决问题的⽅法 **(⼿段 / 途径 / ⼯具)**】 喜欢学术性的介绍可以百度更多的

image-20240201115537522

你是否被问过类似这些 ⽅法论技术⾯试题(考查你的逻辑思维是否清晰)

  • 线上接⼝响应慢,你会怎么定位问题
    • 监控统计问题、机房问题、某个节点问题、服务器带宽问 题、CPU 内存问题、⽹关问题、业务逻辑问题、并发量问 题、数据库问题
  • sql 查询耗时久,你会怎么排查优化
  • java 应⽤发⽣了 OOM,你会怎么排查问题
  • 订单⽀付成功率下滑严重,你会怎么排查问题
  • RabbitMQ 和 RocketMQ 技术选型,你会怎么选,考虑哪些因素

为什么要学⽅法论,常⻅的有哪些是必备的

  • 新业务规划(PEST)

    • 上线被下架 (K12 教育、互联⽹⾦融)
  • 运营推⼴(AARRR)

    • 不被产品牵着⾛,但是也不盲⽬
  • SWOT 态势分析法

    • 个⼈成⻓、技术解决⽅案
  • 团队管理(SMART)

    • 合适的⼈合适的事

    • ⼯作量分配

  • 跳槽⾯试(逻辑考查)

# PEST ⽅法论

公司⾏业前景分析 - PEST ⽅法论 - 选择⼤于努⼒

PEST ⽅法论:指的是政治(Political)、经济(Economic)、社会(Social)和技术(Technological)

  • 政治环境主要是看我们的国家现在是否⿎励相关的业务
  • 经济环境⼜可以分为宏观经济和微观经济,包括居⺠消费⽔平、产业结构
  • 社会环境则是说跟社会的⻛俗习惯是否吻合
  • 技术环境当然就是说的我们的技术实⼒(ASML 光刻机)

案例:BAT 区块链⼤⽜找你开发特币交易平台 app

  • P 国家出台了相关政策、法律法规监管
  • E ⼤家有闲钱,现在⼈居可⽀配收⼊⾼
  • S ⼈⼝规模⼤,⽣活⽅式改变,投资理财越来越多
  • T 团队已有对应的技术,⾏业领先,移动互联⽹成熟

案例延伸

  • 互联⽹⾦融贷款产品 p2p
  • ⾼中在线辅导产品
  • 初中同学找你开发电商平台 - 对标淘宝、拼多多

聪明的⼈做聪明的事,避免⾃⼰⼊坑

  • ⾏业选择错:⽩忙活⼏年,领域专家也吃亏
  • 公司裁员、倒闭发不出⼯资:⽩⼲⼏个⽉,⾟苦⼤半年,⼀夜回到解放前
  • ⼤⼚也⼀样:字节、阿⾥、腾讯、京东等

# AARRR 方法论

技术 Leader 必备⽅法论 - ⽤户增⻓的数据分析模型 AARRR

AARRR 是 Acquisition、Activation、Retention、Revenue、Referral 五个单词的缩写,对应⽤户⽣命周期中的 5 个重要环节。

通俗来说就是⼀个产品从 0~1 到 100 的⽅法论,指引产品运营在不同的产品运营阶段,思考哪些关键节点,更好各个节点的指标数据

AARRR 详细解释:

  • 获取:新⽤户⾸单免费 / 低价 (瑞幸、拼多多)、⼚商预装 (⼿机)、买量投放
  • 激活:app 推送、短信推送、产品价值激活
  • 留存:签到、活动短信推送、平台价值提供
  • 收益:平台⼴告、电商变现、付费会员、融资、软件服务
  • 传播:好友助⼒、分享抽奖、兄弟砍我⼀⼑

# SWOT 态势分析法

技术 Leader 必备⽅法论 - SWOT 态势分析法 - 个⼈能⼒与技术解决⽅案

SWOT 态势分析法:⽤来确定企业⾃身的竞争优势、劣势、外部市场的机会和威胁,从⽽将公司的战略与公司内部资源、外部环境有机地结合起来的⼀种科学的分析⽅法。

4 个单词的缩写 优势 = strength、劣势 = weakness、机会 = opportunity、威胁 = threats

优势和弱势是内部环境的分析,机会和威胁是对于外部环境的分析

  • 外部的机会正好是你的优势,赶紧利⽤起来
  • 外部的机会但是你的劣势,需要改进
  • ⾃身具有优势但外部存在威胁,就需要时刻思考、保持警惕
  • 是威胁⼜是你的劣势,就规避并消除

image-20240201121318177

案例应⽤场景:个⼈做技术 Leader 能⼒分析(⼯作 3 年,⾼级 java,技术能⼒不错,项⽬组⻓刚离职)

  • 优势:技术不错、对公司业务熟悉(个⼈内部)
  • 劣势:项⽬管理能⼒不⾜、PPT 汇报能⼒不⾜(个⼈内部)
  • 机会:独⽴负责的项⽬把控、直接和领导汇报,成为管理层(个⼈外部)
  • 威胁:项⽬的规范机制没有建⽴、项⽬的核⼼难点没有攻破、加班⽐较多(个⼈外部)

技术解决⽅案分析(团队熟悉 RabbitMQ,新来的组⻓熟悉 RocketMQ,技术选型思考)

  • 优势:RabbitMQ 团队多⼈⽤过、AMQP 跨语⾔、模型 API 丰富(团队内部)
  • 劣势:阅读过源码的⼈过少,Erlang 开发,⼆次修改不容易,项⽬组⻓对这个不熟悉(团队内部)
  • 机会:项⽬可以快速上线,减少踩坑(团队外部)
  • 威胁:未来可能有更强⼤的 MQ 产品出现或公司改动架构(团队外部)

总结:根据 SWOT 进⾏充分分析,然后进⾏取舍选择,考虑更全⾯(对⽐没⽤这个分析你会怎么选择)

# SMART 方法论

技术 Leader 必备⽅法论 SMART 衡量需求、⼯作的利器

SMART ⽅法论:源于国外管理⼤师的《管理的实践》,是为了利于员⼯更加明确⾼效地⼯作,更是为了管理者将来对员⼯实施绩效考核提供了考核⽬标和考核标准,使考核更加科学化、规范化。

SMART ⽅法论是 5 个单词的缩写,SMART 原则【⽬标管理、设置】:

  • Specific:⽬标要具体
  • Measurable:⽬标成果要可衡量(量化)
  • Attainable:⽬标要可实现,避免过⾼ / 过低
  • Relevant:与其他⽬标有⼀定的相关性
  • Time bound:⽬标必须有明确的期限

意义:在制定⼯作⽬标或者任务⽬标时,考虑⼀下⽬标与计划是不是 SMART 化的。只有具备 SMART 化的计划才是具有良好可实施性的,也才能指导保证计划得以实现

案例:短链平台项⽬组成员,每天需要开晨会 - 并记录 (周⼀ 早上团队开周会,每个⼈列出本周事项安排)

岗位 运营 + 市场 设计 技术 产品
负责人 冰冰 Anna 二当家小 D 老王
相关人员 小 A(用户运营)小 B(内容运营)小 C(渠道投放)小 D(KA 客户) 大 V 后端:张三、李四、王五、李六前端:大 A、大 B、大 C 测试:大 D、大 G 运维:老吴 老李(产品需求)老张(数据分析)

张三的周报如下

  • 周三前完成短链平台数据库设计并输出相关 sql ⽂件(SMART)
  • 周四前完成短链平台整体架构搭建和⼯作任务拆分并评审完成(SMART)
  • 周⼆完成短链微服务开发⼯作 (SM RT)
  • 开发账号微服务 (S)
  • 解决⽼项⽬的 bug ( )
  • 周⼆完成优化电商项⽬⾥⾯的下单接⼝ RT 响应时间,把 500ms 优化到 200ms 响应(SMART)

# 功能需求介绍和微服务拆分讲解

⼩滴短链商⽤平台需求⽂档地址: https://zhuanlan.zhihu.com/p/428514067

# 项目背景说明

  • 公司电商产品推广、业务活动页、广告落地页 缺少实时【数据反馈和渠道效果分析】
  • APP 和营销活动发送营销短信链接过长,【浪费短信发送费用】
  • 老项目业务推广【没人维护,无法做埋点】需要统计效果
  • 国内【反垄断后】微信、抖音、淘宝 流量互通,很多知识付费公司需要做 私域流量、社群运营,可以对外做产品输出,实现商业化能力增加公司营收
  • 积累终端数据和人群数据,为公司未来产品人群做策略助力

为啥要有这个?做任何项目都是有原因的 1、品牌营销 2、业务赋能:帮助现有业务,减少人力、费用、提升效能 3、增加营收 3、扩展业务线:大厂都在做,而且都是新的 4、业界行业的风口

市面上有短链平台,为啥要自研? 1、多数大厂都会自研短链平台,里面产生的多种数据,存放其他公司平台是不安全的 2、自研短链,可以更多维度进行数据分析,定制业务需要的功能,不受限他人平台 3、把控好流量入口,也可以通过平台进行盈利,增加公司收入

# 产品目标(OKR)

  • 满足公司现有业务的营销推广需求、数据分析和拉新促活能力
  • 对外进行付费商用,支持企业私有化部署
  • 首年日活用户: 10 万
  • 首年日新增短链数据:10 万 * 50 = 500 万
  • 年新增用户数:50 万 / 1 年
  • 年营收目标: 10 万付费用户 * 客单价 200 元 = 2 千万
  • 新增短链:50 条 / 用户每日
  • 年私有化部署用户数:1K 用户 * 3 万 / 单价 = 3 千万

为啥要有这个? 1、没目标的项目都瞎扯淡 2、目标需要经得起老板的挑战 3、ROI 产出比是否划得来 4、能带来什么效益

# 名词解释

  • 短链:原始链接缩短后的链接

  • ABTest: 为同一个产品目标制定多个方案,通过数据分析得出更符合目标的方案

  • KA(KeyAccount):关键客户、重点客户

  • 数据埋点:通过在程序中植入代码的方式,记录用户在软件(web、app、小程序)上的操作行为的技术手段,事件是记录用户在软件中操作行为的标签,如,用户在首页的曝光事件、按钮的点击事件等

  • 公有云:指第三方提供商为用户提供的能够使用的云,比如我们经常使 用阿里云即是一种公有云

  • 私有云:拥有基础设施并可以控制在此设施上部署应用程序的方式,核心属性是专有资源

  • SaaS 服务:软件即服务,产品上用户只需要注册一个 B 端产品,就可以使用软件服务

  • 私有化部署:将程序、业务数据部署在内网的本地服务器上、将核心数据掌握在自己手中

  • 私域流量:从公域(internet)包括 平台、媒体渠道、合作伙伴等、引流到自己私域(官网、微信)的流量

  • 公域流量:指商家直接入驻平台实现流量转换, 比如拼多多、京东、淘宝、抖音等,以及内容付费行业的喜马拉雅、知乎、得到等公域流量平台

  • AARRR 模型:用户生命周期中的 5 个重要环节

    • 获取:新用户首单免费 / 低价 (瑞幸、拼多多)、厂商预装 (手机)、买量投放
    • 激活:app 推送、短信推送、产品价值激活
    • 留存:签到、活动短信推送、平台价值提供
    • 收益:平台广告、电商变现、付费会员、融资、软件服务
    • 传播:好友助力、分享抽奖、兄弟砍我一刀
  • 用户运营:根据用户行为圈数用户画像,建立分层分群,提高 AARRR 各个环节指标

  • 内容运营

    • 根据用户需求推出喜好内容,比如私域流量运营、短链平台使用技巧、案例分析等
    • 用图文、软文、视频等内容形式,在【站内、站外】多个渠道分发吸引用户,打造品牌知名度
  • 活动运营:策划活动提高 订单量、转化率、复购、日活、留存等数据,围绕 AARRR 模型进行开展

  • 渠道投放:付费投放、买量,比如百度广告、朋友圈广告等

为啥要有这个? 不同人群、岗位级看文档理解是不一样的,尽量把大家的【认知拉到统一水平线】 掌握【俗称的互联网黑话】,尽管被吐槽过,但是毋庸置疑这个是最有用的 从跳槽、沟通、面试、汇报等,都是专业性名称会让人放心点 “渗透、打法、赛道、闭环、抓手、赋能、组合拳、中台、平台化、去中心化、商业模式”

# 成员职责

  • 总负责人 1 号位:二当家小 D
  • 联系方式:xdclass6
岗位 运营 + 市场 设计 技术 产品
负责人 冰冰 Anna 二当家小 D 老王
相关人员 小 A(用户运营)小 B(内容运营)小 C(渠道投放)小 D(KA 客户) 大 V 后端:张三、李四、王五、李六前端:大 A、大 B、大 C 测试:大 D、大 G 运维:老吴 老李(产品需求)老张(数据分析

为啥要有这个? 项目业务的 1 号位,负责总把控各个事项,也是背负最大 KPI 压力的人,做的好也是最受益的人 岗位负责人:1 号位正常都是带多个项目的,很少可能只带一个项目,基本只和岗位负责人进行对接 岗位负责人负责团的组建和管理

# 产品需求描述

# 产品首页

功能场景描述:用户产品进入官网首页

前置条件:无

img

功能说明

编号 功能 点击业务规则 备注
1 产品功能 下拉产品功能列表
2 应用场景 进入应用场景页,直接触达用户
3 定价方案 进入报价页面
4 生成短链 1、登录状态下,直接生成短链并自动复制
2、未登录状态,则打开用户登录页

# 产品报价页

  • 功能场景描述:进入相关页面可以看到不同产品的价格和权益
  • 前置条件:无

# 登录注册页

  • 功能场景描述:用户注册登录
  • 前置条件:无

# 分组管理页

  • 功能场景描述:用户进入管理后台,可以新建分组、分组下可以新建短链
  • 前置条件:登录

# 短链管理页

  • 功能场景描述:用户进入管理后台,可以新建分组、分组下可以新建短链
  • 前置条件:登录

# 创建短链页

  • 功能场景描述:用户进入管理后台,选择某个分组下可以新建短链
  • 前置条件:登录

# 短链数据分析页

  • 功能场景描述:用户进入管理后台,选择短链查看访问数据情况
  • 前置条件:登录

# 流量包管理页

  • 功能场景描述:查看用户个人的流量包使用情况
  • 前置条件:登录

# 流量包充值

  • 功能场景描述:下单支付页面,用户根据自己的需要进行下单购买对应数量的流量包
  • 前置条件:登录

# 项目里程碑

节点 开始 结束 备注
需求评审 2022-1-10 2022-1-12
设计 2022-1-13 2022-1-18
前端 2022-1-16 2022-3-16 部分 UI 稿先开发
后端 2022-1-12 2022-3-16 数据库设计、架构设计、编码
联调 2022-3-10 2022-3-25 部分接口可以先对接
测试 2022-2-20 2022-3-30 测试可以提前接入编写用例
回归 2022-4-1 2022-4-5 回归测试和线上资源申请
预发布 2022-4-6 2022-4-7 预发布环境测试
上线 2022-4-8 2022-4-8 上线
日常运营和放量 2022-4-10 2022-5-5 常规运营和适当推广
产品二期 需求评审 2022-5-10 2022-5-12

# 微服务拆分和技术栈版本说明

# Maven 聚合⼯程拆分

  • dcloud-common :公共依赖包
  • dcloud-app :Flink+Kafka 实时计算
  • dcloud-account :账号 + 流量包微服务
  • dcloud-data :数据可视化微服务
  • dcloud-gateway :业务⽹关
  • dcloud-link ;短链微服务
  • dcloud-shop :流量包商品 + ⽀付微服务

# 微服务技术栈 + 前置中间件版本说明

  • JDK11
  • SpringBoot 2.5.5
  • SpringCloud 2020.0.4
  • AlibabaCloud 2021.1
  • Sharding-JDBC 4.1.1
  • Mysql 8.0
  • Nacos 2.0.2
  • Redis 6.2.4
  • RabbitQM 3.8.15
  • Kafka : wurstmeister/kafka:2.13-2.7.0
    • 为啥有 RabbitMQ 还要有 Kafka(单机写⼊ TPS 约在百万条 / 秒,最⼤的优点,就是吞吐量⾼)⼀个是业务 MQ、⼀个⼤数据流式处理的 MQ,建议分开
  • 还有更多的中间件⽤的时候再安装

# 业务架构图讲解

整体业务应⽤架构图

image-20240201161908927

各个微服务模块解析

image-20240201161937352

# 云 Linux 服务器选配和常⽤中间件环境安装

# 云服务器配置和搭建

云服务器购买

  • 阿⾥云:https://www.aliyun.com/
  • 腾讯云:https://cloud.tencent.com/
  • 亚⻢逊云:https://aws.amazon.com/
  • 腾讯云新⽤户折扣地址:https://cloud.tencent.com/act/cps/redirect?redirect=1575&cps_key=3cb5dbec53a023baba788acac8d11871&from=console
  • 阿⾥云新⽤户地址(买 2 核 8g 或者 16g 以上,如果资⾦允许 - 买 3 年):https://www.aliyun.com/minisite/goods?userCode=r5saexap&share_source=copy_link

环境问题说明

  • 务必使⽤ CentOS 7 以上版本,64 位系统,不要在 Windows 系统操作!!!!推荐是 CentOS 7.8

  • 带宽可以选择固定带宽 1M~5M 都⾏,或者按量付费(带宽过⼩,后续 Jenkins 构建推送镜像⽐较慢)

  • 避免使⽤本地⽤虚拟机(后续部署很多中间件 + 本地多个微服务开发会很卡,且容易采坑)

    • 谁都不能保证每个⼈ - 硬件组成 - 系统版本 - 虚拟机软件版本都⼀样
    • 出现问题,⼤家结合报错⽇志搜索博⽂解决
    • 少数同学 -Win7、Win8、Win10、Mac、虚拟机等等,可能存在兼容问题
  • 记得重置密码和配置⽹络安全组相关操作,我这边就不多说了

# Docker

安装 Docker

#按照依赖
yum install -y yum-utils device-mapper-persistent-data lvm2

#配置yum源(⽐较慢)
# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

#配置yum源 使⽤国内的
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

#查看版本
yum list docker-ce --showduplicates | sort -r

#1. 安装docker
yum -y install docker-ce-20.10.10-3.el7
#2. 查看docker版本
docker -v
#3. 启动docker
systemctl start docker
#4. 查看docker 启动状态
systemctl status docker


# 检查安装结果。
docker info

# 启动使⽤Docker
systemctl enable docker # 开机自启动
systemctl start docker #运⾏Docker守护进程
systemctl stop docker #停⽌Docker守护进程
systemctl restart docker #重启Docker守护进程

docker ps # 查看容器
docker stop 容器id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

修改镜像仓库

vim /etc/docker/daemon.json
1
{
    "debug":true,"experimental":true,
    "registry-mirrors":["https://pb5bklzr.mirror.aliyuncs.com","https://hub-mirror.c.163.com","https://docker.mirrors.ustc.edu.cn"]
}
1
2
3
4

重启服务

systemctl restart docker
1

注意:不使⽤ 1.13.1 版本,该版本在 jenkins 使⽤ docker 命令时会说找不到配置⽂件!

# Mysql8.0+Redis6.X

Mysql8.0 安装

#安装mysql8,让容器使⽤宿主机的时间,容器时间与宿主机时间同步
docker run \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=xdclass.net168 \
-v /home/data/mysql/data:/var/lib/mysql:rw \
-v /etc/localtime:/etc/localtime:ro \
--name xdclass_mysql \
--restart=always \
-d mysql:8.0
1
2
3
4
5
6
7
8
9

mysql 密码为: xdclass.net168

配置连接数,为分库分表做准备

#Mysql⼯具连接测试
#连接数配置
show variables like '%max_connections%';
set GLOBAL max_connections=5000;
set GLOBAL mysqlx_max_connections=5000;
1
2
3
4
5

Redis6 安装

docker run -itd --name xdclass-redis1 --restart=always -p 6379:6379 -v /mydata/redis/data:/data redis:6.2.4 --requirepass xdclass.net
1

redis 密码为: xdclass.net

# 进⼊容器的redis
docker exec -it xdclass-redis1 redis-cli
# ⼯具测试连接,需要登录才能执行命令
auth xdclass.net
1
2
3
4

# Nacos2.x+Mysql8

Nacos2.x+Mysql8 配置持久化

Nacos 持久化 SQL 数据脚本,使用工具导入既可

创建数据库 nacos_config


/******************************************/
/*https://github.com/alibaba/nacos/blob/master/distribution/conf/nacos-mysql.sql */
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info   */
/******************************************/

CREATE TABLE `config_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(255) DEFAULT NULL,
  `content` longtext NOT NULL COMMENT 'content',
  `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  `src_user` text COMMENT 'source user',
  `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
  `app_name` varchar(128) DEFAULT NULL,
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  `c_desc` varchar(256) DEFAULT NULL,
  `c_use` varchar(64) DEFAULT NULL,
  `effect` varchar(64) DEFAULT NULL,
  `type` varchar(64) DEFAULT NULL,
  `c_schema` text,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info_aggr   */
/******************************************/
CREATE TABLE `config_info_aggr` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(255) NOT NULL COMMENT 'group_id',
  `datum_id` varchar(255) NOT NULL COMMENT 'datum_id',
  `content` longtext NOT NULL COMMENT '内容',
  `gmt_modified` datetime NOT NULL COMMENT '修改时间',
  `app_name` varchar(128) DEFAULT NULL,
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段';


/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info_beta   */
/******************************************/
CREATE TABLE `config_info_beta` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(128) NOT NULL COMMENT 'group_id',
  `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
  `content` longtext NOT NULL COMMENT 'content',
  `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',
  `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  `src_user` text COMMENT 'source user',
  `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info_tag   */
/******************************************/
CREATE TABLE `config_info_tag` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(128) NOT NULL COMMENT 'group_id',
  `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
  `tag_id` varchar(128) NOT NULL COMMENT 'tag_id',
  `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
  `content` longtext NOT NULL COMMENT 'content',
  `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  `src_user` text COMMENT 'source user',
  `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_tags_relation   */
/******************************************/
CREATE TABLE `config_tags_relation` (
  `id` bigint(20) NOT NULL COMMENT 'id',
  `tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
  `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(128) NOT NULL COMMENT 'group_id',
  `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
  `nid` bigint(20) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`nid`),
  UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = group_capacity   */
/******************************************/
CREATE TABLE `group_capacity` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
  `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
  `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
  `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
  `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
  `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
  `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = his_config_info   */
/******************************************/
CREATE TABLE `his_config_info` (
  `id` bigint(64) unsigned NOT NULL,
  `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `data_id` varchar(255) NOT NULL,
  `group_id` varchar(128) NOT NULL,
  `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
  `content` longtext NOT NULL,
  `md5` varchar(32) DEFAULT NULL,
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `src_user` text,
  `src_ip` varchar(50) DEFAULT NULL,
  `op_type` char(10) DEFAULT NULL,
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  PRIMARY KEY (`nid`),
  KEY `idx_gmt_create` (`gmt_create`),
  KEY `idx_gmt_modified` (`gmt_modified`),
  KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';


/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = tenant_capacity   */
/******************************************/
CREATE TABLE `tenant_capacity` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
  `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
  `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
  `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
  `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
  `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
  `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';


CREATE TABLE `tenant_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `kp` varchar(128) NOT NULL COMMENT 'kp',
  `tenant_id` varchar(128) default '' COMMENT 'tenant_id',
  `tenant_name` varchar(128) default '' COMMENT 'tenant_name',
  `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
  `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
  `gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
  `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';

CREATE TABLE `users` (
	`username` varchar(50) NOT NULL PRIMARY KEY,
	`password` varchar(500) NOT NULL,
	`enabled` boolean NOT NULL
);

CREATE TABLE `roles` (
	`username` varchar(50) NOT NULL,
	`role` varchar(50) NOT NULL,
	UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
);

CREATE TABLE `permissions` (
    `role` varchar(50) NOT NULL,
    `resource` varchar(255) NOT NULL,
    `action` varchar(8) NOT NULL,
    UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);

INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);

INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205

Nacos2.x 安装(⽣产环境让运维⼈员配置⽹络,不暴露公⽹),配置中⼼需要加认证信息才可以访问

开源版本的 Nacos server 配置中,不会对客户端鉴权,即任何能访问 Nacos server 的⽤户,都可以直接获取 Nacos 中存储的配置,假如⼀个⿊客攻进了企业内⽹,就能获取所有的业务配置,这样肯定会有安全隐患。

⽐如请求 http://112.74.55.160:8848/nacos/v1/cs/configs?dataId=dcloud-account-service-dev.yaml&group=DEFAULT_GROUP

需要先开启 Nacos server 的鉴权,在 Nacos server 上修改 application.properties 中的 nacos.core.auth.enabled 值为 true 即可,下面我们安装以环境变量形式开始鉴权

注意 mysql 的地址和密码需要改成服务器的

docker run -d \
-e NACOS_AUTH_ENABLE=true \
-e MODE=standalone \
-e JVM_XMS=128m \
-e JVM_XMX=128m \
-e JVM_XMN=128m \
-p 8848:8848 \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e MYSQL_SERVICE_HOST=120.79.150.146 \
-e MYSQL_SERVICE_PORT=3306 \
-e MYSQL_SERVICE_USER=root \
-e MYSQL_SERVICE_PASSWORD=xdclass.net168 \
-e MYSQL_SERVICE_DB_NAME=nacos_config \
-e MYSQL_SERVICE_DB_PARAM='characterEncoding=utf8&connectTimeout=10000&socketTimeout=30000&autoReconnect=true&useSSL=false' \
--restart=always \
--privileged=true \
-v /home/data/nacos/logs:/home/nacos/logs \
--name xdclass_nacos_auth \
nacos/nacos-server:2.0.2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

访问管理界面:http://120.79.150.146:8848/nacos

注意:重启机器可能导致 nacos 无法启动,需要手动连接一下 msql 客户端重新启动 nacos

默认登录账号密码 账户: nacos 密码: nacos 新版本 nacos 可能遇到 Client not connected, current status:STARTING , Nacos2.0版本相比1.X新增了gRPC的通信方式 ,导致了一个问题,需要再服务器防火墙多开两个端口。

主端口 与主端口偏移量 需要开放的端口 描述
8848 1000 9848 客户端 gRPC 请求服务端端口,用于客户端向服务端发起连接和请求
8848 1001 9849 服务端 gRPC 请求服务端端口,用于服务间同步等
version: '3.8'

services:
  nacos:
    image: nacos/nacos-server:latest
    container_name: nacos-standalone-mysql
    environment:
      - MODE=standalone
      - SPRING_DATASOURCE_PLATFORM=mysql
      - MYSQL_SERVICE_HOST=10.1.31.133
      - MYSQL_SERVICE_PORT=3306
      - MYSQL_SERVICE_USER=root
      - MYSQL_SERVICE_PASSWORD=root
      - MYSQL_SERVICE_DB_NAME=ry-config
    ports:
      - "8848:8848"
      - "9848:9848"
      - "9849:9849"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# RabbitMQ

RabbitMQ 安装

docker run -d --name xd_rabbit --restart=always -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=password -p 15672:15672 -p 5672:5672 rabbitmq:3.8.15-management
1

⽹络安全组记得开放以下端⼝

4369 erlang 发现⼝
5672 client 端通信⼝
15672 管理界⾯ ui 端⼝
25672 server 间内部通信⼝
1
2
3
4

访问管理界面:http://120.79.150.146:15672

默认登录账号密码 账户: admin 密码: password

# 项⽬创建 + git 代码管理 + 开发分层规范

# Maven 聚合⼯程创建微服务项⽬

注意选择 jdk11

创建项⽬父工程 dcloud-short-link ,记得删除聚合⼯程 src ⽬录,再创建各个微服务模块

  • dcloud-common :公共依赖包
  • dcloud-app :Flink+Kafka 实时计算
  • dcloud-account :账号 + 流量包微服务
  • dcloud-data :数据可视化微服务
  • dcloud-gateway :业务⽹关
  • dcloud-link ;短链微服务
  • dcloud-shop :流量包商品 + ⽀付微服务

在父工程,添加依赖

   <properties>

        <!--JDK版本,如果是jdk8则这里是 1.8-->
        <java.version>11</java.version>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>

        <spring.boot.version>2.5.5</spring.boot.version>
        <spring.cloud.version>2020.0.4</spring.cloud.version>
        <alibaba.cloud.version>2021.1</alibaba.cloud.version>

        <mybatisplus.boot.starter.version>3.4.0</mybatisplus.boot.starter.version>
        <lombok.version>1.18.16</lombok.version>
        <commons.lang3.version>3.9</commons.lang3.version>
        <commons.codec.version>1.15</commons.codec.version>

        <xxl-job.version>2.3.0</xxl-job.version>

        <aliyun.oss.version>3.10.2</aliyun.oss.version>

        <captcha.version>1.1.0</captcha.version>

        <redission.version>3.10.1</redission.version>
        <jwt.version>0.7.0</jwt.version>
        <sharding-jdbc.version>4.1.1</sharding-jdbc.version>

        <junit.version>4.12</junit.version>
        <druid.version>1.1.16</druid.version>

        <!--跳过单元测试-->
        <skipTests>true</skipTests>
        <docker.image.prefix>dcloud</docker.image.prefix>

    </properties>




    <!--锁定版本-->
    <dependencyManagement>
        <dependencies>
            <!--https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies/2.3.3.RELEASE-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies/Hoxton.SR8-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-alibaba-dependencies/2.2.1.RELEASE-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${alibaba.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>



            <!--mybatis plus和springboot整合-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatisplus.boot.starter.version}</version>
            </dependency>

            <!--https://mvnrepository.com/artifact/org.projectlombok/lombok/1.18.16-->
            <!--scope=provided,说明它只在编译阶段生效,不需要打入包中, Lombok在编译期将带Lombok注解的Java文件正确编译为完整的Class文件-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
                <!--<scope>provided</scope>-->
            </dependency>



            <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>${commons.lang3.version}</version>
            </dependency>

            <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
            <!--用于加密-->
            <dependency>
                <groupId>commons-codec</groupId>
                <artifactId>commons-codec</artifactId>
                <version>${commons.codec.version}</version>
            </dependency>



            <!--验证码kaptcha依赖包-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>kaptcha-spring-boot-starter</artifactId>
                <version>${captcha.version}</version>
            </dependency>


            <!--阿里云oss-->
            <dependency>
                <groupId>com.aliyun.oss</groupId>
                <artifactId>aliyun-sdk-oss</artifactId>
                <version>${aliyun.oss.version}</version>
            </dependency>



            <!-- JWT相关 -->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>${jwt.version}</version>
            </dependency>



            <!--分布式锁-->
            <dependency>
                <groupId>org.redisson</groupId>
                <artifactId>redisson</artifactId>
                <version>${redission.version}</version>
            </dependency>



            <!--https://mvnrepository.com/artifact/org.apache.shardingsphere/sharding-jdbc-spring-boot-starter-->
            <dependency>
                <groupId>org.apache.shardingsphere</groupId>
                <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
                <version>${sharding-jdbc.version}</version>
            </dependency>


            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>


            <!-- https://mvnrepository.com/artifact/com.xuxueli/xxl-job-core -->
            <dependency>
                <groupId>com.xuxueli</groupId>
                <artifactId>xxl-job-core</artifactId>
                <version>${xxl-job.version}</version>
            </dependency>

        </dependencies>
    </dependencyManagement>

    <!-- 代码库 -->
    <repositories>
        <repository>
            <id>maven-ali</id>
            <url>http://maven.aliyun.com/nexus/content/groups/public//</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
                <updatePolicy>always</updatePolicy>
                <checksumPolicy>fail</checksumPolicy>
            </snapshots>
        </repository>
    </repositories>


    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <!--module不用添加打包版本信息-->
    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring.boot.version}</version>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
    </build>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

# dcloud-common 通⽤模块配置

注意:Hoxton.M2 版本之后不再使用 Ribbon 而是使用 spring-cloud-loadbalancer,所以不引入 spring-cloud-loadbalancer 会报错,所以加入 spring-cloud-loadbalancer 依赖 并且在 nacos 中排除 ribbon 依赖,不然 loadbalancer 无效

pom ⽂件配置

 <dependencies>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--项目中添加 spring-boot-starter-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <!--数据库连接-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--mybatis plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>


        <!--单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>


        <!--redis客户端-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>



        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!--用于加密-->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>

        <!-- JWT相关 -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>


        <!--redisson分布式锁-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
        </dependency>

        <!--Hoxton.M2版本之后不再使用Ribbon而是使用spring-cloud-loadbalancer,所以不引入spring-cloud-loadbalancer会报错,所以加入spring-cloud-loadbalancer依赖 并且在nacos中排除ribbon依赖,不然loadbalancer无效 -->

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>


        <!--配置中心, 留坑,后续用的时候再讲,解决方式,看springboot官方文档版本更新说明-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>


        <!--Feign远程调用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>


        <!--限流依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>



        <!--限流持久化到nacos-->

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>



        <!--Springboot项目整合spring-kafka依赖包配置-->
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>



        <!--引入AMQP-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>


        <!--spring cache依赖包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>


        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
        </dependency>


        <!-- https://mvnrepository.com/artifact/com.xuxueli/xxl-job-core -->
        <!--分布式调度-->
        <dependency>
            <groupId>com.xuxueli</groupId>
            <artifactId>xxl-job-core</artifactId>
        </dependency>


        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

    </dependencies>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

# dcloud-account 账号 + 流量包微服务

如果要使用 common 模块中的依赖,可以直接依赖 common 模块进行使用,其他模块使用 common 中的依赖类似

需要添加 common 模块有以下几个项目

  • dcloud-account :账号 + 流量包微服务
  • dcloud-data :数据可视化微服务
  • dcloud-link ;短链微服务
  • dcloud-shop :流量包商品 + ⽀付微服务

pom 文件配置

    <dependencies>
        <dependency>
            <groupId>net.xdclass</groupId>
            <artifactId>dcloud-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
1
2
3
4
5
6
7

# git 代码管理

Git 是⼀个版本管理⼯具,其作⽤就是可以让你更好的管理你的程序,⽐如你原来提交过的内容,以后虽然修改了,但是通过 git 这个⼯具,可以把你原来提交的内容重现出来,这样对于你后来才意识到的⼀些错误的更改,可以进⾏还原

基于 git 协议的代码仓库

  • github 全球最⼤同性交友社区
  • gitee 开源中国
  • gitlab 开源的 git 仓库平台,阿⾥等⼤⼚就是基于这个搭建
  • codeup 阿⾥云上的免费 git 仓库

本地安装⽂档 https://www.runoob.com/git/git-install-setup.html https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%AE%89%E8%A3%85-Git

项⽬加⼊ git 管理(是公钥设置,不是秘钥设置),如之前本地环境中有公钥,直接在 gitee 中添加公钥既可

# 本地⽣成公钥
ssh-keygen -t rsa -C "[email protected]"
1
2
  • ⽣成公钥⽂档:https://gitee.com/help/articles/4181
  • 设置公钥⽂档:https://gitee.com/help/articles/4191#article-header0

配置 gitignore ⽂件,根⽬录创建⽂件 .gitignore

# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
.DS_Store
.idea
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

设置一下 git 邮箱和用户名,如果以前有环境的可忽略

git config --global user.name "Iekr"
git config --global user.email "[email protected]"
1
2

初始化并提交

git init
touch README.md
git add ./
git commit -m "first commit"
git remote add origin [email protected]:Iekrwh/dcloud-short-link.git
git push -u origin "master"
1
2
3
4
5
6

# 阿⾥编码规范⾥⾯ Manager 分层介绍 - 专⽤名词和 POJO 实体类约定

  • 开发⼈员:张三、李四、王五
  • ⼀定要避免单点故障
    • ⼀个微服务起码两个⼈熟悉:⼀个是主程⼀个是技术 leader , 推荐是团队⾥⾯两个开发⼈员

N ⽅库说明:

  • ⼀⽅库:本⼯程内部⼦项⽬模块依赖的库 (jar 包)。
  • ⼆⽅库:公司内部发布到中央仓库,可供公司内部其它应⽤依赖的库 (jar 包)。
  • 三⽅库:公司之外的开源库 (jar 包)。

POJO 实体类

POJO (Plain Ordinary Java Object): 在⼿册中,POJO 专指只有 setter /getter/toString 的简单类,包括 DO/DTO/BO/VO 等,禁⽌命名成 xxxPOJO

各个层级约束规范

  • Service/DAO 层⽅法命名规约

    1. 获取单个对象的⽅法⽤ get 做前缀。
    2. 获取多个对象的⽅法⽤ list 做前缀,复数形式结尾如:listObjects。
    3. 获取统计值的⽅法⽤ count 做前缀。
    4. 插⼊的⽅法⽤ save/insert 做前缀。
    5. 删除的⽅法⽤ remove/delete 做前缀。
    6. 修改的⽅法⽤ update 做前缀。
  • 领域模型命名规约

    1. 数据对象:xxxDO,xxx 即为数据表名。
    2. ⼀般数据传输对象:xxxDTO,xxx 为业务领域相关的名称,项⽬⾥⾯也⽤ VO。
    3. 展示对象:xxxVO,也就是响应给前端的实体包装类。
    4. 接收前端 json 对象请求的命名为 XXXRequest

Manager 分层说明 通⽤业务处理层,它有如下特征

  • 对第三⽅平台封装的层,预处理返回结果及转化异常信息
  • 对 Service 层通⽤能⼒的下沉,如缓存⽅案、中间件通⽤处理;
  • 与 DAO 层交互,对多个 DAO 的组合复⽤。

image-20240201174749398

更多开发规范,可以参考阿⾥巴巴编码⼿册

# 统⼀接⼝响应协议 - 响应⼯具类封装

# 统⼀业务状态码

状态码定义约束,共 6 位数,前三位代表服务,后 3 位代表接⼝

如 商品服务 210

net.xdclass.enums.BizCodeEnum

package net.xdclass.enums;

import lombok.Getter;

/**
 * @author iekrw
 * Date:  2024/2/2/002 15:49
 * @Description 状态码定义约束,共6位数,前三位代表服务,后
 * 3位代表接⼝
 * * ⽐如 商品服务210,购物⻋是220、⽤户服务230,403代表权限
 */
public enum BizCodeEnum {
    /**
     * 短链分组
     */
    GROUP_REPEAT(23001, "分组名重复"),
    GROUP_OPER_FAIL(23503, "分组名操作失败"),
    GROUP_NOT_EXIST(23404, "分组不存在"),
    GROUP_ADD_FAIL(23505, "分组添加失败"),

    /**
     * 验证码
     */
    CODE_TO_ERROR(240001, "接收号码不合规"),
    CODE_LIMITED(240002, "验证码发送过快"),
    CODE_ERROR(240003, "验证码错误"),
    CODE_CAPTCHA_ERROR(240101, "图形验证码错误"),
    /**
     * 账号
     */
    ACCOUNT_REPEAT(250001, "账号已经存在"),
    ACCOUNT_UNREGISTER(250002, "账号不存在"),
    ACCOUNT_PWD_ERROR(250003, "账号或者密码错误"),
    ACCOUNT_UNLOGIN(250004, "账号未登录"),
    /**
     * 短链
     */
    SHORT_LINK_NOT_EXIST(260404, "短链不存在"),
    /**
     * 订单
     */
    ORDER_CONFIRM_PRICE_FAIL(280002, "创建订单-验价失 败"),
    ORDER_CONFIRM_REPEAT(280008, "订单恶意-重复提交"),
    ORDER_CONFIRM_TOKEN_EQUAL_FAIL(280009, "订单令牌缺 少"),
    ORDER_CONFIRM_NOT_EXIST(280010, "订单不存在"),
    /**
     * ⽀付
     */
    PAY_ORDER_FAIL(300001, "创建⽀付订单失败"),
    PAY_ORDER_CALLBACK_SIGN_FAIL(300002, "⽀付订单回调验 证签失败"),
    PAY_ORDER_CALLBACK_NOT_SUCCESS(300003, "⽀付宝回调 更新订单失败"),
    PAY_ORDER_NOT_EXIST(300005, "订单不存在"),
    PAY_ORDER_STATE_ERROR(300006, "订单状态不正常"),
    PAY_ORDER_PAY_TIMEOUT(300007, "订单⽀付超时"),
    /**
     * 流控操作
     */
    CONTROL_FLOW(500101, "限流控制"),
    CONTROL_DEGRADE(500201, "降级控制"),
    CONTROL_AUTH(500301, "认证控制"),
    /**
     * 流量包操作
     */
    TRAFFIC_FREE_NOT_EXIST(600101, "免费流量包不存在,联系客服"),
    TRAFFIC_REDUCE_FAIL(600102, "流量不⾜,扣减失败"),
    TRAFFIC_EXCEPTION(600103, "流量包数据异常,⽤户⽆流量包"),
    /**
     * 通⽤操作码
     */
    OPS_REPEAT(110001, "重复操作"),
    OPS_NETWORK_ADDRESS_ERROR(110002, "⽹络地址错误"),
    /**
     * ⽂件相关
     */
    FILE_UPLOAD_USER_IMG_FAIL(700101, "⽤户头像⽂件上传失败"),
    
     /**
     * 数据库路由信息
     */
    DB_ROUTE_NOT_FOUND(800101,"数据库路由信息不存在");
    
    @Getter
    private String message;
    @Getter
    private int code;

    private BizCodeEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

# JsonData ⼯具类

net.xdclass.util.JsonData

package net.xdclass.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.xdclass.enums.BizCodeEnum;

/**
 * @author iekrw
 * Date:  2024/2/2/002 15:53
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JsonData {

    /**
     * 状态码 0 表示成功
     */

    private Integer code;
    /**
     * 数据
     */
    private Object data;
    /**
     * 描述
     */
    private String msg;


    /**
     *  获取远程调用数据
     *  注意事项:
     *      支持多单词下划线专驼峰(序列化和反序列化)
     *
     * @param typeReference
     * @param <T>
     * @return
     */
    public <T> T getData(TypeReference<T> typeReference){
        return JSON.parseObject(JSON.toJSONString(data),typeReference);
    }

    /**
     * 成功,不传入数据
     * @return
     */
    public static JsonData buildSuccess() {
        return new JsonData(0, null, null);
    }

    /**
     *  成功,传入数据
     * @param data
     * @return
     */
    public static JsonData buildSuccess(Object data) {
        return new JsonData(0, data, null);
    }

    /**
     * 失败,传入描述信息
     * @param msg
     * @return
     */
    public static JsonData buildError(String msg) {
        return new JsonData(-1, null, msg);
    }



    /**
     * 自定义状态码和错误信息
     * @param code
     * @param msg
     * @return
     */
    public static JsonData buildCodeAndMsg(int code, String msg) {
        return new JsonData(code, null, msg);
    }

    /**
     * 传入枚举,返回信息
     * @param codeEnum
     * @return
     */
    public static JsonData buildResult(BizCodeEnum codeEnum){
        return JsonData.buildCodeAndMsg(codeEnum.getCode(),codeEnum.getMessage());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

# ⾃定义全局异常 + 处理器 handler

# ⾃定义全局异常

net.xdclass.exception.BizException

package net.xdclass.exception;

import lombok.Data;
import net.xdclass.enums.BizCodeEnum;

/**
 * @author iekrw
 * Date:  2024/2/2/002 16:01
 */
@Data
public class BizException extends RuntimeException {

    private int code;

    private String msg;

    public BizException(Integer code, String message) {
        super(message);
        this.code = code;
        this.msg = message;
    }



    public BizException(BizCodeEnum bizCodeEnum){
        super(bizCodeEnum.getMessage());
        this.code = bizCodeEnum.getCode();
        this.msg = bizCodeEnum.getMessage();
    }


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# ⾃定义异常处理器

net.xdclass.exception.CustomExceptionHandler

package net.xdclass.exception;

import lombok.extern.slf4j.Slf4j;
import net.xdclass.util.JsonData;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author iekrw
 * Date:  2024/2/2/002 16:02
 */
@ControllerAdvice
//@RestControllerAdvice
@Slf4j
public class CustomExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public JsonData handler(Exception e){

        if(e instanceof BizException){
            BizException bizException = (BizException) e;
            log.error("[业务异常]{}",e);
            return JsonData.buildCodeAndMsg(bizException.getCode(),bizException.getMsg());
        }else {
            log.error("[系统异常]{}",e);
            return JsonData.buildError("系统异常");
        }

    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# common 通⽤⼯具和时间格式化⼯具

# 时间格式化⼯具类封装

TimeUtil

public class TimeUtil {

    /**
     * 默认日期格式
     */
    private static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss";

    /**
     * 默认日期格式
     */
    private static final DateTimeFormatter DEFAULT_DATE_TIME_FORMATTER  = DateTimeFormatter.ofPattern(DEFAULT_PATTERN);

    private static final ZoneId DEFAULT_ZONE_ID = ZoneId.systemDefault();


    /**
     * LocalDateTime 转 字符串,指定日期格式
     * @param time
     * @param pattern
     * @return
     */
    public static String format(LocalDateTime localDateTime, String pattern){
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        String timeStr = formatter.format(localDateTime.atZone(DEFAULT_ZONE_ID));
        return timeStr;
    }


    /**
     * Date 转 字符串, 指定日期格式
     * @param time
     * @param pattern
     * @return
     */
    public static String format(Date time, String pattern){
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        String timeStr = formatter.format(time.toInstant().atZone(DEFAULT_ZONE_ID));
        return timeStr;
    }

    /**
     *  Date 转 字符串,默认日期格式
     * @param time
     * @return
     */
    public static String format(Date time){

        String timeStr = DEFAULT_DATE_TIME_FORMATTER.format(time.toInstant().atZone(DEFAULT_ZONE_ID));
        return timeStr;
    }

    /**
     * timestamp 转 字符串,默认日期格式
     *
     * @param time
     * @return
     */
    public static String format(long timestamp) {
        String timeStr = DEFAULT_DATE_TIME_FORMATTER.format(new Date(timestamp).toInstant().atZone(DEFAULT_ZONE_ID));
        return timeStr;
    }


    /**
     * 字符串 转 Date
     *
     * @param time
     * @return
     */
    public static Date strToDate(String time) {
        LocalDateTime localDateTime = LocalDateTime.parse(time, DEFAULT_DATE_TIME_FORMATTER);
        return Date.from(localDateTime.atZone(DEFAULT_ZONE_ID).toInstant());

    }


    /**
     * 获取当天剩余的秒数,用于流量包过期配置
     * @param currentDate
     * @return
     */
    public static Integer getRemainSecondsOneDay(Date currentDate) {
        LocalDateTime midnight = LocalDateTime.ofInstant(currentDate.toInstant(),
                ZoneId.systemDefault()).plusDays(1).withHour(0).withMinute(0)
                .withSecond(0).withNano(0);

        LocalDateTime currentDateTime = LocalDateTime.ofInstant(currentDate.toInstant(),
                ZoneId.systemDefault());
        long seconds = ChronoUnit.SECONDS.between(currentDateTime, midnight);
        return (int) seconds;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

# Json 序列化⼯具类封装

JsonUtil

@Slf4j
public class JsonUtil {


    private static final ObjectMapper mapper = new ObjectMapper();

    static {

        //设置可用单引号
        mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

        //序列化的时候序列对象的所有属性
        mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);

        //反序列化的时候如果多了其他属性,不抛出异常
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        //如果是空对象的时候,不抛异常
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

        //取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    }



    /**
     * 对象转为Json字符串
     * @param data
     * @return
     */
    public static String obj2Json(Object obj) {
        String jsonStr = null;
        try {
            jsonStr = mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            //e.printStackTrace();
            log.error("json格式化异常:{}",e);
        }
        return jsonStr;
    }
    /**
     * json字符串转为对象
     * @param str
     * @param valueType
     * @return
     */
    public static <T> T json2Obj(String jsonStr, Class<T> beanType) {
        T obj = null;
        try {
            obj = mapper.readValue(jsonStr, beanType);
        } catch (Exception e){
            //e.printStackTrace();
            log.error("json格式化异常:{}",e);
        }
        return obj;
    }


    /**
     * json数据转换成pojo对象list
     * @param jsonData
     * @param beanType
     * @return
     */
    public static <T> List<T> json2List(String jsonData, Class<T> beanType) {
        JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, beanType);
        try {
            List<T> list = mapper.readValue(jsonData, javaType);
            return list;
        } catch (Exception e) {
            //e.printStackTrace();
            log.error("json格式化异常:{}",e);
        }
        return null;
    }

    /**
     * 对象转为byte数组
     * @param data
     * @return
     */
    public static byte[] obj2Bytes(Object obj) {
        byte[] byteArr = null;
        try {
            byteArr = mapper.writeValueAsBytes(obj);
        } catch (JsonProcessingException e) {
            //e.printStackTrace();
            log.error("json格式化异常:{}",e);
        }
        return byteArr;
    }



    /**
     * byte数组转为对象
     * @param byteArr
     * @param valueType
     * @return
     */
    public static <T> T bytes2Obj(byte[] byteArr, Class<T> beanType) {
        T obj = null;
        try {
            obj = mapper.readValue(byteArr, beanType);
        } catch (Exception e) {
            //e.printStackTrace();
            log.error("json格式化异常:{}",e);
        }
        return obj;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

# 通用工具

CommonUtil

@Slf4j
public class CommonUtil {
    /**
     * 获取ip
     *
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) {
                // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress = "";
        }
        return ipAddress;
    }


    /**
     * 获取全部请求头
     *
     * @param request
     * @return
     */
    public static Map<String, String> getAllRequestHeader(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, String> map = new HashMap<>();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            //根据名称获取请求头的值
            String value = request.getHeader(key);
            map.put(key, value);
        }

        return map;
    }


    /**
     * MD5加密
     *
     * @param data
     * @return
     */
    public static String MD5(String data) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(data.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }

            return sb.toString().toUpperCase();
        } catch (Exception exception) {
        }
        return null;

    }


    /**
     * 获取验证码随机数
     *
     * @param length
     * @return
     */
    public static String getRandomCode(int length) {

        String sources = "0123456789";
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int j = 0; j < length; j++) {
            sb.append(sources.charAt(random.nextInt(9)));
        }
        return sb.toString();
    }


    /**
     * 获取当前时间戳
     *
     * @return
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis();
    }


    /**
     * 生成uuid
     *
     * @return
     */
    public static String generateUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }

    /**
     * 获取随机长度的串
     *
     * @param length
     * @return
     */
    private static final String ALL_CHAR_NUM = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    public static String getStringNumRandom(int length) {
        //生成随机数字和字母,
        Random random = new Random();
        StringBuilder saltString = new StringBuilder(length);
        for (int i = 1; i <= length; ++i) {
            saltString.append(ALL_CHAR_NUM.charAt(random.nextInt(ALL_CHAR_NUM.length())));
        }
        return saltString.toString();
    }


    /**
     * 响应json数据给前端
     *
     * @param response
     * @param obj
     */
    public static void sendJsonMessage(HttpServletResponse response, Object obj) {

        response.setContentType("application/json; charset=utf-8");

        try (PrintWriter writer = response.getWriter()) {
            writer.print(JsonUtil.obj2Json(obj));
            response.flushBuffer();

        } catch (IOException e) {
            log.warn("响应json数据给前端异常:{}", e);
        }


    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

# 账号微服务和流量包数据库表 + 索引规范

索引规范

  • 主键索引名为 pk_字段名;pk 即 primary key;
  • 唯⼀索引名为 uk_字段名;uk 即 unique key
  • 普通索引名则为 idx_字段名;idx 即 index 的简称

创建 dcloud_account 数据库,创建以下表

# account

account

CREATE TABLE `account` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `account_no` bigint DEFAULT NULL,
  `head_img` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '头像',
  `phone` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '手机号',
  `pwd` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',
  `secret` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '盐,用于个人敏感信息处理',
  `mail` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '邮箱',
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户名',
  `auth` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '认证级别,DEFAULT,REALNAME,ENTERPRISE,访问次数不一样',
  `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
  `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_phone` (`phone`) USING BTREE,
  UNIQUE KEY `uk_account` (`account_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# traffic

账号和流量包的关系:⼀对多

思考点:

  • 海量数据下每天免费次数怎么更新?
  • 海量数据付费流量套餐包每天次数限制怎么更新?
  • ⾼性能扣减流量包设计怎么做?
  • 流量包数据更新处理 - ⾼并发下分布式事务怎么解决

traffic

CREATE TABLE `traffic` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `day_limit` int DEFAULT NULL COMMENT '每天限制多少条,短链',
  `day_used` int DEFAULT NULL COMMENT '当天用了多少条,短链',
  `total_limit` int DEFAULT NULL COMMENT '总次数,活码才用',
  `account_no` bigint DEFAULT NULL COMMENT '账号',
  `out_trade_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '订单号',
  `level` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '产品层级:FIRST青铜、SECOND黄金、THIRD钻石',
  `expired_date` date DEFAULT NULL COMMENT '过期日期',
  `plugin_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '插件类型',
  `product_id` bigint DEFAULT NULL COMMENT '商品主键',
  `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
  `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_trade_no` (`out_trade_no`,`account_no`) USING BTREE,
  KEY `idx_account_no` (`account_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

traffic_task

CREATE TABLE `traffic_task` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `account_no` bigint DEFAULT NULL,
  `traffic_id` bigint DEFAULT NULL,
  `use_times` int DEFAULT NULL,
  `lock_state` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '锁定状态锁定LOCK  完成FINISH-取消CANCEL',
  `message_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '唯一标识',
  `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
  `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_msg_id` (`message_id`) USING BTREE,
  KEY `idx_release` (`account_no`,`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
1
2
3
4
5
6
7
8
9
10
11
12
13

image-20240202164753365

# 流量包业务模型概念补充

账号微服务流量包业务模型概念补充

image-20240202164908811

# Mybatis-plus-generator 代码⾃动⽣成⼯具

Mybatis-plus-generator 的底层是模板引擎技术,可以⾃定义⽣成的 java 类模板

基础版 mybatis-genarator,进阶版 mybatis-plus-genarator

使⽤起来和普通版的 mybatis generator ⼀样,但是这个纯代码,不⽤复杂 xml 配置任何框架。不要使⽤过多的侵⼊或者框架定制化深的内容,防⽌后续改动耦合性⾼,成本⼤

添加依赖

统⼀ Common 项⽬添加,各个项⽬测试类⾥⾯配置

        <!-- 代码自动生成依赖 begin -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!-- velocity -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>
        <!-- 代码自动生成依赖 end-->
1
2
3
4
5
6
7
8
9
10
11
12
13

生成器代码(标记 TODO 的记得修改)

dcloud-account 模块下创建 net.xdclass.db.MyBatisPlusGenerator

public class MyBatisPlusGenerator {

    public static void main(String[] args) {
        //1. 全局配置
        GlobalConfig config = new GlobalConfig();
        // 是否支持AR模式
        config.setActiveRecord(true)
                // 作者
                .setAuthor("iekr")
                // 生成路径,最好使用绝对路径,window路径是不一样的
                //TODO  TODO  TODO  TODO
                .setOutputDir("D:\\xdclass\\demo\\src\\main\\java")
                // 文件覆盖
                .setFileOverride(true)
                // 主键策略
                .setIdType(IdType.AUTO)

                .setDateType(DateType.ONLY_DATE)
                // 设置生成的service接口的名字的首字母是否为I,默认Service是以I开头的
                .setServiceName("%sService")

                //实体类结尾名称
                .setEntityName("%sDO")

                //生成基本的resultMap
                .setBaseResultMap(true)

                //不使用AR模式
                .setActiveRecord(false)

                //生成基本的SQL片段
                .setBaseColumnList(true);

        //2. 数据源配置
        DataSourceConfig dsConfig = new DataSourceConfig();
        // 设置数据库类型
        dsConfig.setDbType(DbType.MYSQL)
                .setDriverName("com.mysql.cj.jdbc.Driver")
                //TODO  TODO  TODO  TODO
                .setUrl("jdbc:mysql://192.168.130.24:3306/dcloud_account?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai")
                .setUsername("root")
                .setPassword("xdclass.net168");

        //3. 策略配置globalConfiguration中
        StrategyConfig stConfig = new StrategyConfig();

        //全局大写命名
        stConfig.setCapitalMode(true)
                // 数据库表映射到实体的命名策略
                .setNaming(NamingStrategy.underline_to_camel)

                //使用lombok
                .setEntityLombokModel(true)

                //使用restcontroller注解
                .setRestControllerStyle(true)

                // 生成的表, 支持多表一起生成,以数组形式填写
                //TODO  TODO  TODO  TODO
                .setInclude("account","traffic","traffic_task");

        //4. 包名策略配置
        PackageConfig pkConfig = new PackageConfig();
        pkConfig.setParent("net.xdclass")
                .setMapper("mapper")
                .setService("service")
                .setController("controller")
                .setEntity("model")
                .setXml("mapper");

        //5. 整合配置
        AutoGenerator ag = new AutoGenerator();
        ag.setGlobalConfig(config)
                .setDataSource(dsConfig)
                .setStrategy(stConfig)
                .setPackageInfo(pkConfig);

        //6. 执行操作
        ag.execute();
        System.out.println("======= 相关代码生成完毕  ========");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

导⼊⽣成好的代码到项目中

  • model (为啥不放 common 项⽬,如果是确定每个服务都⽤到的依赖或者类才放到 common 项⽬)
  • mapper 类接⼝拷⻉
  • resource/mapper ⽂件夹 xml 脚本拷⻉
  • controller
  • service 不拷⻉

image-20240202171801874

开启 Mybatis plus 配置控制台打印⽇志

application.yaml

spring:
  application:
    name: dcloud-account
server:
  port: 8001
#配置plus打印sql⽇志
mybatis-plus:
  configuration:
   log-impl:
    org.apache.ibatis.logging.stdout.StdOutImpl
1
2
3
4
5
6
7
8
9
10

补全一些包,accountxxxx 只是新建类 / 接口

image-20240202172734892

# 账号微服务注册 Nacos + 配置⽂件增加

在 account 模块中排除 common 模块中的 sharding-jdbc 依赖

    <dependencies>
        <dependency>
            <groupId>net.xdclass</groupId>
            <artifactId>dcloud-common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.shardingsphere</groupId>
                    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
1
2
3
4
5
6
7
8
9
10
11
12
13

启动类 AccountApplication

@MapperScan("net.xdclass.mapper")
@EnableTransactionManagement
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class AccountApplication {
    public static void main(String[] args) {
        SpringApplication.run(AccountApplication.class, args);
    }
}
1
2
3
4
5
6
7
8
9
10

application.yaml

server:
  port: 8001
spring:
  application:
    name: dcloud-account
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.130.24:8848
        username: nacos
        password: nacos
  datasource:
    url: jdbc:mysql://192.168.130.24:3306/dcloud_account?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: xdclass.net168


#配置plus打印sql⽇志
mybatis-plus:
  configuration:
    log-impl:
      org.apache.ibatis.logging.stdout.StdOutImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

多个微服务增加配置 + 代码⽣成配置映⼊

编辑 (opens new window)
上次更新: 2025/01/01, 10:09:39
RocketMQ 应用
账号微服务注册、压测与架构核心技术

← RocketMQ 应用 账号微服务注册、压测与架构核心技术→

最近更新
01
k8s
06-06
02
进程与线程
03-04
03
计算机操作系统概述
02-26
更多文章>
Theme by Vdoing | Copyright © 2022-2025 Iekr | Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式