引言
在实际业务开发中,我们经常需要使用到各种工具和技术来提高效率和优化代码质量。本文将介绍一些与AI开发相关的工具和技术,来教你如何在业务开发实战中,玩转AI!
原则:
不要使用任何你看不懂的代码
工具准备:
Chatgpt
新世代万物之始——ChatGPT是一个新世代的语言模型,它可以产生逼真的对话,并提供人机交互的能力。详细信息可访问官网:Introducing ChatGPT (openai.com)。
Mermaid
支持Markdown的流程图js语言工具,文档地址: Mermaid | Diagramming and charting tool
Copilot
Copilot是一个集成在IDE中的代码提示和生成工具,它利用AI技术来辅助开发人员编写代码。GitHub Copilot · Your AI pair programmer
Bito
Bito是一个功能强大的AI工具,它内置了GPT3和GPT4模型,并提供代码生成、代码分析、注释生成和单元测试生成等功能Homepage - Bito
实战
在介绍以上的工具之后,我们话不多说,进入到实际的实战环节。
在实际开发中,我们需要对需求进行分析,并绘制流程图以展示整个流程。下面是一个需求分析的实例:
一,需求分析&流程图Mermaid
1,第一步,不用多说,肯定是按照我们的需求,进行需求拆分和分析。
举一个企业微信登录以及对应的后台需求为例,我们的需求流程可以拆分为如下
- 用户成功添加好友,接收企微添加企业客户事件回调通知,获得用户external_userid、员工userid
- 使用企微获取客户详情接口,带external_userid请求unionid
- 使用读取成员接口,获得员工所在的主部门(main_department),根据主部门名称的顶级游戏ID,更新顶级游戏字段
- 写进用户记录表(后台)
- 用户“企微登录成功”,将uid和unionid绑定,更新记录表
1、包含字段:37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、用户删除、员工删除、已转接)、添加时间、状态更新时间
2、37uid、员工userid、external_userid的组合只新增,不修改
3、接收企微删除跟进成员回调通知,状态变更为用户删除,不删除记录
4、接收企微删除企业客户回调通知,状态变更为员工删除,不删除记录
5、需支持查询项:37UID、添加时间、状态;
6、后台可显示总记录条数、筛选后的记录条数
2,在梳理完需求后,我们便可以根据上面的需求,提炼出GPT提示词:
- 用户成功添加好友,接收企微添加企业客户事件回调通知,获得用户external_userid、员工userid
- 使用企微获取客户详情接口,带external_userid请求unionid
- 使用读取成员接口,获得员工所在的主部门(main_department),根据主部门名称的顶级游戏ID,更新顶级游戏字段
- 写进用户记录表(后台)
- 用户“企微登录成功”,将uid和unionid绑定,更新记录表
1、包含字段:37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、用户删除、员工删除、已转接)、添加时间、状态更新时间
2、37uid、员工userid、external_userid的组合只新增,不修改
3、接收企微删除跟进成员回调通知,状态变更为用户删除,不删除记录
4、接收企微删除企业客户回调通知,状态变更为员工删除,不删除记录
5、需支持查询项:37UID、添加时间、状态;
6、后台可显示总记录条数、筛选后的记录条数
----
你是一个业务开发工程师,业务提了一个上述产品逻辑,请将它总结成逻辑性较强的语句
3,将上述词汇喂给GPT后,会得到更加明确而且清晰的流程
当用户成功添加好友时,系统将接收到企微添加企业客户事件回调通知,并获取用户的external_userid和员工的userid。
接着,使用企微的获取客户详情接口,带上external_userid请求unionid。再使用读取成员接口,获得员工所在的主部门(main_department),根据主部门名称的顶级游戏ID,更新顶级游戏字段。
接下来,将这些信息写入用户记录表(后台)。
如果用户“企微登录成功”,将uid和unionid绑定,并更新记录表。
记录表包含字段:37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、用户删除、员工删除、已转接)、添加时间、状态更新时间。
37uid、员工userid、external_userid的组合只新增,不修改。
当接收到企微删除跟进成员回调通知时,状态变更为用户删除,但不会删除记录。
当接收到企微删除企业客户回调通知时,状态变更为员工删除,但不会删除记录。
查询项包括:37UID、添加时间、状态。后台可以显示总记录条数和筛选后的记录条数。
4,由于此时GPT返回的词汇还有可能是有误的,所以这里需要再进一步,优化我们的语义/逻辑化我们的语义(类似于把整个流程都用文字清晰出来)
1、起始点为:“企业微信回调”
2、“企业微信回调”请求到 “平台企微服务”
3、“平台企微服务” 对“用户添加联系人”,“企微员工删除联系人”,“用户自行删除联系”三个事件做“解密xml,重构成json协议”处理,其他事件忽略掉
4、对上述三个处理后的事件进行分发到kafka
5、“平台企微事件消费者”消费kafka数据对“用户添加联系人”,“企微员工删除联系人”,“用户自行删除联系”三个事件进行业务处理
6、其中对于处理 “用户添加联系人” 事件有以下流程
- 使用企微获取客户详情接口,带external_userid请求unionid
- 使用读取成员接口,获得员工所在的主部门main_department,根据主部门名称的顶级游戏ID,解析顶级游戏字段
- 将“37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、已删除)、添加时间、状态更新时间”字段写进用户记录表
7、其中对于处理“企微员工删除联系人”,“用户自行删除联系”事件有以下流程
- 查询记录是否存在
- 更新用户记录表状态为“已删除”
最终为喂给GPT的提示词为如下
Output all the following requests as an whole Mermaid flowchart and 请将内容里的中文标点符号替换成英文标点符号,但不要把文字内容替换成英文
1、起始点为:“企业微信回调”
2、“企业微信回调”请求到 “平台企微服务”
3、“平台企微服务” 对“用户添加联系人”,“企微员工删除联系人”,“用户自行删除联系”三个事件做“解密xml,重构成json协议”处理,其他事件忽略掉
4、对上述三个处理后的事件进行分发到kafka
5、“平台企微事件消费者”消费kafka数据对“用户添加联系人”,“企微员工删除联系人”,“用户自行删除联系”三个事件进行业务处理
6、其中对于处理“用户添加联系人”事件有以下流程
- 使用企微获取客户详情接口,带external_userid请求unionid
- 使用读取成员接口,获得员工所在的主部门main_department,根据主部门名称的顶级游戏ID,解析顶级游戏字段
- 将“37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、已删除)、添加时间、状态更新时间”字段写进用户记录表
7、其中对于处理“企微员工删除联系人”,“用户自行删除联系”事件有以下流程
- 查询记录是否存在
- 更新用户记录表状态为“已删除”
根据上面这个,我们便可以用mermaid产生具体的流程图,以供我们开发的时候查看使用
graph TD
A[企业微信回调]
B[平台企微服务]
C{事件类型}
D[解密xml,重构成json协议]
E[忽略掉]
F[分发到kafka]
G[平台企微事件消费者]
H{事件类型}
I[使用企微获取客户详情接口,带external_userid请求unionid]
J[使用读取成员接口,获得员工所在的主部门main_department,根据主部门名称的顶级游戏ID,解析顶级游戏字段]
K["将37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、已删除)、添加时间、状态更新时间字段写进用户记录表"]
L[查询记录是否存在]
M["更新用户记录表状态为“已删除”"]
A-->B
B-->C
C-- 用户添加联系人 -->D
C-- 企微员工删除联系人 -->D
C-- 用户自行删除联系 -->D
C-- 其他事件 -->E
D-->F
F-->G
G-->H
H-- 用户添加联系人 -->I
I-->J
J-->K
H-- 企微员工删除联系人, 用户自行删除联系 -->L
L-->M
二、伪代码生成
此时,我们可以将上面的提示语喂给GPT,就能出的出一个具体的伪代码
请用golang代码实现这个流程
.....
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/confluentinc/confluent-kafka-go/kafka"
)
// 定义企业微信回调的结构体
type Callback struct {
XMLName xml.Name `xml:"xml"`
ToUserName string
FromUserName string
CreateTime string
MsgType string
Event string
ChangeType string
UserID string
ExternalUserID string
}
// 定义用户记录表的结构体
type User struct {
UID int // 37uid
UserID string // 员工userid
MainDepartment int // 员工主部门
CorpID string // 员工归属企微主体
UnionID string // unionid
ExternalUserID string // external_userid
Status string // 状态(正常、已删除)
AddTime time.Time // 添加时间
StatusUpdateTime time.Time // 状态更新时间
}
// 定义顶级游戏结构体
type Game struct {
ID int // 游戏ID
Name string // 游戏名称
}
func main() {
// 监听企业微信回调
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
// 读取请求体
body, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println("读取请求体失败:", err)
return
}
// 解析xml
var cb Callback
err = xml.Unmarshal(body, &cb)
if err != nil {
fmt.Println("解析xml失败:", err)
return
}
// 处理事件
handleEvent(cb)
}
})
// 启动监听
http.ListenAndServe(":8080", nil)
}
// 处理事件
func handleEvent(cb Callback) {
// 判断事件类型
switch cb.Event {
case "change_external_contact":
// 处理用户添加联系人事件
handleAddContact(cb.ExternalUserID)
case "del_external_contact":
// 处理用户自行删除联系人事件
handleDeleteContact(cb.ExternalUserID)
case "change_external_chat":
// 处理企微员工删除联系人事件
handleDeleteContact(cb.UserID)
default:
// 忽略其他事件
fmt.Println("忽略事件:", cb.Event)
}
}
// 处理用户添加联系人事件
func handleAddContact(externalUserID string) {
// 使用企微获取客户详情接口,带external_userid请求unionid
// TODO: 调用企微获取客户详情接口
unionID := "1234567890"
// 使用读取成员接口,获得员工所在的主部门main_department,根据主部门名称的顶级游戏ID,解析顶级游戏字段
mainDepartment := 1
gameID := getTopGame(mainDepartment)
// 将37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、已删除)、添加时间、状态更新时间字段写进用户记录表
addUser(externalUserID, mainDepartment, unionID, gameID)
}
// 处理用户自行删除联系人事件和企微员工删除联系人事件
func handleDeleteContact(externalUserID string) {
// 查询记录是否存在
user, err := getUserByExternalUserID(externalUserID)
if err != nil {
fmt.Println("查询用户记录失败:", err)
return
}
if user == nil {
fmt.Println("用户记录不存在")
return
}
// 更新用户记录表状态为“已删除”
user.Status = "已删除"
user.StatusUpdateTime = time.Now()
updateUser(user)
}
// 获取顶级游戏
func getTopGame(mainDepartment int) int {
// TODO: 调用读取部门详情接口,获取主部门名称
departmentName := "游戏部"
// TODO: 根据主部门名称的顶级游戏ID,解析顶级游戏字段
switch departmentName {
case "游戏部":
return 1
case "财务部":
return 2
default:
return 0
}
}
// 添加用户记录
func addUser(externalUserID string, mainDepartment int, unionID string, gameID int) {
// TODO: 生成37uid和员工userid
uid := 123456
userID := "123456"
// TODO: 获取员工归属企微
addTime := time.Now()
user := User{
UID: uid,
UserID: userID,
MainDepartment: mainDepartment,
CorpID: "123456",
UnionID: unionID,
ExternalUserID: externalUserID,
Status: "正常",
AddTime: addTime,
StatusUpdateTime: addTime,
}
// TODO: 将用户记录写入数据库中
// ...
// 发送消息到Kafka
sendMessage(user, gameID)
}
// 获取用户记录
func getUserByExternalUserID(externalUserID string) (*User, error) {
// TODO: 从数据库中查询用户记录
// ...
// 模拟返回数据
user := &User{
UID: 123456,
UserID: "123456",
MainDepartment: 1,
CorpID: "123456",
UnionID: "1234567890",
ExternalUserID: externalUserID,
Status: "正常",
AddTime: time.Now(),
StatusUpdateTime: time.Now(),
}
return user, nil
}
// 更新用户记录
func updateUser(user *User) {
// TODO: 更新数据库中的用户记录
// ...
// 发送消息到Kafka
sendMessage(*user, 0)
}
// 发送消息到Kafka
func sendMessage(user User, gameID int) {
// 构建消息
message := fmt.Sprintf("UID:%d,UserID:%s,MainDepartment:%d,CorpID:%s,UnionID:%s,ExternalUserID:%s,Status:%s,AddTime:%s,StatusUpdateTime:%s,GameID:%d",
user.UID, user.UserID, user.MainDepartment, user.CorpID, user.UnionID, user.ExternalUserID, user.Status, user.AddTime.Format("2006-01-02 15:04:05"), user.StatusUpdateTime.Format("2006-01-02 15:04:05"), gameID)
// 发送消息到Kafka
p, err := kafka.NewProducer(&kafka.ConfigMap{"bootstrap.servers": "localhost"})
if err != nil {
fmt.Printf("创建Kafka生产者失败:%s\n", err)
return
}
defer p.Close()
deliveryChan := make(chan kafka.Event)
err = p.Produce(&kafka.Message{
TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny},
Value: []byte(message),
}, deliveryChan)
if err != nil {
fmt.Printf("发送Kafka消息失败:%s\n", err)
return
}
e := <-deliveryChan
m := e.(*kafka.Message)
if m.TopicPartition.Error != nil {
fmt.Printf("发送Kafka消息失败:%s\n", m.TopicPartition.Error)
return
}
fmt.Printf("发送Kafka消息成功:%s\n", message)
}
三、Copliot快速开发
生成了伪代码之后,我们就可以进行实际的业务开发(往里面补充纠正逻辑),在实际的业务开发中,我们还可以用运用copliot去快速生成我们的代码。
这里可以牢记一个关键点—————方法名提现核心逻辑
四、Bito代码注释、分析和单元测试
在开发完代码之后,必不可少的一件事就是自测,如何提升自测的效率?这里就可以用到我们上面所提到的Bito了
安装教程这里省略,成功后可以先在IDE右侧点击
它可以生成对应注释
也可以根据提示词,生成对应的单元测试: 具体提示词:
func TestAddUserInfo(t *testing.T) {
addParam := service.PtWecomManagerAddUserInfoParam{
Tgid: 1,
WecomUserId: "Test123",
WecomExternalUserId: "wmX5*******0Q",
WecomUserDepartmentId: "13",
WecomUserDepartmentName: "测试组组1-1",
WecomCorpId: "ww*********e",
WecomCorpName: "*******公司",
Unionid: "**************",
WecomExternalUsername: "测试1234",
}
_, err := s.AddUserInfo(ctx, addParam)
if err != nil {
t.Error(err)
}
fmt.Println(err)
}
----
请为上述逻辑代码生成类似这种风格的单元测试
总结
现在是AI大时代,AI的发展正呈现出不可逆转,爆发式增长的趋势。在时代的浪潮中,我们只有紧跟时代的脚步,甚至站在浪尖才能不被时代落下。