赞
踩
ai接口申请:http://www.tuling123.com
- github.com/gin-gonic/gin
- github.com/spf13/viper
- github.com/tidwall/gjson
目录结构如下:
项目根目录是 aichat
aichat 的目录结构
├── conf # 配置文件统一存放目录
│ ├── config.yaml # 配置文件
├── config # 专门用来处理配置和配置文件的Go package
│ └── config.go
├── handler # 类似MVC架构中的C,用来读取输入,并将处理流程转发给实际的处理函数,最后返回结果
│ ├── handler.go
├── model #数据模型
│ ├── chater.go # 图灵参数构造体模型
├── pkg # 引用的包
│ ├── errno # 错误码存放位置
│ │ ├── code.go
│ │ └── errno.go
├── router # 路由相关处理
│ ├── middleware # API服务器用的是Gin Web框架,Gin中间件存放位置
│ │ ├── header.go
│ └── router.go # 路由
├── service # 实际业务处理函数存放位置
│ └── service.go
├── main.go # Go程序唯一入口
- common:
- #http://www.tuling123.com 图灵机器人接口
- tuling:
- apikey: 你自己申请的图灵apikey
- api: http://openapi.tuling123.com/openapi/api/v2
- server: #服务器配置
- runmode: debug # 开发模式, debug, release, test
- addr: :6663 # HTTP绑定端口
- name: apiserver # API Server的名字
- url: http://10.10.87.243:6663 # pingServer函数请求的API服务器的ip:port
- max_ping_count: 10 # pingServer函数尝试的次数
- package config
-
- import (
- "github.com/spf13/viper"
- "time"
- "os"
- "log"
- )
-
- // LogInfo 初始化日志配置
- func LogInfo() {
- file := "./logs/" + time.Now().Format("2006-01-02") + ".log"
- logFile, _ := os.OpenFile(file,os.O_RDWR| os.O_CREATE| os.O_APPEND, 0755)
- log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
- log.SetOutput(logFile)
- }
-
- // Init 读取初始化配置文件
- func Init() error {
- //初始化配置
- if err := Config();err != nil{
- return err
- }
-
- //初始化日志
- LogInfo()
- return nil
- }
-
- // Config viper解析配置文件
- func Config() error{
- viper.AddConfigPath("conf")
- viper.SetConfigName("config")
- if err := viper.ReadInConfig();err != nil{
- return err
- }
- return nil
- }
-
- package handler
-
- import (
- "bytes"
- "net/http"
- "io/ioutil"
-
- "github.com/gin-gonic/gin"
-
- "aichat/pkg/errno"
- )
-
- type Response struct {
- Code int `json:"code"`
- Message string `json:"message"`
- Data interface{} `json:"data"`
- }
-
- //返回json 格式
- func SendResponse(c *gin.Context,err error,data interface{}){
- code,message := errno.DecodeErr(err)
-
- //总是返回http状态ok
- c.JSON(http.StatusOK,Response{
- Code: code,
- Message:message,
- Data: data,
- })
-
- }
-
- //返回html 格式
- func SendResponseHtml(c *gin.Context,err error,data string){
- c.Header("Content-Type", "text/html; charset=utf-8")
- //总是返回http状态ok
- c.String(http.StatusOK,data)
- }
-
- //http请求
- func HttpRequest(api string,json string,method string) (string, error) {
- jsonStr := []byte(json)
- req, err := http.NewRequest(method, api, bytes.NewBuffer(jsonStr))
- req.Header.Set("Content-Type", "application/json") //使用json格式传参
-
- client := &http.Client{}
- resp, err := client.Do(req)
- if err != nil {
- return "", errno.ApiServerError
- }
- defer resp.Body.Close()
-
- body, _ := ioutil.ReadAll(resp.Body)
-
- if !(resp.StatusCode == 200) {
- return "", errno.ApiServerError
- }
- return string(body), nil
- }
- package model
-
- import (
- "encoding/json"
-
- "aichat/pkg/errno"
- )
-
- /*
- 传入参数 json
- {
- "reqType":0,
- "perception": {
- "inputText": {
- "text": "附近的酒店"
- },
- "inputImage": {
- "url": "imageUrl"
- },
- "selfInfo": {
- "location": {
- "city": "北京",
- "province": "北京",
- "street": "信息路"
- }
- }
- },
- "userInfo": {
- "apiKey": "",
- "userId": ""
- }
- }
- */
-
- type Chatting struct {
- ReqType int `json:"reqType"`
- Perception Perception `json:"perception"`
- UserInfo UserInfo `json:"userInfo"`
- }
-
- type InputText struct {
- Text string `json:"text"`
- }
-
- type Perception struct {
- InputText InputText `json:"inputText"`
- }
-
- type UserInfo struct {
- ApiKey string `json:"apiKey"`
- UserId string `json:"userId"`
- }
- //更新 图灵参数构造体 信息
- func UpdateChatting(userId string, text string, chattingInfo Chatting) Chatting {
- chattingInfo.UserInfo.UserId = userId
- chattingInfo.Perception.InputText.Text = text
- return chattingInfo
- }
- //建立 图灵参数构造体
- func BuildChatting(text string, userId string,appKey string) Chatting {
- chatting := Chatting{ReqType: 0}
- chatting.Perception = buildPerception(text)
- chatting.UserInfo = buildUserInfo(userId,appKey)
- return chatting
- }
- //建立 Perception
- func buildPerception(text string) Perception {
- perception := Perception{buildInputText(text)}
- return perception
- }
- //建立 InputText
- func buildInputText(text string) InputText {
- inputText := InputText{text}
- return inputText
- }
- //建立 UserInfo
- func buildUserInfo(userId string,appKey string) UserInfo {
- return UserInfo{appKey, userId}
- }
-
- //构造体转换成字符串
- func ConvertJson(chattingInfo Chatting) (string,error) {
- jsons, errs := json.Marshal(chattingInfo)
- if errs != nil {
- return "", errno.ModelError
- }
- return string(jsons),nil
- }
- package errno
-
- var (
- // Common errors
- OK = &Errno{Code: 0, Message: "OK"}
- VALUEERROR = &Errno{Code: -1, Message: "输入错误"}
-
- InternalServerError = &Errno{Code: 10001, Message: "服务器错误"}
- ApiServerError = &Errno{Code: 20001, Message: "接口服务器错误"}
- ModelError = &Errno{Code: 30001, Message: "聊天模型错误"}
- )
- package errno
-
- import "fmt"
-
- type Errno struct {
- Code int
- Message string
- }
-
- //返回错误信息
- func (err Errno) Error() string{
- return err.Message
- }
-
- //设置 Err 结构体
- type Err struct {
- Code int
- Message string
- Err error
- }
-
- //声明构造体
- func New(errno *Errno,err error) *Err{
- return &Err{Code:errno.Code,Message:errno.Message,Err:err}
- }
-
- //添加错误信息
- func (err *Err) Add(message string) error{
- err.Message += " " + message
- return err
- }
-
- //添加指定格式的错误信息
- func (err * Err) Addf(format string,args...interface{}) error{
- err.Message += " " + fmt.Sprintf(format,args...)
- return err
- }
-
- //拼接错误信息字符串
- func (err *Err) Error() string{
- return fmt.Sprintf("Err - code: %d, message: %s, error: %s",err.Code,err.Message,err.Err)
- }
-
- // 解析 错误信息, 返回字符串
- func DecodeErr(err error) (int,string){
- if err == nil{
- return OK.Code,OK.Message
- }
- switch typed := err.(type) {
- case *Err:
- return typed.Code,typed.Message
- case *Errno:
- return typed.Code,typed.Message
- default:
- }
- return InternalServerError.Code,err.Error()
- }
- package middleware
-
- import (
- "net/http"
- "time"
- "github.com/gin-gonic/gin"
- )
-
- //无缓存头部中间件 ,
- //要来防止客户端获取已经缓存的响应信息
- func NoCache(c *gin.Context){
- c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
- c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
- c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
- c.Next()
- }
-
- //选项中间件
- //要来给预请求 终止并退出中间件 ,链接并结束请求
- func Options(c *gin.Context){
- if c.Request.Method != "OPTIONS"{
- c.Next()
- }else{
- c.Header("Access-Control-Allow-Origin","*")
- c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
- c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
- c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
- c.Header("Content-Type", "application/json")
- c.AbortWithStatus(200)
- }
- }
-
- //安全中间件
- //要来保障数据安全的头部
- func Secure(c *gin.Context){
- c.Header("Access-Control-Allow-Origin", "*")
- c.Header("X-Frame-Options", "DENY")
- c.Header("X-Content-Type-Options", "nosniff")
- c.Header("X-XSS-Protection", "1; mode=block")
- if c.Request.TLS != nil {
- c.Header("Strict-Transport-Security", "max-age=31536000")
- }
-
- //也可以考虑添加一个安全代理的头部
- //c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
- }
- package router
-
- import (
- "net/http"
- "aichat/service"
- "aichat/router/middleware"
- "github.com/gin-gonic/gin"
- )
-
- //初始化路由
- func InitRouter(g *gin.Engine){
- middlewares := []gin.HandlerFunc{}
- //中间件
- g.Use(gin.Recovery())
- g.Use(middleware.NoCache)
- g.Use(middleware.Options)
- g.Use(middleware.Secure)
- g.Use(middlewares...)
-
- //404处理
- g.NoRoute(func(c *gin.Context){
- c.String(http.StatusNotFound,"该路径不存在")
- })
- //健康检查中间件
- g.GET("/",service.Index)//主页
- g.GET("/chat",service.AiChat)//
- }
- package service
-
- import (
- "github.com/tidwall/gjson"
- "github.com/gin-gonic/gin"
- "github.com/spf13/viper"
- "strconv"
- "log"
-
- "aichat/pkg/errno"
- . "aichat/handler"
- "aichat/model"
- )
-
- //首页
- func Index(c *gin.Context){
- html := `
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>hello world</title>
- </head>
- <body>
- hello world
- </body>
- </html>
- `
- SendResponseHtml(c,nil,html)
- }
-
- //获取tuling接口回复
- func TulingAi(info string) (string,error) {
- api := viper.GetString("common.tuling.api")
-
- //发送http请求图灵api , body是http响应
- var body, resultErrs = HttpRequest(api,info,"POST")
- if resultErrs != nil {
- return "", errno.ApiServerError
- }
-
- return body, nil
- }
-
- //回复信息构造体
- type tlReply struct {
- code int
- Text string `json:"text"`
- }
-
- //聊天函数
- func AiChat(c *gin.Context){
- //获取聊天信息
- message := c.Query("message")
- if message == ""{
- SendResponse(c,errno.VALUEERROR,nil)
- return
- }
- var userId = "1"
- //图灵接口参数构造体
- var chattingInfo = model.BuildChatting(message,userId, viper.GetString("common.tuling.apikey"))
- log.Printf("chattingInfo: %+v\n",chattingInfo)
- // 参数构造体 转换成 字符串
- chatstr,err := model.ConvertJson(chattingInfo)
- if err != nil{
- SendResponse(c,errno.InternalServerError,nil)
- return
- }
-
- //调用图灵接口
- body,err := TulingAi(chatstr)
- if err != nil{
- SendResponse(c,errno.InternalServerError,nil)
- return
- }
- log.Printf("body: %+v\n",body)
- var results string
- // 使用gjson 获取返回结果的 resultType
- result := gjson.Get(body, "results.#.resultType")
- for key, name := range result.Array() {
- //如果 resultType 是 text格式
- if name.String() == "text"{
- //获取对应 key 的 values里的text ,就是图灵回复的文字
- getstring := "results."+strconv.Itoa(key)+".values.text"
- log.Printf("getstring: %+v\n",getstring)
- result_text := gjson.Get(body,getstring)
- results = result_text.String()
- }
- }
-
- SendResponse(c,nil,results)
- }
- package main
-
- import (
- "aichat/config"
- "github.com/gin-gonic/gin"
- "github.com/spf13/viper"
-
- "log"
-
- "aichat/router"
- )
-
- func main() {
- if err := config.Init();err != nil{
- panic(err)
- }
- //设置gin模式
- gin.SetMode(viper.GetString("common.server.runmode"))
-
- //创建一个gin引擎
- g := gin.New()
-
- router.InitRouter(g)
- log.Printf("开始监听服务器地址: %s\n", viper.GetString("common.server.url"))
- if err := g.Run(viper.GetString("common.server.addr"));err != nil {
- log.Fatal("监听错误:", err)
- }
-
- }
- [root@localhost aichat]# go mod init aichat
- go: creating new go.mod: module aichat
- [root@localhost aichat]# go run main.go
- [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- - using env: export GIN_MODE=release
- - using code: gin.SetMode(gin.ReleaseMode)
-
- [GIN-debug] GET / --> aichat/service.Index (5 handlers)
- [GIN-debug] GET /chat --> aichat/service.AiChat (5 handlers)
- [GIN-debug] Listening and serving HTTP on :6663
参考:https://blog.csdn.net/weixin_40165163/article/details/89044491
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。