# 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前端整体实现效果展示 #### 注册 ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5412441ebd174599a5996a34a65b5892~tplv-k3u1fbpfcp-watermark.image?) #### 登录 ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2a9a2eac97ec4345a64e08612e26e8bf~tplv-k3u1fbpfcp-watermark.image?) #### 主界面 ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/10dfcc84637b4ac0a053edcdb46eab18~tplv-k3u1fbpfcp-watermark.image?) #### 立即充值 ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/69a0941c97494ba49d861d1f8d91a1d2~tplv-k3u1fbpfcp-watermark.image?) ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/00a2fd02a06646a3a7ab2e2f0b5dbf5c~tplv-k3u1fbpfcp-watermark.image?) #### 二维码支付 ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7ef60880c72243108c2ba731a25463ca~tplv-k3u1fbpfcp-watermark.image?) #### 我的订单 ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5b9eb861d8ca4cd89cc9681354668f64~tplv-k3u1fbpfcp-watermark.image?) #### 我的兑换码 ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/820e2502a95f4fd08ad059d6162dafee~tplv-k3u1fbpfcp-watermark.image?) ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/056f9f0f5bca47ca88e85edeceae1cfd~tplv-k3u1fbpfcp-watermark.image?) #### 兑换码兑换 ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5837bf2084b949a29e6c729f0d7dcd5b~tplv-k3u1fbpfcp-watermark.image?) ### 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