# ChatGPT在线聊天系统付费版
**Repository Path**: ibotkid/openai-chat
## Basic Information
- **Project Name**: ChatGPT在线聊天系统付费版
- **Description**: 涉及技术栈
- 后端
- Java17、Golang、Redis、Redisson、RabbitMQ、CloudStreamRabbitMQ、MySQL、MybatisFlex、SpringBoot3、Druid、SaToken、Docker等
- 前端
- Typescript、Vue3、VueRouter、Pinia、NaiveUI、Vite、Docker等
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 9
- **Created**: 2023-07-05
- **Last Updated**: 2023-07-18
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Openai聊天系统开发文档(v1.0)
## 风险申明
- 本项目仅供学习和研究使用,不鼓励用于商业用途。对于因使用本项目而导致的任何损失,我们不承担任何责任
## 开发作者
- **B站Up: `枫度柚子`**,希望大家能关注下卑微的up,支持下
- **视频**: https://www.bilibili.com/video/BV1wo4y147aQ/?spm_id_from=333.788&vd_source=a58fd651e622550e04d6eedf025b47ca
- **由于好多个视频审核不通过,大家就看代码和文档讲解吧**
- **体验地址**:https://chat.linc-tian.uk
- **qq群**: 995832569
## 项目简介
### 涉及技术栈
- **`后端`**
- Java17、Golang、Redis、Redisson、RabbitMQ、CloudStreamRabbitMQ、MySQL、MybatisFlex、SpringBoot3、Druid、SaToken、Docker等
- **`前端`**
- Typescript、Vue3、VueRouter、Pinia、NaiveUI、Vite、Docker等
### 主要功能
- C端
- 用户
- 注册
- 邮箱注册
- 用户可以通过邮箱注册来绑定Chat的用户信息,当你进行注册的时候,会给你需要绑定的邮箱发送邮件,用户需要点击邮件来确定是否是真实有效的邮箱,当用户绑定成功之后,会跳转成功响应的页面,以及失败后也会对应一个失败的响应页面
- 手机号注册(`待定`)
- 登录
- 邮箱登录
- 用户通过`验证成功`的邮箱进行登录,登录之后,会查询当前登录用户的基本信息显示在左侧栏里,比如ID、聊天剩余使用次数等
- 手机号登录(`待定`)
- 聊天
- 通用调用Openai的接口,以`application/octet-stream`形式 传输信息到前端,前端通过字符串截取的形式,将JSON字符串以某个特定字符片段截取,然后依次显示在页面上,给用户一个打字效果,并且呢,对于Vue3前端的部署需要注意的是,要关闭缓存以及配置流式输出的形式
- 商品
- 在`立即充值`按钮点击下会显示Top4的商品,对应的会展示商品的名称、库存、单价、奖励聊天次数,当点击`+`时,页面的库存会减少对应下,左下部总金额会进行计算,要注意的是,这里只是负责展示,并不会传递给后端,后端会通过订单项去计算总金额,另外对于PC端的话可以通过`确认下单`去进行下单,当点击确认下单后,会弹出支付宝二维码,这里前段会进行轮询查看订单状态,如果用户支付了,就会自动关闭,如果用户直接关闭了二维码弹框,可以到`我的订单`去查看当前的订单,然后`去支付`或者`关闭`
- 另外,对于之前未处理过的订单,商品是不允许继续下单的,必须先处理之前的订单,才能继续下单
- 订单
- 我的订单,主要是展示当前登录用户的订单信息,也可以进行订单状态的更改以及去支付订单
- 下单,主要是通过选择的商品进行下单,对于订单后端也有超时未支付的处理,会进行自动关闭订单,归还预库存
- 兑换码
- 按照一定规则生成兑换码,当商品创建的时候,就会根据商品的库存生成对应的兑换码进行绑定,方便后续进行分配
- 支付
- 对于支付,分为Java后端和Go中台,Java后端通过调用Go中台的支付接口,进行相关信息的处理,以及对应的回调会在Go中台进行处理,对于传输的信息都是进行Rsa加密以及Base64加码的
- B端(`待定`)
## 细节简讲解
### Vue3前端整体实现效果展示
#### 注册

#### 登录

#### 主界面

#### 立即充值


#### 二维码支付

#### 我的订单

#### 我的兑换码


#### 兑换码兑换

### Java支付宝当面付对接
- 对于当面付,我这边主要是进行了`支付宝的当面付对接`
#### 开通支付宝当面付
- https://b.alipay.com/signing/productDetailV2.htm?productId=I1011000290000001003
- 填写好相关资料
- `经营类目`选择 “百货零售 / 其他零售 / 杂货店”,或者其他...问题不大
- `营业执照`可不上传
- `店铺招牌 `可以拍一下身份的百货店,或者百度找一张类似的图
- `等待审核`(工作日的话大概30分钟,非工作日就不好说了)
- 创建应用并且添加支付能力
- 可以参考文档https://opendocs.alipay.com/open/200/105310
- 创建应用(点击链接进入)
- https://b.alipay.com/signing/productDetailV2.htm?productId=I1011000290000001003
- 填写相关应用资料
- `应用名称`
- `应用图标`
- 应用添加能力
- 添加`当面付`
- 应用开发设置
- 设置`接口加签方式`,手机收到验证码填写
- 下载`支付宝开放平台开发助手即密钥生成工具`:https://opendocs.alipay.com/common/02kipk
- 上传刚才生成的`应用公钥`
- `设置应用网关`
- 应用网关要跟你后台配置的回调地址保持一致,并且是公网能访问的
- 应用`提交审核`
#### Java后端如何实现
##### 引入maven依赖
```xml
com.alipay.sdk
alipay-sdk-java
4.10.170.ALL
```
##### 当面付逻辑预下单参考
```java
/** 创建的应用ID */
@Value("${fastboot.pay.alipay.app-id}")
private String appId;
/** 自己生成的应用私钥 */
@Value("${fastboot.pay.alipay.private-key}")
private String privateKey;
/** 支付宝提供的支付宝公钥 */
@Value("${fastboot.pay.alipay.alipay-public-key}")
private String alipayPublicKey;
/** 回调地址,需要公网且链接不需要重定向的,如:https://fastboot.shaines.cn/api/myalipay/trade-notify */
@Value("${fastboot.pay.alipay.notify-url}")
private String notifyUrl;
@RequestMapping("/trade-precreate")
@ApiOperation("当面付预下订单")
@ResponseBody
public Result tradePrecreate(AlipayTradePrecreateRequestDTO requestDTO) {
AssertUtils.notEmpty(requestDTO.getSubject(), "标题不可以为空");
AssertUtils.notEmpty(requestDTO.getTotalAmount(), "金额不可以为空");
AssertUtils.check(requestDTO.getSubject().length() > 50, "标题不可以超过50字");
AssertUtils.check(requestDTO.getTotalAmount().compareTo(new BigDecimal("0")) <= 0, "金额需要大于0元");
AssertUtils.check(requestDTO.getTotalAmount().compareTo(new BigDecimal(50)) >= 0, "金额不可以超过50元");
// 金额保留两位小数
requestDTO.setTotalAmount(requestDTO.getTotalAmount().setScale(2, BigDecimal.ROUND_HALF_UP));
// 创建 AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do",
appId,
privateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2");
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
// String outTradeNo = UUID.randomUUID().toString().replace("-", "");
String orderNo = IDUtil.generate().toString();
request.setBizContent(String.format("{\n"
+ " \"out_trade_no\": \"%s\", \n"
+ " \"total_amount\": \"%s\", \n"
+ " \"subject\": \"%s\", \n"
+ " \"store_id\": \"JM01\", \n"
+ " \"qr_code_timeout_express\": \"30m\"\n"
+ "}",
orderNo,
requestDTO.getTotalAmount(),
requestDTO.getSubject()));
request.setNotifyUrl(notifyUrl);
AlipayTradePrecreateResponse response;
try {
response = alipayClient.execute(request);
} catch (AlipayApiException e) {
throw new BusinessException(String.format("下单失败 错误代码:[%s], 错误信息:[%s]", e.getErrCode(), e.getErrMsg()));
}
log.info("AlipayTradePrecreateResponse = {}", response.getBody());
/*
{
"code": "10000",
"msg": "Success",
"out_trade_no": "815259610498863104",
"qr_code": "https://qr.alipay.com/bax09455sq1umiufbxf4503e"
}
*/
if (!response.isSuccess()) {
throw new BusinessException(String.format("下单失败 错误代码:[%s], 错误信息:[%s]", response.getCode(), response.getMsg()));
}
// TODO 下单记录保存入库
//
// 返回结果,主要是返回 qr_code,前端根据 qr_code 进行重定向或者生成二维码引导用户支付
AlipayTradePrecreateResponseDTO responseDTO = new AlipayTradePrecreateResponseDTO();
responseDTO.setOrderNo(orderNo);
responseDTO.setQrCode(response.getQrCode());
return new Result<>(responseDTO);
}
```
##### 支付宝官方支付回调(扫码支付)
```java
@RequestMapping("/trade-notify")
@ResponseBody
public String tradeNotify(HttpServletRequest request) {
Map parameterMap = request.getParameterMap();
Map params = new HashMap<>(32);
parameterMap.forEach((key, val) -> params.put(key, String.join(",", val)));
/*
{
"gmt_create": "2021-02-24 09:12:57",
"charset": "UTF-8",
"seller_email": "272694308@qq.com",
"subject": "测试",
"sign": "XyrF+MU+zPIAVKdBGcrpgwpYYaG3uAqk7Y34LDQJ+BHXyBTfRvkq8wEm6MOz2np4GoJ1pkoV1ZzZGKdhoTHrxFAxk2hFvTXs9UoBpyrzV/kuLmC436IHWOe2koTWqTZCWhm+6w3NJWCNwq+nmUjzvLvmQWgSruBBJ/ZLR3cWGwMQleoj1clkc7smVdBKiFpY1oxc8nCOSIgKsyxZZ3Iza0rJKYResR7BRNFhmSI7sOYQyQEr0c8LPTPnyhAWVypQcAhHEkpZIyMbRjG9ayOdhtmI3VLh7Kh5AD9c1/VVT1grHrcucXhkhwBvJndQPDmXxcl/nSwhuSlMttAd9HPL2A==",
"buyer_id": "2088112022569596",
"invoice_amount": "0.01",
"notify_id": "2021022400222091303069591445812598",
"fund_bill_list": "[{\"amount\":\"0.01\",\"fundChannel\":\"ALIPAYACCOUNT\"}]",
"notify_type": "trade_status_sync",
"trade_status": "TRADE_SUCCESS",
"receipt_amount": "0.01",
"app_id": "2021002107650269",
"buyer_pay_amount": "0.01",
"sign_type": "RSA2",
"seller_id": "2088122717972675",
"gmt_payment": "2021-02-24 09:13:03",
"notify_time": "2021-02-24 09:13:03",
"version": "1.0",
"out_trade_no": "def55f2d4076451c948eba010dfc1255",
"total_amount": "0.01",
"trade_no": "2021022422001469591437625740",
"auth_app_id": "2021002107650269",
"buyer_logon_id": "143***@qq.com",
"point_amount": "0.00"
}
*/
log.info("============>>> /trade-notifyparams:{}", JSON.toJSONString(params));
//
boolean check;
try {
// 必须要验签
check = AlipaySignature.rsaCheckV1(params, alipayPublicKey, "UTF-8", "RSA2");
log.info("============>>> check: [{}]", check);
} catch (Exception e) {
log.warn("tradeNotify exception", e);
return "failure";
}
if (!check) {
return "rsaCheckV1 = false";
}
// TODO 修改下单的订单信息为已支付
//
return "success";
}
```
#### Go后端如何实现
##### 引入mod
```shell
go get github.com/smartwalle/alipay/v3
```
##### 核心配置
```go
client, err := alipay.New(alipayConfig.Appid, alipayConfig.PrivateKey, true)
if err != nil {
panic(err)
}
err = client.LoadAliPayPublicKey(alipayConfig.PublicKey)
if err != nil {
panic(err)
}
```
##### 具体细节可以参考源码
### Java支付迁移至Go支付当面付中台
##### 迁移Go中台应该注意的点
- 对于`Go中台`支付接口主要流程
- `Base64 ==> 解码 ==> RSA解密 ==> 反序列化 ==> 验签 ==> 进行支付宝调用`
- 对应的接口`POST http://127.0.0.1:36259/api/alipay/face2face/pay`
- `注意:` 这个接口需要能被外网访问到,可以使用内网穿透
- 对于`Java前台`的支付接口主要流程
- `请求参数封装=>>验签处理==>转JSON==>RSA加密=>Base64加码==>调用Go支付中台`
- 对应的接口`POST /api/chat_pay/alipay/face2face_pay`
### 商品订单表设计
#### open_ai_chat数据库文档
| 表名 | 说明 |
| :----------------------------------------------------------: | :--------------------: |
| [chat_exchange](#chat_exchange) | Chat聊天兑换表 |
| [chat_message](#chat_message) | Chat聊天记录表 |
| [chat_order](#chat_order) | Chat订单信息表 |
| [chat_order_delay_failure_record](#chat_order_delay_failure_record) | Chat超时订单失败记录表 |
| [chat_order_history](#chat_order_history) | Chat订单历史记录表 |
| [chat_order_item](#chat_order_item) | Chat订单明细表 |
| [chat_product](#chat_product) | Chat商品表 |
| [chat_room](#chat_room) | chat聊天室表 |
| [chat_sensitive_word](#chat_sensitive_word) | Chat敏感词表 |
| [chat_user](#chat_user) | Chat用户表 |
| [chat_user_binding](#chat_user_binding) | Chat用户绑定关系表 |
| [chat_user_binding_email](#chat_user_binding_email) | Chat用户邮箱绑定表 |
| [chat_user_exchange_record](#chat_user_exchange_record) | Chat用户聊天兑换记录表 |
| [chat_user_login_log](#chat_user_login_log) | Chat用户登录日志表 |
| [sys_email_send_log](#sys_email_send_log) | Sys系统邮箱发送日志表 |
| [sys_email_verify_code](#sys_email_verify_code) | Sys系统邮箱验证码表 |
##### **表名:** chat_exchange
**说明:** Chat聊天兑换表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :--------------: | :------: | :--: | :----: | :------: | :--: | :----: | :-----------------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | product_id | bigint | 20 | 0 | N | N | | 商品id#chat_product |
| 3 | name | varchar | 30 | 0 | N | N | | 名称 |
| 4 | code | varchar | 100 | 0 | N | N | | 通兑码 |
| 5 | reward_use_times | int | 10 | 0 | N | N | 0 | 奖励次数 |
| 6 | price | decimal | 11 | 2 | N | N | 0.00 | 面值 |
| 7 | is_used | bit | 1 | 0 | N | N | 0 | 是否使用,false否,true是 |
| 8 | is_occupied | bit | 1 | 0 | N | N | 0 | 是否被占用:0否1是 |
| 9 | status | bit | 1 | 0 | N | N | 0 | 状态1:启用2:停用 |
| 10 | is_deleted | int | 10 | 0 | N | N | 0 | 是否删除0否1是 |
| 11 | activation_time | datetime | 19 | 0 | Y | N | | 激活时间 |
| 12 | expires_days | int | 10 | 0 | Y | N | 0 | 多少天后过期 |
| 13 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 14 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
##### **表名:** chat_message
**说明:** Chat聊天记录表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :------------------------: | :------: | :---: | :----: | :------: | :--: | :----: | :----------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | user_id | bigint | 20 | 0 | N | N | | 用户id#chat_user |
| 3 | message_id | varchar | 255 | 0 | N | N | | 消息id |
| 4 | parent_message_id | varchar | 255 | 0 | Y | N | | 父级消息id |
| 5 | parent_answer_message_id | varchar | 255 | 0 | Y | N | | 父级回答消息id |
| 6 | parent_question_message_id | varchar | 255 | 0 | Y | N | | 父级问题消息id |
| 7 | context_count | bigint | 20 | 0 | N | N | | 上下文数量 |
| 8 | question_context_count | bigint | 20 | 0 | N | N | | 问题上下文数量 |
| 9 | message_type | int | 10 | 0 | N | N | | 消息类型枚举 |
| 10 | chat_room_id | bigint | 20 | 0 | N | N | | 聊天室id#chat_room |
| 11 | conversation_id | varchar | 255 | 0 | Y | N | | 对话id |
| 12 | api_type | varchar | 20 | 0 | N | N | | API类型 |
| 13 | model_name | varchar | 50 | 0 | Y | N | | 模型名称 |
| 14 | api_key | varchar | 255 | 0 | Y | N | | ApiKey |
| 15 | content | varchar | 5000 | 0 | N | N | | 消息内容 |
| 16 | original_data | text | 65535 | 0 | Y | N | | 消息的原始数据 |
| 17 | response_error_data | text | 65535 | 0 | Y | N | | 错误响应数据 |
| 18 | prompt_tokens | bigint | 20 | 0 | Y | N | | 输入消息的tokens |
| 19 | completion_tokens | bigint | 20 | 0 | Y | N | | 输出消息的tokens |
| 20 | total_tokens | bigint | 20 | 0 | Y | N | | 累计Tokens |
| 21 | ip | varchar | 255 | 0 | Y | N | | ip |
| 22 | status | int | 10 | 0 | N | N | | 聊天信息状态 |
| 23 | is_hide | bit | 1 | 0 | N | N | 0 | 是否被隐藏 |
| 24 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 25 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
##### **表名:** chat_order
**说明:** Chat订单信息表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :----------: | :------: | :--: | :----: | :------: | :--: | :----: | :------------------------------------------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | user_id | bigint | 20 | 0 | N | N | | 用户id#chat_user |
| 3 | total_amount | decimal | 11 | 2 | N | N | | 订单总金额 |
| 4 | pay_amount | decimal | 11 | 2 | N | N | | 应付金额 |
| 5 | status | tinyint | 4 | 0 | N | N | 0 | 订单状态(0:待付款1:待确认2:已完成3:无效订单4已关闭 |
| 6 | type | tinyint | 4 | 0 | N | N | 0 | 订单类型(0:正常订单1:秒杀订单) |
| 7 | remark | varchar | 500 | 0 | Y | N | | 备注 |
| 8 | pay_time | datetime | 19 | 0 | Y | N | | 支付时间 |
| 9 | is_deleted | int | 10 | 0 | N | N | 0 | 是否删除0否1是 |
| 10 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 11 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
##### **表名:** chat_order_delay_failure_record
**说明:** Chat超时订单失败记录表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :-------------: | :------: | :--------: | :----: | :------: | :--: | :----: | :----------: |
| 1 | id | bigint | 20 | 0 | N | Y | | 主键 |
| 2 | error_message | text | 65535 | 0 | N | N | | 错误信息 |
| 3 | message_payload | longtext | 2147483647 | 0 | N | N | | 消息内容json |
| 4 | retry_count | int | 10 | 0 | N | N | 0 | 重试次数 |
| 5 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
##### **表名:** chat_order_history
**说明:** Chat订单历史记录表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :----------: | :------: | :--: | :----: | :------: | :--: | :----: | :----------------------------------------------------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | user_id | bigint | 20 | 0 | N | N | | 用户id#chat_user |
| 3 | order_id | bigint | 20 | 0 | N | N | | 订单id#chat_order |
| 4 | order_status | tinyint | 4 | 0 | N | N | 0 | 订单状态0:待付款1:待确认2:已完成3:无效订单4:已关闭#chat_order |
| 5 | pay_status | tinyint | 4 | 0 | N | N | 0 | 支付状态0:支付中(0:待付款1:待确认)1:支付成功(2:已完成)2:支付失败(3:无效订单)3:支付关闭 |
| 6 | total_amount | decimal | 11 | 2 | N | N | 0.00 | 订单总金额 |
| 7 | pay_amount | decimal | 11 | 2 | N | N | | 应付金额 |
| 8 | pay_account | varchar | 30 | 0 | Y | N | | 支付账户 |
| 9 | pay_qr_code | varchar | 255 | 0 | Y | N | | 支付二维码链接 |
| 10 | is_deleted | int | 10 | 0 | N | N | 0 | 是否删除0否1是 |
| 11 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 12 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
| 13 | remark | varchar | 500 | 0 | Y | N | | 备注 |
##### **表名:** chat_order_item
**说明:** Chat订单明细表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :-----------: | :------: | :--: | :----: | :------: | :--: | :----: | :-------------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | order_id | bigint | 20 | 0 | N | N | | 订单id#chat_order |
| 3 | product_id | bigint | 20 | 0 | N | N | | 商品id#chat_product |
| 4 | exchange_num | int | 10 | 0 | N | N | | 兑换码数量 |
| 5 | product_name | varchar | 200 | 0 | N | N | | 商品名称#chat_product |
| 6 | product_price | decimal | 11 | 2 | N | N | | 商品价格#chat_product |
| 7 | is_deleted | int | 10 | 0 | N | N | 0 | 是否删除0否1是 |
| 8 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 9 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
##### **表名:** chat_product
**说明:** Chat商品表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :--------------: | :------: | :--: | :----: | :------: | :--: | :----: | :--------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | name | varchar | 30 | 0 | N | N | | 名称 |
| 3 | reward_use_times | int | 10 | 0 | N | N | 0 | 奖励次数 |
| 4 | price | decimal | 11 | 2 | N | N | 0.00 | 价格 |
| 5 | description | varchar | 64 | 0 | N | N | | 描述 |
| 6 | sale | int | 10 | 0 | N | N | 0 | 销量 |
| 7 | stock | int | 10 | 0 | N | N | 0 | 库存 |
| 8 | status | bit | 1 | 0 | N | N | 0 | 状态1:启用2:停用 |
| 9 | is_deleted | int | 10 | 0 | N | N | 0 | 是否删除0否1是 |
| 10 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 11 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
##### **表名:** chat_room
**说明:** chat聊天室表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :-------------------: | :------: | :--: | :----: | :------: | :--: | :----: | :--------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | user_id | bigint | 20 | 0 | N | N | | 用户id#chat_user |
| 3 | ip | varchar | 255 | 0 | Y | N | | ip |
| 4 | conversation_id | varchar | 255 | 0 | Y | N | | 对话id |
| 5 | first_chat_message_id | bigint | 20 | 0 | N | N | | 第一条消息主键 |
| 6 | first_message_id | varchar | 255 | 0 | N | N | | 第一条消息id |
| 7 | title | varchar | 5000 | 0 | N | N | | 对话标题 |
| 8 | api_type | varchar | 20 | 0 | N | N | | API类型 |
| 9 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 10 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
##### **表名:** chat_sensitive_word
**说明:** Chat敏感词表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :---------: | :------: | :--: | :----: | :------: | :--: | :----: | :------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | word | varchar | 255 | 0 | N | N | | 敏感词 |
| 3 | status | int | 10 | 0 | N | N | | 状态1启用2停用 |
| 4 | is_deleted | int | 10 | 0 | Y | N | 0 | 是否删除0否1是 |
| 5 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 6 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
##### **表名:** chat_user
**说明:** Chat用户表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :--------------------: | :------: | :--: | :----: | :------: | :--: | :----: | :----------: |
| 1 | id | bigint | 20 | 0 | N | Y | | 用户ID |
| 2 | nickname | varchar | 16 | 0 | N | N | | 用户昵称 |
| 3 | description | varchar | 64 | 0 | Y | N | | 描述 |
| 4 | avatar_version | int | 10 | 0 | N | N | | 头像版本 |
| 5 | last_login_ip | varchar | 64 | 0 | Y | N | | 上一次登录IP |
| 6 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 7 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
| 8 | can_conversation_times | int | 10 | 0 | Y | N | 0 | 可聊天次数 |
##### **表名:** chat_user_binding
**说明:** Chat用户绑定关系表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :-------------------: | :------: | :--: | :----: | :------: | :--: | :----: | :----------------------------------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | binding_type | varchar | 16 | 0 | N | N | | 绑定类型 |
| 3 | user_binding_email_id | bigint | 20 | 0 | N | N | | 额外信息表的用户ID#chat_user_binding_email |
| 4 | user_id | bigint | 20 | 0 | N | N | | 基础用户表的ID#chat_user |
| 5 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 6 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
##### **表名:** chat_user_binding_email
**说明:** Chat用户邮箱绑定表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :---------: | :------: | :--: | :----: | :------: | :--: | :----: | :-----------------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | username | varchar | 64 | 0 | N | N | | 用户名 |
| 3 | password | varchar | 128 | 0 | N | N | | 密码 |
| 4 | verified | tinyint | 4 | 0 | N | N | 0 | 是否验证过,false否true是 |
| 5 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 6 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
##### **表名:** chat_user_exchange_record
**说明:** Chat用户聊天兑换记录表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :---------: | :------: | :--: | :----: | :------: | :--: | :----: | :------------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | user_id | bigint | 20 | 0 | N | N | | 用户id#chat_user |
| 3 | exchange_id | bigint | 20 | 0 | N | N | | 兑换id#chat_exchange |
| 4 | code | varchar | 100 | 0 | N | N | | 通兑码#chat_exchange |
| 5 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
##### **表名:** chat_user_login_log
**说明:** Chat用户登录日志表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :-------------------: | :------: | :--: | :----: | :------: | :--: | :----: | :----------------------------------------------------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | user_id | bigint | 20 | 0 | N | N | | 登录的基础用户ID#chat_user |
| 3 | login_type | varchar | 32 | 0 | N | N | | 登录方式(注册方式),邮箱登录,手机登录等等 |
| 4 | user_binding_email_id | bigint | 20 | 0 | N | N | | 登录信息ID与login_type有关联,邮箱登录时关联#chat_user_binding_email |
| 5 | login_ip | varchar | 32 | 0 | N | N | | 登录的IP地址 |
| 6 | login_status | bit | 1 | 0 | N | N | | 登录状态,1登录成功,0登录失败 |
| 7 | message | varchar | 64 | 0 | N | N | | 登录后返回的消息 |
| 8 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
##### **表名:** sys_email_send_log
**说明:** Sys系统邮箱发送日志表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :----------------: | :------: | :---: | :----: | :------: | :--: | :----: | :----------------------------------------------------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | from_email_address | varchar | 64 | 0 | N | N | | 发件人邮箱 |
| 3 | to_email_address | varchar | 64 | 0 | N | N | | 收件人邮箱 |
| 4 | biz_type | int | 10 | 0 | N | N | | 业务类型(10:用户注册验证码认证11:用户找回密码验证码认证12:兑换码购买) |
| 5 | request_ip | varchar | 255 | 0 | N | N | | 请求ip |
| 6 | content | text | 65535 | 0 | N | N | | 发送内容 |
| 7 | message_id | varchar | 128 | 0 | N | N | | 消息id |
| 8 | status | tinyint | 4 | 0 | N | N | | 发送状态,1成功,0失败 |
| 9 | message | varchar | 1024 | 0 | N | N | | 发送后的消息,用于记录成功/失败的信息,成功默认为success |
| 10 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 11 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
##### **表名:** sys_email_verify_code
**说明:** Sys系统邮箱验证码表
**数据列:**
| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
| :--: | :--------------: | :------: | :--: | :----: | :------: | :--: | :----: | :----------------------------: |
| 1 | id | bigint | 20 | 0 | N | Y | | ID |
| 2 | to_email_address | varchar | 64 | 0 | N | N | | 验证码接收邮箱地址 |
| 3 | verify_code | varchar | 32 | 0 | N | N | | 验证码唯一 |
| 4 | is_used | bit | 1 | 0 | N | Y | | 是否使用,false否,true是 |
| 5 | verify_ip | varchar | 100 | 0 | N | N | | 核销IP,方便识别一些机器人账号 |
| 6 | expire_at | datetime | 19 | 0 | N | N | | 验证码过期时间 |
| 7 | biz_type | tinyint | 4 | 0 | N | N | | 当前邮箱业务 |
| 8 | create_time | datetime | 19 | 0 | N | N | | 创建时间 |
| 9 | update_time | datetime | 19 | 0 | N | N | | 更新时间 |
### Java商品下单以及超时订单详解
#### 超时订单预备工作
- 对于超时订单,这里使用RabbitMQ的死信队列+TTL来实现的
- 对于开启延时队列需要再RabbitMQ中安装插件,注意的是需要安装符合版本的插件
- https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/download/3.11.1/rabbitmq_delayed_message_exchange-3.11.1.ez
- 下载好之后,将这个插件放到对应的plugins目录
- 执行: `rabbitmq-plugins enable rabbitmq_delayed_message_exchange`
- 另外如果想把死信重新移动消费,可以安装一个插件
- 执行:`rabbitmq-plugins enable rabbitmq_shovel rabbitmq_shovel_management`
#### Java后台处理
##### 引入Maven依赖
```xml
org.springframework.cloud
spring-cloud-stream-binder-rabbit
4.0.2
```
##### 配置yaml
```yaml
spring:
cloud:
function:
definition: chat-order-source;chat-order-sink; # 函数名称,对应服务中的注入的Bean 定义消费者,多个用分号分隔,当存在大于1个的消费者时,不定义不会生效
stream:
function:
bindings:
chat-order-source-out-0: chat-order-dlq-output
chat-order-sink-in-0: chat-order-dlq-input
binders: # 需要绑定的rabbitmq的服务信息
default-binder: # 定义的名称,用于bidding整合 v
type: rabbit # 消息组件类型
environment: # 配置rabbitmq连接环境
spring:
rabbitmq:
addresses: ${RABBIT_ADDRESSES:127.0.0.1:5672} #服务器的地址和端口
username: ${RABBIT_USERNAME:root}
password: ${RABBIT_PASSWORD:123456}
virtual-host: /
connection-timeout: 16000
bindings:
# 死信队列
chat-order-dlq-output: # 自定义消息通道的名称
destination: chat-order-dlq-topic # exchange名称,交换模式默认是topic,创建时同时会创建队列
content-type: application/json
group: dlq-group # 消息组
binder: default-binder # 绑定的binder名称
chat-order-dlq-input:
destination: chat-order-dlq-topic # exchange名称,交换模式默认是topic,创建时同时会创建}队列
content-type: application/json
group: dlq-group # 消息组
binder: default-binder
consumer:
max-attempts: 3 # 次数等于1,相当于不重试
acknowledge-mode: manual # manual手动确认 ,auto 自动确认
rabbit:
bindings:
# 死信队列
chat-order-dlq-output:
producer:
auto-bind-dlq: true # 为true是开启死信队列
delayed-exchange: true # 开启延时
chat-order-dlq-input:
consumer:
auto-bind-dlq: true #是否自动声明DLQ并将其绑定到绑定器DLX
delayed-exchange: true #是否将交换声明为Delayed Message Exchange - 需要经纪人上的延迟消息交换插件。x-delayed-type参数设置为exchangeType
republish-to-dlq: true # 默认情况下,尝试重试后失败的消息将被拒绝。如果配置了死信队列(DLQ),则RabbitMQ将将失败的消息(未更改)路由到DLQ。如果设置为true,则绑定器将重新发布具有附加头的DLQ的失败消息,包括最终失败的原因的异常消息和堆栈跟踪
prefetch: 10 # 限制consumer在消费消息时,一次能同时获取的消息数量,默认:1。
max-concurrency: 20 # 最大并发度
# requeue-rejected: true # 消费失败后重写放回队列 可能会导致无线循环
acknowledge-mode: manual # manual手动确认 ,auto 自动确认
```
##### 对应Bean注册
```java
@Component
public record StreamProducer(StreamBridge streamBridge) {
/**
* 发送消息
*/
public boolean sendMessage(String msgText, String bindingName) {
// 构建消息对象
StreamMessage messaging = new StreamMessage();
messaging.setMessageId(UUID.randomUUID().toString());
messaging.setMessageText(msgText);
Message message = MessageBuilder.withPayload(messaging).build();
return streamBridge.send(bindingName, message);
}
/**
* 发送延迟消息
*/
public boolean sendDelayMessage(String msgText, String bindingName, Integer seconds) {
// 构建消息对象
StreamMessage messaging = new StreamMessage();
messaging.setMessageId(UUID.randomUUID().toString());
messaging.setMessageText(msgText);
Message message = MessageBuilder.withPayload(messaging)
.setHeader("x-delay", seconds * 1000)
.build();
return streamBridge.send(bindingName, message);
}
}
```
```java
public interface StreamConsumer {
Consumer> consume();
}
```
```java
@Configuration
@Slf4j
public class ChatOrderStreamConsumer implements StreamConsumer {
/**
* mq接收ackMessage消息/手动ack确认
*/
@Bean("chat-order-sink")
public Consumer> consume() {
// TODO: 逻辑处理
}
}
```
##### 需要注意的地方
- 具体如何使用cloud stream可以参考官网的例子,或者此项目里面的代码
- 注意:
- Cloud stream4 和之前的写法是有很大区别的,生产者代码可以一份,通过绑定交换机来进行传输信息,但是消费者可能有多个bean,这里最好指定好对应的bean名称与yaml配置里的保持一致
### Go中台支付订单回调详解
- 对于Go中台的回调,注意了这里的接口必须是`POST`请求方式,以及回调接口地址必须与支付宝的`应用网关`保持一致,以及必须公网可以访问的
- 回调主要是用来处理订单的状态,商品库存的真实扣减,发放兑换码等一些功能,目前Go中台部分有几处TODO的地方,最重要的地方就是需要对落库时异常的兜底,还是需要额外建立一张异常回调日志表,后续可以进行人工处理,进行发放
- 另外要学会使用一个工具: https://opensupport.alipay.com/support/tools/NewCloudparse?ant_source=antsupport,来查看自己的回调接口是否是真的正确以及支付宝响应的数据
### Chat商品下单整个流程操作详解
- **`校验当前用户是否存在未支付的订单,如果存在就提示先支付或者取消订单`**
- **`校验合法性,校验库存就从Redis缓存中进行,扣减库存也是从这里面进行预扣减`**
- **`计算总金额`**
- **`组装订单`**
- **`组装历史订单`**
- **`组装订单项`**
- **`下单数据落库`,这里主要是采用`编程式事务`**
- **`事务成功`之后进行,**
- **`Redis库存扣减`**
- **`发送延迟队列,超时时间进行处理`**
- **由于好多个视频审核不通过,大家就看代码和文档讲解吧**
- **体验地址**:https://chat.linc-tian.uk