From 8b2e2d61afcef88a9c4b488b6aa870e57139229f Mon Sep 17 00:00:00 2001 From: RockYang Date: Tue, 10 Sep 2024 15:24:36 +0800 Subject: [PATCH 01/25] file list api support pagination --- api/handler/upload_handler.go | 41 +++++++++++++++++++++++------------ web/.env.development | 2 +- web/.env.production | 2 +- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/api/handler/upload_handler.go b/api/handler/upload_handler.go index d9c75ec2..2020b873 100644 --- a/api/handler/upload_handler.go +++ b/api/handler/upload_handler.go @@ -64,7 +64,9 @@ func (h *NetHandler) Upload(c *gin.Context) { func (h *NetHandler) List(c *gin.Context) { var data struct { - Urls []string `json:"urls,omitempty"` + Urls []string `json:"urls,omitempty"` + Page int `json:"page"` + PageSize int `json:"page_size"` } if err := c.ShouldBindJSON(&data); err != nil { resp.ERROR(c, types.InvalidArgs) @@ -79,21 +81,32 @@ func (h *NetHandler) List(c *gin.Context) { if len(data.Urls) > 0 { session = session.Where("url IN ?", data.Urls) } - session.Find(&items) - if len(items) > 0 { - for _, v := range items { - var file vo.File - err := utils.CopyObject(v, &file) - if err != nil { - logger.Error(err) - continue - } - file.CreatedAt = v.CreatedAt.Unix() - files = append(files, file) - } + // 统计总数 + var total int64 + session.Model(&model.File{}).Count(&total) + + if data.Page > 0 && data.PageSize > 0 { + offset := (data.Page - 1) * data.PageSize + session = session.Offset(offset).Limit(data.PageSize) + } + err := session.Order("id desc").Find(&items).Error + if err != nil { + resp.ERROR(c, err.Error()) + return } - resp.SUCCESS(c, files) + for _, v := range items { + var file vo.File + err := utils.CopyObject(v, &file) + if err != nil { + logger.Error(err) + continue + } + file.CreatedAt = v.CreatedAt.Unix() + files = append(files, file) + } + + resp.SUCCESS(c, vo.NewPage(total, data.Page, data.PageSize, files)) } // Remove remove files diff --git a/web/.env.development b/web/.env.development index 1e54193a..97c67f7e 100644 --- a/web/.env.development +++ b/web/.env.development @@ -6,6 +6,6 @@ VUE_APP_ADMIN_USER=admin VUE_APP_ADMIN_PASS=admin123 VUE_APP_KEY_PREFIX=GeekAI_DEV_ VUE_APP_TITLE="Geek-AI 创作系统" -VUE_APP_VERSION=v4.1.3 +VUE_APP_VERSION=v4.1.4 VUE_APP_DOCS_URL=https://docs.geekai.me VUE_APP_GIT_URL=https://github.com/yangjian102621/geekai diff --git a/web/.env.production b/web/.env.production index 056d8338..fee207e7 100644 --- a/web/.env.production +++ b/web/.env.production @@ -1,6 +1,6 @@ VUE_APP_API_HOST= VUE_APP_WS_HOST= VUE_APP_KEY_PREFIX=GeekAI_ -VUE_APP_VERSION=v4.1.3 +VUE_APP_VERSION=v4.1.4 VUE_APP_DOCS_URL=https://docs.geekai.me VUE_APP_GIT_URL=https://github.com/yangjian102621/geekai From 97eff6085aa0db4e3b3b16992dcf16e71c4de915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=8F=8C=E6=98=8E?= Date: Tue, 10 Sep 2024 18:14:34 +0800 Subject: [PATCH 02/25] =?UTF-8?q?feat:=20210=20AI=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E6=96=87=E4=BB=B6=E5=88=97=E8=A1=A8=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=88=86=E9=A1=B5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/App.vue | 3 +- web/src/components/FileSelect.vue | 122 ++++++++++++++++++++---------- 2 files changed, 82 insertions(+), 43 deletions(-) diff --git a/web/src/App.vue b/web/src/App.vue index 059f4ffe..b94eed26 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -66,7 +66,8 @@ html, body { margin 0; .el-dialog__body { - max-height 90vh + max-height 80vh + overflow-y auto } } } diff --git a/web/src/components/FileSelect.vue b/web/src/components/FileSelect.vue index d340c460..de1f8739 100644 --- a/web/src/components/FileSelect.vue +++ b/web/src/components/FileSelect.vue @@ -1,57 +1,64 @@ + + \ No newline at end of file diff --git a/web/src/views/admin/Users.vue b/web/src/views/admin/Users.vue index 9c6930e2..1282ae3a 100644 --- a/web/src/views/admin/Users.vue +++ b/web/src/views/admin/Users.vue @@ -18,6 +18,8 @@ + + @@ -73,6 +75,12 @@ + + + + + + From 5c77e67b0fd091b220d948004bec53736211b1fd Mon Sep 17 00:00:00 2001 From: RockYang Date: Thu, 12 Sep 2024 17:25:19 +0800 Subject: [PATCH 05/25] fixed bug for reset password --- api/core/app_server.go | 3 +- api/handler/admin/api_key_handler.go | 4 -- api/handler/admin/chat_app_type_handler.go | 13 ++++--- api/handler/chat_app_type_handler.go | 43 ++++++++++++++++++++++ api/handler/chat_role_handler.go | 36 ++++++++++++++++-- api/handler/user_handler.go | 2 +- api/main.go | 3 +- api/service/smtp_sms_service.go | 2 +- api/store/model/app_type.go | 1 + api/store/vo/app_type.go | 14 +++---- database/update-v4.1.4.sql | 12 ++++++ web/src/views/ChatApps.vue | 5 +-- web/src/views/ChatPlus.vue | 2 +- web/src/views/mobile/ChatList.vue | 4 +- web/src/views/mobile/ChatSession.vue | 2 +- web/src/views/mobile/Index.vue | 2 +- 16 files changed, 115 insertions(+), 33 deletions(-) create mode 100644 api/handler/chat_app_type_handler.go create mode 100644 database/update-v4.1.4.sql diff --git a/api/core/app_server.go b/api/core/app_server.go index b7187d21..e911af72 100644 --- a/api/core/app_server.go +++ b/api/core/app_server.go @@ -204,7 +204,8 @@ func needLogin(c *gin.Context) bool { c.Request.URL.Path == "/api/chat/history" || c.Request.URL.Path == "/api/chat/detail" || c.Request.URL.Path == "/api/chat/list" || - c.Request.URL.Path == "/api/role/list" || + c.Request.URL.Path == "/api/app/list" || + c.Request.URL.Path == "/api/app/list/user" || c.Request.URL.Path == "/api/model/list" || c.Request.URL.Path == "/api/mj/imgWall" || c.Request.URL.Path == "/api/mj/client" || diff --git a/api/handler/admin/api_key_handler.go b/api/handler/admin/api_key_handler.go index ef1d93d1..b6454252 100644 --- a/api/handler/admin/api_key_handler.go +++ b/api/handler/admin/api_key_handler.go @@ -74,7 +74,6 @@ func (h *ApiKeyHandler) Save(c *gin.Context) { func (h *ApiKeyHandler) List(c *gin.Context) { status := h.GetBool(c, "status") t := h.GetTrim(c, "type") - platform := h.GetTrim(c, "platform") session := h.DB.Session(&gorm.Session{}) if status { @@ -83,9 +82,6 @@ func (h *ApiKeyHandler) List(c *gin.Context) { if t != "" { session = session.Where("type", t) } - if platform != "" { - session = session.Where("platform", platform) - } var items []model.ApiKey var keys = make([]vo.ApiKey, 0) diff --git a/api/handler/admin/chat_app_type_handler.go b/api/handler/admin/chat_app_type_handler.go index 2e121a13..d462f42c 100644 --- a/api/handler/admin/chat_app_type_handler.go +++ b/api/handler/admin/chat_app_type_handler.go @@ -25,6 +25,7 @@ func (h *ChatAppTypeHandler) Save(c *gin.Context) { var data struct { Id uint `json:"id"` Name string `json:"name"` + Enable bool `json:"enable"` Icon string `json:"icon"` SortNum int `json:"sort_num"` } @@ -36,12 +37,13 @@ func (h *ChatAppTypeHandler) Save(c *gin.Context) { if data.Id == 0 { // for add err := h.DB.Where("name", data.Name).First(&model.AppType{}).Error if err == nil { - resp.ERROR(c, "App类型已存在") + resp.ERROR(c, "当前分类已经存在") return } err = h.DB.Create(&model.AppType{ Name: data.Name, Icon: data.Icon, + Enabled: data.Enable, SortNum: data.SortNum, }).Error if err != nil { @@ -49,9 +51,10 @@ func (h *ChatAppTypeHandler) Save(c *gin.Context) { return } } else { // for update - err := h.DB.Where("id", data.Id).Updates(map[string]interface{}{ - "name": data.Name, - "icon": data.Icon, + err := h.DB.Model(&model.AppType{}).Where("id", data.Id).Updates(map[string]interface{}{ + "name": data.Name, + "icon": data.Icon, + "enabled": data.Enable, }).Error if err != nil { resp.ERROR(c, err.Error()) @@ -65,7 +68,7 @@ func (h *ChatAppTypeHandler) Save(c *gin.Context) { func (h *ChatAppTypeHandler) List(c *gin.Context) { var items []model.AppType var appTypes = make([]vo.AppType, 0) - err := h.DB.Order("created_at DESC").Find(&items).Error + err := h.DB.Order("sort_num ASC").Find(&items).Error if err != nil { resp.ERROR(c, err.Error()) return diff --git a/api/handler/chat_app_type_handler.go b/api/handler/chat_app_type_handler.go new file mode 100644 index 00000000..fc5e277e --- /dev/null +++ b/api/handler/chat_app_type_handler.go @@ -0,0 +1,43 @@ +package handler + +import ( + "geekai/core" + "geekai/store/model" + "geekai/store/vo" + "geekai/utils" + "geekai/utils/resp" + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +type ChatAppTypeHandler struct { + BaseHandler +} + +func NewChatAppTypeHandler(app *core.AppServer, db *gorm.DB) *ChatAppTypeHandler { + return &ChatAppTypeHandler{BaseHandler: BaseHandler{App: app, DB: db}} +} + +// List 获取App类型列表 +func (h *ChatAppTypeHandler) List(c *gin.Context) { + var items []model.AppType + var appTypes = make([]vo.AppType, 0) + err := h.DB.Order("sort_num ASC").Find(&items).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + for _, v := range items { + var appType vo.AppType + err = utils.CopyObject(v, &appType) + if err != nil { + continue + } + appType.Id = v.Id + appType.CreatedAt = v.CreatedAt.Unix() + appTypes = append(appTypes, appType) + } + + resp.SUCCESS(c, appTypes) +} diff --git a/api/handler/chat_role_handler.go b/api/handler/chat_role_handler.go index ce18ec8e..8ef899be 100644 --- a/api/handler/chat_role_handler.go +++ b/api/handler/chat_role_handler.go @@ -29,10 +29,37 @@ func NewChatRoleHandler(app *core.AppServer, db *gorm.DB) *ChatRoleHandler { // List 获取用户聊天应用列表 func (h *ChatRoleHandler) List(c *gin.Context) { + tid := h.GetInt(c, "tid", 0) + var roles []model.ChatRole + session := h.DB.Where("enable", true) + if tid > 0 { + session = session.Where("tid", tid) + } + err := session.Order("sort_num ASC").Find(&roles).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + var roleVos = make([]vo.ChatRole, 0) + for _, r := range roles { + var v vo.ChatRole + err := utils.CopyObject(r, &v) + if err == nil { + v.Id = r.Id + roleVos = append(roleVos, v) + } + } + resp.SUCCESS(c, roleVos) +} + +// ListByUser 获取用户添加的角色列表 +func (h *ChatRoleHandler) ListByUser(c *gin.Context) { id := h.GetInt(c, "id", 0) userId := h.GetLoginUserId(c) var roles []model.ChatRole - query := h.DB.Where("enable", true) + session := h.DB.Where("enable", true) + // 如果用户没登录,则获取所有角色 if userId > 0 { var user model.User h.DB.First(&user, userId) @@ -42,12 +69,13 @@ func (h *ChatRoleHandler) List(c *gin.Context) { resp.ERROR(c, "角色解析失败!") return } - query = query.Where("marker IN ?", roleKeys) + session = session.Where("marker IN ?", roleKeys) } + if id > 0 { - query = query.Or("id", id) + session = session.Or("id", id) } - res := h.DB.Where("enable", true).Order("sort_num ASC").Find(&roles) + res := session.Order("sort_num ASC").Find(&roles) if res.Error != nil { resp.ERROR(c, res.Error.Error()) return diff --git a/api/handler/user_handler.go b/api/handler/user_handler.go index 6b4ab0a9..de7a9657 100644 --- a/api/handler/user_handler.go +++ b/api/handler/user_handler.go @@ -601,7 +601,7 @@ func (h *UserHandler) ResetPass(c *gin.Context) { session = session.Where("email", data.Email) key = CodeStorePrefix + data.Email } else if data.Type == "mobile" { - session = session.Where("mobile", data.Email) + session = session.Where("mobile", data.Mobile) key = CodeStorePrefix + data.Mobile } else { resp.ERROR(c, "验证类别错误") diff --git a/api/main.go b/api/main.go index f240f893..dd725536 100644 --- a/api/main.go +++ b/api/main.go @@ -226,8 +226,9 @@ func main() { // 注册路由 fx.Invoke(func(s *core.AppServer, h *handler.ChatRoleHandler) { - group := s.Engine.Group("/api/role/") + group := s.Engine.Group("/api/app/") group.GET("list", h.List) + group.GET("list/user", h.ListByUser) group.POST("update", h.UpdateRole) }), fx.Invoke(func(s *core.AppServer, h *handler.UserHandler) { diff --git a/api/service/smtp_sms_service.go b/api/service/smtp_sms_service.go index 025ffa39..c6abf586 100644 --- a/api/service/smtp_sms_service.go +++ b/api/service/smtp_sms_service.go @@ -29,7 +29,7 @@ func NewSmtpService(appConfig *types.AppConfig) *SmtpService { func (s *SmtpService) SendVerifyCode(to string, code int) error { subject := fmt.Sprintf("%s 注册验证码", s.config.AppName) - body := fmt.Sprintf("您正在注册 %s 账户,注册验证码为 %d,请不要告诉他人。如非本人操作,请忽略此邮件。", s.config.AppName, code) + body := fmt.Sprintf("【%s】:您的验证码为 %d,请不要告诉他人。如非本人操作,请忽略此邮件。", s.config.AppName, code) auth := smtp.PlainAuth("", s.config.From, s.config.Password, s.config.Host) if s.config.UseTls { diff --git a/api/store/model/app_type.go b/api/store/model/app_type.go index de10cb16..ee1aceed 100644 --- a/api/store/model/app_type.go +++ b/api/store/model/app_type.go @@ -6,6 +6,7 @@ type AppType struct { Id uint `gorm:"primarykey"` Name string Icon string + Enabled bool SortNum int CreatedAt time.Time } diff --git a/api/store/vo/app_type.go b/api/store/vo/app_type.go index 714b398a..26dfe345 100644 --- a/api/store/vo/app_type.go +++ b/api/store/vo/app_type.go @@ -1,12 +1,10 @@ package vo type AppType struct { - BaseVo - Name string `json:"name"` - Type string `json:"type"` - Value string `json:"value"` // API Key 的值 - ApiURL string `json:"api_url"` - Enabled bool `json:"enabled"` - ProxyURL string `json:"proxy_url"` - LastUsedAt int64 `json:"last_used_at"` // 最后使用时间 + Id uint `json:"id"` + Name string `json:"name"` + Icon string `json:"icon"` + SortNum int `json:"sort_num"` + Enabled bool `json:"enabled"` + CreatedAt int64 `json:"created_at"` } diff --git a/database/update-v4.1.4.sql b/database/update-v4.1.4.sql new file mode 100644 index 00000000..86d47b63 --- /dev/null +++ b/database/update-v4.1.4.sql @@ -0,0 +1,12 @@ +CREATE TABLE `chatgpt_app_types` ( + `id` int NOT NULL, + `name` varchar(50) NOT NULL COMMENT '名称', + `icon` varchar(255) NOT NULL COMMENT '图标URL', + `sort_num` tinyint(3) NOT NULL COMMENT '排序', + `enabled` tinyint(1) NOT NULL COMMENT '是否启用', + `created_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='应用分类表'; + +ALTER TABLE `chatgpt_app_types`ADD PRIMARY KEY (`id`); +ALTER TABLE `chatgpt_app_types` MODIFY `id` int NOT NULL AUTO_INCREMENT; +ALTER TABLE `chatgpt_chat_roles` ADD `tid` INT NOT NULL COMMENT '分类ID' AFTER `name`; \ No newline at end of file diff --git a/web/src/views/ChatApps.vue b/web/src/views/ChatApps.vue index 48193f8e..45ab5677 100644 --- a/web/src/views/ChatApps.vue +++ b/web/src/views/ChatApps.vue @@ -64,7 +64,6 @@ import {arrayContains, removeArrayItem, substr} from "@/utils/libs"; import {useRouter} from "vue-router"; import {useSharedStore} from "@/store/sharedata"; import ItemList from "@/components/ItemList.vue"; -import {Plus} from "@element-plus/icons-vue"; const listBoxHeight = window.innerHeight - 87 const list = ref([]) @@ -72,7 +71,7 @@ const roles = ref([]) const store = useSharedStore(); onMounted(() => { - httpGet("/api/role/list").then((res) => { + httpGet("/api/app/list").then((res) => { const items = res.data // 处理 hello message for (let i = 0; i < items.length; i++) { @@ -112,7 +111,7 @@ const updateRole = (row, opt) => { } roles.value = removeArrayItem(roles.value, row.key) } - httpPost("/api/role/update", {keys: roles.value}).then(() => { + httpPost("/api/app/update", {keys: roles.value}).then(() => { ElMessage.success({message: title.value + "成功!", duration: 1000}) }).catch(e => { ElMessage.error(title.value + "失败:" + e.message) diff --git a/web/src/views/ChatPlus.vue b/web/src/views/ChatPlus.vue index 3552f5b9..48161be4 100644 --- a/web/src/views/ChatPlus.vue +++ b/web/src/views/ChatPlus.vue @@ -364,7 +364,7 @@ const initData = () => { modelID.value = models.value[0].id } // 加载角色列表 - httpGet(`/api/role/list`,{id:roleId.value}).then((res) => { + httpGet(`/api/app/list/user`,{id:roleId.value}).then((res) => { roles.value = res.data; if (!roleId.value) { roleId.value = roles.value[0]['id'] diff --git a/web/src/views/mobile/ChatList.vue b/web/src/views/mobile/ChatList.vue index a85af846..20864cf3 100644 --- a/web/src/views/mobile/ChatList.vue +++ b/web/src/views/mobile/ChatList.vue @@ -105,7 +105,7 @@ checkSession().then((user) => { loginUser.value = user isLogin.value = true // 加载角色列表 - httpGet(`/api/role/list`).then((res) => { + httpGet(`/api/app/list/user`).then((res) => { if (res.data) { const items = res.data for (let i = 0; i < items.length; i++) { @@ -139,7 +139,7 @@ checkSession().then((user) => { finished.value = true // 加载角色列表 - httpGet('/api/role/list').then((res) => { + httpGet('/api/app/list/user').then((res) => { if (res.data) { const items = res.data for (let i = 0; i < items.length; i++) { diff --git a/web/src/views/mobile/ChatSession.vue b/web/src/views/mobile/ChatSession.vue index f30db613..7a458bdd 100644 --- a/web/src/views/mobile/ChatSession.vue +++ b/web/src/views/mobile/ChatSession.vue @@ -186,7 +186,7 @@ httpGet('/api/model/list').then(res => { } modelValue.value = getModelName(modelId.value) // 加载角色列表 - httpGet(`/api/role/list`).then((res) => { + httpGet(`/api/app/list/user`).then((res) => { roles.value = res.data; if (!roleId.value) { roleId.value = roles.value[0]['id'] diff --git a/web/src/views/mobile/Index.vue b/web/src/views/mobile/Index.vue index 5ebd16a0..dd5430dd 100644 --- a/web/src/views/mobile/Index.vue +++ b/web/src/views/mobile/Index.vue @@ -109,7 +109,7 @@ onMounted(() => { }) const fetchApps = () => { - httpGet("/api/role/list").then((res) => { + httpGet("/api/app/list/user").then((res) => { const items = res.data // 处理 hello message for (let i = 0; i < items.length; i++) { From 6c7fa17e506297d4a95461089929ec730f317510 Mon Sep 17 00:00:00 2001 From: RockYang Date: Fri, 13 Sep 2024 17:03:05 +0800 Subject: [PATCH 06/25] fixed bug, filelist page support pagination, do not load captcha component for user login first time --- CHANGELOG.md | 1 + api/handler/admin/chat_app_type_handler.go | 6 +++--- api/handler/user_handler.go | 12 +++++++++--- web/.env.production | 2 +- web/src/components/ChatPrompt.vue | 2 +- web/src/components/FileList.vue | 1 + web/src/components/FileSelect.vue | 2 +- web/src/components/LoginDialog.vue | 10 +++++++--- web/src/views/ChatPlus.vue | 7 +++---- web/src/views/Login.vue | 7 +++++-- web/src/views/Suno.vue | 2 +- 11 files changed, 33 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08e9116e..857a200c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## v4.1.4 * 功能优化:用户文件列表组件增加分页功能支持 * Bug修复:修复用户注册失败Bug,注册操作只弹出一次行为验证码 +* 功能优化:首次登录不需要验证码,直接登录,登录失败之后才弹出验证码 ## v4.1.3 * 功能优化:重构用户登录模块,给所有的登录组件增加行为验证码功能,支持用户绑定手机,邮箱和微信 diff --git a/api/handler/admin/chat_app_type_handler.go b/api/handler/admin/chat_app_type_handler.go index d462f42c..d842a279 100644 --- a/api/handler/admin/chat_app_type_handler.go +++ b/api/handler/admin/chat_app_type_handler.go @@ -25,7 +25,7 @@ func (h *ChatAppTypeHandler) Save(c *gin.Context) { var data struct { Id uint `json:"id"` Name string `json:"name"` - Enable bool `json:"enable"` + Enabled bool `json:"enabled"` Icon string `json:"icon"` SortNum int `json:"sort_num"` } @@ -43,7 +43,7 @@ func (h *ChatAppTypeHandler) Save(c *gin.Context) { err = h.DB.Create(&model.AppType{ Name: data.Name, Icon: data.Icon, - Enabled: data.Enable, + Enabled: data.Enabled, SortNum: data.SortNum, }).Error if err != nil { @@ -54,7 +54,7 @@ func (h *ChatAppTypeHandler) Save(c *gin.Context) { err := h.DB.Model(&model.AppType{}).Where("id", data.Id).Updates(map[string]interface{}{ "name": data.Name, "icon": data.Icon, - "enabled": data.Enable, + "enabled": data.Enabled, }).Error if err != nil { resp.ERROR(c, err.Error()) diff --git a/api/handler/user_handler.go b/api/handler/user_handler.go index de7a9657..3b93310b 100644 --- a/api/handler/user_handler.go +++ b/api/handler/user_handler.go @@ -244,8 +244,10 @@ func (h *UserHandler) Login(c *gin.Context) { resp.ERROR(c, types.InvalidArgs) return } + verifyKey := fmt.Sprintf("users/verify/%s", data.Username) + needVerify, err := h.redis.Get(c, verifyKey).Bool() - if h.App.SysConfig.EnabledVerify { + if h.App.SysConfig.EnabledVerify && needVerify { var check bool if data.X != 0 { check = h.captcha.SlideCheck(data) @@ -261,12 +263,14 @@ func (h *UserHandler) Login(c *gin.Context) { var user model.User res := h.DB.Where("username = ?", data.Username).First(&user) if res.Error != nil { + h.redis.Set(c, verifyKey, true, 0) resp.ERROR(c, "用户名不存在") return } password := utils.GenPassword(data.Password, user.Salt) if password != user.Password { + h.redis.Set(c, verifyKey, true, 0) resp.ERROR(c, "用户名或密码错误") return } @@ -299,11 +303,13 @@ func (h *UserHandler) Login(c *gin.Context) { return } // 保存到 redis - key := fmt.Sprintf("users/%d", user.Id) - if _, err := h.redis.Set(c, key, tokenString, 0).Result(); err != nil { + sessionKey := fmt.Sprintf("users/%d", user.Id) + if _, err = h.redis.Set(c, sessionKey, tokenString, 0).Result(); err != nil { resp.ERROR(c, "error with save token: "+err.Error()) return } + // 移除登录行为验证码 + h.redis.Del(c, verifyKey) resp.SUCCESS(c, gin.H{"token": tokenString, "user_id": user.Id, "username": user.Username}) } diff --git a/web/.env.production b/web/.env.production index fee207e7..056d8338 100644 --- a/web/.env.production +++ b/web/.env.production @@ -1,6 +1,6 @@ VUE_APP_API_HOST= VUE_APP_WS_HOST= VUE_APP_KEY_PREFIX=GeekAI_ -VUE_APP_VERSION=v4.1.4 +VUE_APP_VERSION=v4.1.3 VUE_APP_DOCS_URL=https://docs.geekai.me VUE_APP_GIT_URL=https://github.com/yangjian102621/geekai diff --git a/web/src/components/ChatPrompt.vue b/web/src/components/ChatPrompt.vue index 92dd392c..518ce2f4 100644 --- a/web/src/components/ChatPrompt.vue +++ b/web/src/components/ChatPrompt.vue @@ -143,7 +143,7 @@ onMounted(() => { const links = props.data.content.match(linkRegex); if (links) { httpPost("/api/upload/list", {urls: links}).then(res => { - files.value = res.data + files.value = res.data.items for (let link of links) { if (isExternalImg(link, files.value)) { diff --git a/web/src/components/FileList.vue b/web/src/components/FileList.vue index b503b922..8bf96897 100644 --- a/web/src/components/FileList.vue +++ b/web/src/components/FileList.vue @@ -60,6 +60,7 @@ const removeFile = (file) => { display flex flex-flow row margin-right 10px + max-width 600px position relative .el-image { diff --git a/web/src/components/FileSelect.vue b/web/src/components/FileSelect.vue index d340c460..554fb209 100644 --- a/web/src/components/FileSelect.vue +++ b/web/src/components/FileSelect.vue @@ -68,7 +68,7 @@ const fileList = ref([]) const fetchFiles = () => { show.value = true httpPost("/api/upload/list").then(res => { - fileList.value = res.data + fileList.value = res.data.items }).catch(() => { }) } diff --git a/web/src/components/LoginDialog.vue b/web/src/components/LoginDialog.vue index 6af69740..58f81428 100644 --- a/web/src/components/LoginDialog.vue +++ b/web/src/components/LoginDialog.vue @@ -263,8 +263,8 @@ watch(() => props.show, (newValue) => { const login = ref(true) const data = ref({ - username: "", - password: "", + username: process.env.VUE_APP_USER, + password: process.env.VUE_APP_PASS, mobile: "", email: "", repass: "", @@ -285,6 +285,8 @@ const action = ref("login") const enableVerify = ref(false) const showResetPass = ref(false) const router = useRouter() +// 是否需要验证码,输入一次密码错之后就要验证码 +const needVerify = ref(false) onMounted(() => { const returnURL = `${location.protocol}//${location.host}/login/callback?action=login` @@ -338,7 +340,7 @@ const submitLogin = () => { if (data.value.password === '') { return ElMessage.error('请输入密码'); } - if (enableVerify.value) { + if (enableVerify.value && needVerify.value) { captchaRef.value.loadCaptcha() action.value = "login" } else { @@ -355,8 +357,10 @@ const doLogin = (verifyData) => { ElMessage.success("登录成功!") emits("hide") emits('success') + needVerify.value = false }).catch((e) => { ElMessage.error('登录失败,' + e.message) + needVerify.value = true }) } diff --git a/web/src/views/ChatPlus.vue b/web/src/views/ChatPlus.vue index 48161be4..389c2b1a 100644 --- a/web/src/views/ChatPlus.vue +++ b/web/src/views/ChatPlus.vue @@ -349,7 +349,6 @@ onMounted(() => { onUnmounted(() => { if (socket.value !== null) { - socket.value.close() socket.value = null } }) @@ -518,9 +517,9 @@ const loadChat = function (chat) { modelID.value = chat.model_id; chatId.value = chat.chat_id; showStopGenerate.value = false; - router.push(`/chat/${chatId.value}`) loadHistory.value = true - socket.value.close() + connect() + router.replace(`/chat/${chatId.value}`) } // 编辑会话标题 @@ -757,7 +756,7 @@ const sendMessage = function () { if (files.value.length === 1) { content += files.value.map(file => file.url).join(" ") } else if (files.value.length > 1) { - showMessageError("当前只支持一个文件!") + showMessageError("当前只支持上传一个文件!") return false } // 追加消息 diff --git a/web/src/views/Login.vue b/web/src/views/Login.vue index d0ad1b66..9a9f7a14 100644 --- a/web/src/views/Login.vue +++ b/web/src/views/Login.vue @@ -76,7 +76,6 @@ import {setUserToken} from "@/store/session"; import ResetPass from "@/components/ResetPass.vue"; import {showMessageError} from "@/utils/dialog"; import Captcha from "@/components/Captcha.vue"; -import QRCode from "qrcode"; import {setRoute} from "@/store/system"; const router = useRouter(); @@ -89,6 +88,8 @@ const licenseConfig = ref({}) const wechatLoginURL = ref('') const enableVerify = ref(false) const captchaRef = ref(null) +// 是否需要验证码,输入一次密码错之后就要验证码 +const needVerify = ref(false) onMounted(() => { // 获取系统配置 @@ -137,7 +138,7 @@ const login = function () { return showMessageError('请输入密码'); } - if (enableVerify.value) { + if (enableVerify.value && needVerify.value) { captchaRef.value.loadCaptcha() } else { doLogin({}) @@ -153,6 +154,7 @@ const doLogin = (verifyData) => { x: verifyData.x }).then((res) => { setUserToken(res.data.token) + needVerify.value = false if (isMobile()) { router.push('/mobile') } else { @@ -161,6 +163,7 @@ const doLogin = (verifyData) => { }).catch((e) => { showMessageError('登录失败,' + e.message) + needVerify.value = true }) } diff --git a/web/src/views/Suno.vue b/web/src/views/Suno.vue index b5a58832..92aa4fcc 100644 --- a/web/src/views/Suno.vue +++ b/web/src/views/Suno.vue @@ -610,7 +610,7 @@ const publishJob = (item) => { } const getShareURL = (item) => { - return `${location.protocol}//${location.host}/song/${item.id}` + return `${location.protocol}//${location.host}/song/${item.song_id}` } const uploadCover = (file) => { From dcdc0d891853e78bd494fdfda647ea7cab14117f Mon Sep 17 00:00:00 2001 From: RockYang Date: Fri, 13 Sep 2024 18:32:13 +0800 Subject: [PATCH 07/25] add tid field for chat app role --- api/handler/admin/chat_app_handler.go | 15 +++++++++++++++ api/store/model/chat_role.go | 1 + api/store/vo/chat_role.go | 4 +++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/api/handler/admin/chat_app_handler.go b/api/handler/admin/chat_app_handler.go index 8b54eec9..e068c645 100644 --- a/api/handler/admin/chat_app_handler.go +++ b/api/handler/admin/chat_app_handler.go @@ -75,13 +75,18 @@ func (h *ChatAppHandler) List(c *gin.Context) { // initialize model mane for role modelIds := make([]int, 0) + typeIds := make([]int, 0) for _, v := range items { if v.ModelId > 0 { modelIds = append(modelIds, v.ModelId) } + if v.Tid > 0 { + typeIds = append(typeIds, v.Tid) + } } modelNameMap := make(map[int]string) + typeNameMap := make(map[int]string) if len(modelIds) > 0 { var models []model.ChatModel tx := h.DB.Where("id IN ?", modelIds).Find(&models) @@ -91,6 +96,15 @@ func (h *ChatAppHandler) List(c *gin.Context) { } } } + if len(typeIds) > 0 { + var appTypes []model.AppType + tx := h.DB.Where("id IN ?", typeIds).Find(&appTypes) + if tx.Error == nil { + for _, m := range appTypes { + typeNameMap[int(m.Id)] = m.Name + } + } + } for _, v := range items { var role vo.ChatRole @@ -100,6 +114,7 @@ func (h *ChatAppHandler) List(c *gin.Context) { role.CreatedAt = v.CreatedAt.Unix() role.UpdatedAt = v.UpdatedAt.Unix() role.ModelName = modelNameMap[role.ModelId] + role.TypeName = typeNameMap[role.Tid] roles = append(roles, role) } } diff --git a/api/store/model/chat_role.go b/api/store/model/chat_role.go index 50e438bf..95dfbce6 100644 --- a/api/store/model/chat_role.go +++ b/api/store/model/chat_role.go @@ -2,6 +2,7 @@ package model type ChatRole struct { BaseModel + Tid int Key string `gorm:"column:marker;unique"` // 角色唯一标识 Name string // 角色名称 Context string `gorm:"column:context_json"` // 角色语料信息 json diff --git a/api/store/vo/chat_role.go b/api/store/vo/chat_role.go index 4bd530f8..ad82d949 100644 --- a/api/store/vo/chat_role.go +++ b/api/store/vo/chat_role.go @@ -4,7 +4,8 @@ import "geekai/core/types" type ChatRole struct { BaseVo - Key string `json:"key"` // 角色唯一标识 + Key string `json:"key"` // 角色唯一标识 + Tid uint `json:"tid"` Name string `json:"name"` // 角色名称 Context []types.Message `json:"context"` // 角色语料信息 HelloMsg string `json:"hello_msg"` // 打招呼的消息 @@ -13,4 +14,5 @@ type ChatRole struct { SortNum int `json:"sort"` // 排序 ModelId int `json:"model_id"` // 绑定模型 ID ModelName string `json:"model_name"` // 模型名称 + TypeName string `json:"type_name"` // 分类名称 } From 866564370d4125124f2379ff337ebd5ddd9de2b9 Mon Sep 17 00:00:00 2001 From: RockYang Date: Sat, 14 Sep 2024 05:54:55 +0800 Subject: [PATCH 08/25] return at least one chat role for getUserRoles API --- api/handler/admin/user_handler.go | 2 +- api/handler/chat_role_handler.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/api/handler/admin/user_handler.go b/api/handler/admin/user_handler.go index 6cd03e6a..4c247fbe 100644 --- a/api/handler/admin/user_handler.go +++ b/api/handler/admin/user_handler.go @@ -269,7 +269,7 @@ func (h *UserHandler) Remove(c *gin.Context) { } } if err != nil { - resp.ERROR(c, "删除失败") + resp.ERROR(c, err.Error()) tx.Rollback() return } diff --git a/api/handler/chat_role_handler.go b/api/handler/chat_role_handler.go index 8ef899be..aa579a51 100644 --- a/api/handler/chat_role_handler.go +++ b/api/handler/chat_role_handler.go @@ -69,7 +69,10 @@ func (h *ChatRoleHandler) ListByUser(c *gin.Context) { resp.ERROR(c, "角色解析失败!") return } - session = session.Where("marker IN ?", roleKeys) + // 保证用户至少有一个角色可用 + if len(roleKeys) > 0 { + session = session.Where("marker IN ?", roleKeys) + } } if id > 0 { From 131efd6ba5d4a795e5c3cf737beb8d20d48690bf Mon Sep 17 00:00:00 2001 From: RockYang Date: Sat, 14 Sep 2024 07:11:45 +0800 Subject: [PATCH 09/25] refactor chat message body struct --- CHANGELOG.md | 2 + api/core/types/chat.go | 19 ++-- api/core/types/web.go | 11 ++- api/handler/chatimpl/chat_handler.go | 122 ++++++++++++++----------- api/handler/chatimpl/openai_handler.go | 52 +++++++++-- api/handler/dalle_handler.go | 2 +- api/handler/markmap_handler.go | 10 +- api/store/model/chat_history.go | 21 +++-- api/store/vo/chat_role.go | 2 +- api/utils/net.go | 11 ++- database/update-v4.1.4.sql | 4 +- 11 files changed, 161 insertions(+), 95 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 857a200c..c119bd0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ * 功能优化:用户文件列表组件增加分页功能支持 * Bug修复:修复用户注册失败Bug,注册操作只弹出一次行为验证码 * 功能优化:首次登录不需要验证码,直接登录,登录失败之后才弹出验证码 +* 功能新增:给 AI 应用(角色)增加分类 +* 功能优化:允许用户在聊天页面设置是否使用流式输出或者一次性输出,兼容 GPT-O1 模型。 ## v4.1.3 * 功能优化:重构用户登录模块,给所有的登录组件增加行为验证码功能,支持用户绑定手机,邮箱和微信 diff --git a/api/core/types/chat.go b/api/core/types/chat.go index 42c86a2b..95a55397 100644 --- a/api/core/types/chat.go +++ b/api/core/types/chat.go @@ -9,14 +9,14 @@ package types // ApiRequest API 请求实体 type ApiRequest struct { - Model string `json:"model,omitempty"` // 兼容百度文心一言 - Temperature float32 `json:"temperature"` - MaxTokens int `json:"max_tokens,omitempty"` // 兼容百度文心一言 - Stream bool `json:"stream"` - Messages []interface{} `json:"messages,omitempty"` - Prompt []interface{} `json:"prompt,omitempty"` // 兼容 ChatGLM - Tools []Tool `json:"tools,omitempty"` - Functions []interface{} `json:"functions,omitempty"` // 兼容中转平台 + Model string `json:"model,omitempty"` + Temperature float32 `json:"temperature"` + MaxTokens int `json:"max_tokens,omitempty"` + MaxCompletionTokens int `json:"max_completion_tokens,omitempty"` // 兼容GPT O1 模型 + Stream bool `json:"stream,omitempty"` + Messages []interface{} `json:"messages,omitempty"` + Tools []Tool `json:"tools,omitempty"` + Functions []interface{} `json:"functions,omitempty"` // 兼容中转平台 ToolChoice string `json:"tool_choice,omitempty"` @@ -57,7 +57,8 @@ type ChatSession struct { ClientIP string `json:"client_ip"` // 客户端 IP ChatId string `json:"chat_id"` // 客户端聊天会话 ID, 多会话模式专用字段 Model ChatModel `json:"model"` // GPT 模型 - Tools string `json:"tools"` // 函数 + Tools []int `json:"tools"` // 工具函数列表 + Stream bool `json:"stream"` // 是否采用流式输出 } type ChatModel struct { diff --git a/api/core/types/web.go b/api/core/types/web.go index 408d9a58..8ca9b90f 100644 --- a/api/core/types/web.go +++ b/api/core/types/web.go @@ -17,8 +17,8 @@ type BizVo struct { Data interface{} `json:"data,omitempty"` } -// WsMessage Websocket message -type WsMessage struct { +// ReplyMessage 对话回复消息结构 +type ReplyMessage struct { Type WsMsgType `json:"type"` // 消息类别,start, end, img Content interface{} `json:"content"` } @@ -32,6 +32,13 @@ const ( WsErr = WsMsgType("error") ) +// InputMessage 对话输入消息结构 +type InputMessage struct { + Content string `json:"content"` + Tools []int `json:"tools"` // 允许调用工具列表 + Stream bool `json:"stream"` // 是否采用流式输出 +} + type BizCode int const ( diff --git a/api/handler/chatimpl/chat_handler.go b/api/handler/chatimpl/chat_handler.go index 730043e9..561a7918 100644 --- a/api/handler/chatimpl/chat_handler.go +++ b/api/handler/chatimpl/chat_handler.go @@ -73,13 +73,12 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) { roleId := h.GetInt(c, "role_id", 0) chatId := c.Query("chat_id") modelId := h.GetInt(c, "model_id", 0) - tools := c.Query("tools") client := types.NewWsClient(ws) var chatRole model.ChatRole res := h.DB.First(&chatRole, roleId) if res.Error != nil || !chatRole.Enable { - utils.ReplyMessage(client, "当前聊天角色不存在或者未启用,连接已关闭!!!") + utils.ReplyErrorMessage(client, "当前聊天角色不存在或者未启用,对话已关闭!!!") c.Abort() return } @@ -91,7 +90,7 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) { var chatModel model.ChatModel res = h.DB.First(&chatModel, modelId) if res.Error != nil || chatModel.Enabled == false { - utils.ReplyMessage(client, "当前AI模型暂未启用,连接已关闭!!!") + utils.ReplyErrorMessage(client, "当前AI模型暂未启用,对话已关闭!!!") c.Abort() return } @@ -100,7 +99,6 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) { SessionId: sessionId, ClientIP: c.ClientIP(), UserId: h.GetLoginUserId(c), - Tools: tools, } // use old chat data override the chat model and role ID @@ -137,20 +135,16 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) { return } - var message types.WsMessage + var message types.InputMessage err = utils.JsonDecode(string(msg), &message) if err != nil { continue } - // 心跳消息 - if message.Type == "heartbeat" { - logger.Debug("收到 Chat 心跳消息:", message.Content) - continue - } - - logger.Info("Receive a message: ", message.Content) + logger.Infof("Receive a message:%+v", message) + session.Tools = message.Tools + session.Stream = message.Stream ctx, cancel := context.WithCancel(context.Background()) h.ReqCancelFunc.Put(sessionId, cancel) // 回复消息 @@ -159,7 +153,7 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) { logger.Error(err) utils.ReplyMessage(client, err.Error()) } else { - utils.ReplyChunkMessage(client, types.WsMessage{Type: types.WsEnd}) + utils.ReplyChunkMessage(client, types.ReplyMessage{Type: types.WsEnd}) logger.Infof("回答完毕: %v", message.Content) } @@ -208,16 +202,21 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session *types.ChatSessio } var req = types.ApiRequest{ - Model: session.Model.Value, - Stream: true, + Model: session.Model.Value, + Temperature: session.Model.Temperature, + } + // 兼容 GPT-O1 模型 + if strings.HasPrefix(session.Model.Value, "o1-") { + req.MaxCompletionTokens = session.Model.MaxTokens + req.Stream = false + } else { + req.MaxTokens = session.Model.MaxTokens + req.Stream = session.Stream } - req.Temperature = session.Model.Temperature - req.MaxTokens = session.Model.MaxTokens - if session.Tools != "" { - toolIds := strings.Split(session.Tools, ",") + if len(session.Tools) > 0 && !strings.HasPrefix(session.Model.Value, "o1-") { var items []model.Function - res = h.DB.Where("enabled", true).Where("id IN ?", toolIds).Find(&items) + res = h.DB.Where("enabled", true).Where("id IN ?", session.Tools).Find(&items) if res.Error == nil { var tools = make([]types.Tool, 0) for _, v := range items { @@ -279,7 +278,7 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session *types.ChatSessio for i := len(messages) - 1; i >= 0; i-- { v := messages[i] - tks, _ := utils.CalcTokens(v.Content, req.Model) + tks, _ = utils.CalcTokens(v.Content, req.Model) // 上下文 token 超出了模型的最大上下文长度 if tokens+tks >= session.Model.MaxContext { break @@ -500,10 +499,17 @@ func (h *ChatHandler) subUserPower(userVo vo.User, session *types.ChatSession, p } } +type Usage struct { + Prompt string + Content string + PromptTokens int + CompletionTokens int + TotalTokens int +} + func (h *ChatHandler) saveChatHistory( req types.ApiRequest, - prompt string, - contents []string, + usage Usage, message types.Message, chatCtx []types.Message, session *types.ChatSession, @@ -514,8 +520,8 @@ func (h *ChatHandler) saveChatHistory( if message.Role == "" { message.Role = "assistant" } - message.Content = strings.Join(contents, "") - useMsg := types.Message{Role: "user", Content: prompt} + message.Content = usage.Content + useMsg := types.Message{Role: "user", Content: usage.Prompt} // 更新上下文消息,如果是调用函数则不需要更新上下文 if h.App.SysConfig.EnableContext { @@ -526,42 +532,52 @@ func (h *ChatHandler) saveChatHistory( // 追加聊天记录 // for prompt - promptToken, err := utils.CalcTokens(prompt, req.Model) - if err != nil { - logger.Error(err) + var promptTokens, replyTokens, totalTokens int + if usage.PromptTokens > 0 { + promptTokens = usage.PromptTokens + } else { + promptTokens, _ = utils.CalcTokens(usage.Content, req.Model) } + historyUserMsg := model.ChatMessage{ - UserId: userVo.Id, - ChatId: session.ChatId, - RoleId: role.Id, - Type: types.PromptMsg, - Icon: userVo.Avatar, - Content: template.HTMLEscapeString(prompt), - Tokens: promptToken, - UseContext: true, - Model: req.Model, + UserId: userVo.Id, + ChatId: session.ChatId, + RoleId: role.Id, + Type: types.PromptMsg, + Icon: userVo.Avatar, + Content: template.HTMLEscapeString(usage.Prompt), + Tokens: promptTokens, + TotalTokens: promptTokens, + UseContext: true, + Model: req.Model, } historyUserMsg.CreatedAt = promptCreatedAt historyUserMsg.UpdatedAt = promptCreatedAt - err = h.DB.Save(&historyUserMsg).Error + err := h.DB.Save(&historyUserMsg).Error if err != nil { logger.Error("failed to save prompt history message: ", err) } // for reply // 计算本次对话消耗的总 token 数量 - replyTokens, _ := utils.CalcTokens(message.Content, req.Model) - totalTokens := replyTokens + getTotalTokens(req) + if usage.CompletionTokens > 0 { + replyTokens = usage.CompletionTokens + totalTokens = usage.TotalTokens + } else { + replyTokens, _ = utils.CalcTokens(message.Content, req.Model) + totalTokens = replyTokens + getTotalTokens(req) + } historyReplyMsg := model.ChatMessage{ - UserId: userVo.Id, - ChatId: session.ChatId, - RoleId: role.Id, - Type: types.ReplyMsg, - Icon: role.Icon, - Content: message.Content, - Tokens: totalTokens, - UseContext: true, - Model: req.Model, + UserId: userVo.Id, + ChatId: session.ChatId, + RoleId: role.Id, + Type: types.ReplyMsg, + Icon: role.Icon, + Content: message.Content, + Tokens: replyTokens, + TotalTokens: totalTokens, + UseContext: true, + Model: req.Model, } historyReplyMsg.CreatedAt = replyCreatedAt historyReplyMsg.UpdatedAt = replyCreatedAt @@ -572,7 +588,7 @@ func (h *ChatHandler) saveChatHistory( // 更新用户算力 if session.Model.Power > 0 { - h.subUserPower(userVo, session, promptToken, replyTokens) + h.subUserPower(userVo, session, promptTokens, replyTokens) } // 保存当前会话 var chatItem model.ChatItem @@ -582,10 +598,10 @@ func (h *ChatHandler) saveChatHistory( chatItem.UserId = userVo.Id chatItem.RoleId = role.Id chatItem.ModelId = session.Model.Id - if utf8.RuneCountInString(prompt) > 30 { - chatItem.Title = string([]rune(prompt)[:30]) + "..." + if utf8.RuneCountInString(usage.Prompt) > 30 { + chatItem.Title = string([]rune(usage.Prompt)[:30]) + "..." } else { - chatItem.Title = prompt + chatItem.Title = usage.Prompt } chatItem.Model = req.Model err = h.DB.Create(&chatItem).Error diff --git a/api/handler/chatimpl/openai_handler.go b/api/handler/chatimpl/openai_handler.go index 775c8275..ccefe74f 100644 --- a/api/handler/chatimpl/openai_handler.go +++ b/api/handler/chatimpl/openai_handler.go @@ -23,6 +23,28 @@ import ( "time" ) +type respVo struct { + Id string `json:"id"` + Object string `json:"object"` + Created int `json:"created"` + Model string `json:"model"` + SystemFingerprint string `json:"system_fingerprint"` + Choices []struct { + Index int `json:"index"` + Message struct { + Role string `json:"role"` + Content string `json:"content"` + } `json:"message"` + Logprobs interface{} `json:"logprobs"` + FinishReason string `json:"finish_reason"` + } `json:"choices"` + Usage struct { + PromptTokens int `json:"prompt_tokens"` + CompletionTokens int `json:"completion_tokens"` + TotalTokens int `json:"total_tokens"` + } `json:"usage"` +} + // OPenAI 消息发送实现 func (h *ChatHandler) sendOpenAiMessage( chatCtx []types.Message, @@ -49,6 +71,10 @@ func (h *ChatHandler) sendOpenAiMessage( defer response.Body.Close() } + if response.StatusCode != 200 { + body, _ := io.ReadAll(response.Body) + return fmt.Errorf("请求 OpenAI API 失败:%d, %v", response.StatusCode, body) + } contentType := response.Header.Get("Content-Type") if strings.Contains(contentType, "text/event-stream") { replyCreatedAt := time.Now() // 记录回复时间 @@ -106,8 +132,8 @@ func (h *ChatHandler) sendOpenAiMessage( if res.Error == nil { toolCall = true callMsg := fmt.Sprintf("正在调用工具 `%s` 作答 ...\n\n", function.Label) - utils.ReplyChunkMessage(ws, types.WsMessage{Type: types.WsStart}) - utils.ReplyChunkMessage(ws, types.WsMessage{Type: types.WsMiddle, Content: callMsg}) + utils.ReplyChunkMessage(ws, types.ReplyMessage{Type: types.WsStart}) + utils.ReplyChunkMessage(ws, types.ReplyMessage{Type: types.WsMiddle, Content: callMsg}) contents = append(contents, callMsg) } continue @@ -125,10 +151,10 @@ func (h *ChatHandler) sendOpenAiMessage( content := responseBody.Choices[0].Delta.Content contents = append(contents, utils.InterfaceToString(content)) if isNew { - utils.ReplyChunkMessage(ws, types.WsMessage{Type: types.WsStart}) + utils.ReplyChunkMessage(ws, types.ReplyMessage{Type: types.WsStart}) isNew = false } - utils.ReplyChunkMessage(ws, types.WsMessage{ + utils.ReplyChunkMessage(ws, types.ReplyMessage{ Type: types.WsMiddle, Content: utils.InterfaceToString(responseBody.Choices[0].Delta.Content), }) @@ -161,13 +187,13 @@ func (h *ChatHandler) sendOpenAiMessage( } if errMsg != "" || apiRes.Code != types.Success { msg := "调用函数工具出错:" + apiRes.Message + errMsg - utils.ReplyChunkMessage(ws, types.WsMessage{ + utils.ReplyChunkMessage(ws, types.ReplyMessage{ Type: types.WsMiddle, Content: msg, }) contents = append(contents, msg) } else { - utils.ReplyChunkMessage(ws, types.WsMessage{ + utils.ReplyChunkMessage(ws, types.ReplyMessage{ Type: types.WsMiddle, Content: apiRes.Data, }) @@ -177,11 +203,17 @@ func (h *ChatHandler) sendOpenAiMessage( // 消息发送成功 if len(contents) > 0 { - h.saveChatHistory(req, prompt, contents, message, chatCtx, session, role, userVo, promptCreatedAt, replyCreatedAt) + usage := Usage{ + Prompt: prompt, + Content: strings.Join(contents, ""), + PromptTokens: 0, + CompletionTokens: 0, + TotalTokens: 0, + } + h.saveChatHistory(req, usage, message, chatCtx, session, role, userVo, promptCreatedAt, replyCreatedAt) } - } else { - body, _ := io.ReadAll(response.Body) - return fmt.Errorf("请求 OpenAI API 失败:%s", body) + } else { // 非流式输出 + } return nil diff --git a/api/handler/dalle_handler.go b/api/handler/dalle_handler.go index bcf44ba8..80b993ee 100644 --- a/api/handler/dalle_handler.go +++ b/api/handler/dalle_handler.go @@ -73,7 +73,7 @@ func (h *DallJobHandler) Client(c *gin.Context) { return } - var message types.WsMessage + var message types.ReplyMessage err = utils.JsonDecode(string(msg), &message) if err != nil { continue diff --git a/api/handler/markmap_handler.go b/api/handler/markmap_handler.go index b4147deb..9c624961 100644 --- a/api/handler/markmap_handler.go +++ b/api/handler/markmap_handler.go @@ -64,7 +64,7 @@ func (h *MarkMapHandler) Client(c *gin.Context) { return } - var message types.WsMessage + var message types.ReplyMessage err = utils.JsonDecode(string(msg), &message) if err != nil { continue @@ -85,7 +85,7 @@ func (h *MarkMapHandler) Client(c *gin.Context) { err = h.sendMessage(client, utils.InterfaceToString(message.Content), modelId, userId) if err != nil { logger.Error(err) - utils.ReplyChunkMessage(client, types.WsMessage{Type: types.WsErr, Content: err.Error()}) + utils.ReplyErrorMessage(client, err.Error()) } } @@ -170,16 +170,16 @@ func (h *MarkMapHandler) sendMessage(client *types.WsClient, prompt string, mode } if isNew { - utils.ReplyChunkMessage(client, types.WsMessage{Type: types.WsStart}) + utils.ReplyChunkMessage(client, types.ReplyMessage{Type: types.WsStart}) isNew = false } - utils.ReplyChunkMessage(client, types.WsMessage{ + utils.ReplyChunkMessage(client, types.ReplyMessage{ Type: types.WsMiddle, Content: utils.InterfaceToString(responseBody.Choices[0].Delta.Content), }) } // end for - utils.ReplyChunkMessage(client, types.WsMessage{Type: types.WsEnd}) + utils.ReplyChunkMessage(client, types.ReplyMessage{Type: types.WsEnd}) } else { body, _ := io.ReadAll(response.Body) diff --git a/api/store/model/chat_history.go b/api/store/model/chat_history.go index 36abeb4e..876c427f 100644 --- a/api/store/model/chat_history.go +++ b/api/store/model/chat_history.go @@ -4,16 +4,17 @@ import "gorm.io/gorm" type ChatMessage struct { BaseModel - ChatId string // 会话 ID - UserId uint // 用户 ID - RoleId uint // 角色 ID - Model string // AI模型 - Type string - Icon string - Tokens int - Content string - UseContext bool // 是否可以作为聊天上下文 - DeletedAt gorm.DeletedAt + ChatId string // 会话 ID + UserId uint // 用户 ID + RoleId uint // 角色 ID + Model string // AI模型 + Type string + Icon string + Tokens int + TotalTokens int // 总 token 消耗 + Content string + UseContext bool // 是否可以作为聊天上下文 + DeletedAt gorm.DeletedAt } func (ChatMessage) TableName() string { diff --git a/api/store/vo/chat_role.go b/api/store/vo/chat_role.go index ad82d949..9ab49cf6 100644 --- a/api/store/vo/chat_role.go +++ b/api/store/vo/chat_role.go @@ -5,7 +5,7 @@ import "geekai/core/types" type ChatRole struct { BaseVo Key string `json:"key"` // 角色唯一标识 - Tid uint `json:"tid"` + Tid int `json:"tid"` Name string `json:"name"` // 角色名称 Context []types.Message `json:"context"` // 角色语料信息 HelloMsg string `json:"hello_msg"` // 打招呼的消息 diff --git a/api/utils/net.go b/api/utils/net.go index 5f02922c..127f0f51 100644 --- a/api/utils/net.go +++ b/api/utils/net.go @@ -33,9 +33,14 @@ func ReplyChunkMessage(client *types.WsClient, message interface{}) { // ReplyMessage 回复客户端一条完整的消息 func ReplyMessage(ws *types.WsClient, message interface{}) { - ReplyChunkMessage(ws, types.WsMessage{Type: types.WsStart}) - ReplyChunkMessage(ws, types.WsMessage{Type: types.WsMiddle, Content: message}) - ReplyChunkMessage(ws, types.WsMessage{Type: types.WsEnd}) + ReplyChunkMessage(ws, types.ReplyMessage{Type: types.WsStart}) + ReplyChunkMessage(ws, types.ReplyMessage{Type: types.WsMiddle, Content: message}) + ReplyChunkMessage(ws, types.ReplyMessage{Type: types.WsEnd}) +} + +// ReplyErrorMessage 向客户端发送错误消息 +func ReplyErrorMessage(ws *types.WsClient, message interface{}) { + ReplyChunkMessage(ws, types.ReplyMessage{Type: types.WsErr, Content: message}) } func DownloadImage(imageURL string, proxy string) ([]byte, error) { diff --git a/database/update-v4.1.4.sql b/database/update-v4.1.4.sql index 86d47b63..28d93e5c 100644 --- a/database/update-v4.1.4.sql +++ b/database/update-v4.1.4.sql @@ -9,4 +9,6 @@ CREATE TABLE `chatgpt_app_types` ( ALTER TABLE `chatgpt_app_types`ADD PRIMARY KEY (`id`); ALTER TABLE `chatgpt_app_types` MODIFY `id` int NOT NULL AUTO_INCREMENT; -ALTER TABLE `chatgpt_chat_roles` ADD `tid` INT NOT NULL COMMENT '分类ID' AFTER `name`; \ No newline at end of file +ALTER TABLE `chatgpt_chat_roles` ADD `tid` INT NOT NULL COMMENT '分类ID' AFTER `name`; + +ALTER TABLE `chatgpt_chat_history` ADD `total_tokens` INT NOT NULL COMMENT '消耗总Token长度' AFTER `tokens`; \ No newline at end of file From aaea23f785b20d72198edf6a9a56fb04b698b5c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=8F=8C=E6=98=8E?= Date: Sat, 14 Sep 2024 11:05:49 +0800 Subject: [PATCH 10/25] =?UTF-8?q?feat:=20=E5=BA=94=E7=94=A8=E5=88=86?= =?UTF-8?q?=E7=B1=BB=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/assets/css/chat-app.styl | 41 +++++- web/src/assets/css/luma.styl | 2 +- web/src/router.js | 4 +- web/src/views/ChatApps.vue | 54 ++++++-- web/src/views/Luma.vue | 4 +- web/src/views/admin/AppType.vue | 208 ++++++++++++++++++++++++++++++- web/src/views/admin/Apps.vue | 31 +++++ web/vue.config.js | 6 + 8 files changed, 328 insertions(+), 22 deletions(-) diff --git a/web/src/assets/css/chat-app.styl b/web/src/assets/css/chat-app.styl index c7692bd4..a6dff71e 100644 --- a/web/src/assets/css/chat-app.styl +++ b/web/src/assets/css/chat-app.styl @@ -2,10 +2,49 @@ background-color: #282c34; height 100% + .apps-type-nav{ + height 43px + padding 8px 0; + margin-bottom 3px + } + + .scrollbar-type-nav{ + display flex + align-items center + height 43px + padding 0 5px + li{ + flex-shrink 0 + display flex + align-items center + justify-content center + margin 0 10px + height 26px + border-radius 4px + border 1px solid rgb(80,80,80) + padding 2px 12px + background rgba(60,60,60 0.9) + color #fff + font-size 14px + cursor pointer + + .image { + width 22px + height 22px + overflow hidden + margin-right 5px + } + &.active{ + background #21aa93; + } + } + } + + .inner { display flex color #ffffff - padding 15px; + padding 2px 15px; overflow-y visible overflow-x hidden diff --git a/web/src/assets/css/luma.styl b/web/src/assets/css/luma.styl index 212a47c8..a3b95efb 100644 --- a/web/src/assets/css/luma.styl +++ b/web/src/assets/css/luma.styl @@ -141,7 +141,7 @@ display flex flex-flow row align-items center - height 100px + min-height 100px padding 10px 15px border-radius 10px cursor pointer diff --git a/web/src/router.js b/web/src/router.js index 8507382a..e1ca5cd6 100644 --- a/web/src/router.js +++ b/web/src/router.js @@ -170,13 +170,13 @@ const routes = [ { path: '/admin/app', name: 'admin-app', - meta: {title: '应用管理'}, + meta: {title: '应用列表'}, component: () => import('@/views/admin/Apps.vue'), }, { path: '/admin/app/type', name: 'admin-app-type', - meta: {title: '应用管理'}, + meta: {title: '应用分类'}, component: () => import('@/views/admin/AppType.vue'), }, { diff --git a/web/src/views/ChatApps.vue b/web/src/views/ChatApps.vue index 45ab5677..8794de95 100644 --- a/web/src/views/ChatApps.vue +++ b/web/src/views/ChatApps.vue @@ -1,6 +1,20 @@ @@ -80,12 +72,6 @@ getSystemInfo().then(res => { ElMessage.error("加载系统配置失败: " + e.message) }) -const keyupHandle = (e) => { - if (e.key === 'Enter') { - login(); - } -} - const login = function () { if (username.value === '') { return ElMessage.error('请输入用户名'); diff --git a/web/src/views/mobile/ChatList.vue b/web/src/views/mobile/ChatList.vue index 20864cf3..8d9de46d 100644 --- a/web/src/views/mobile/ChatList.vue +++ b/web/src/views/mobile/ChatList.vue @@ -225,7 +225,7 @@ const newChat = (item) => { } showPicker.value = false const options = item.selectedOptions - router.push(`/mobile/chat/session?title=新对话&role_id=${options[0].value}&model_id=${options[1].value}&chat_id=0}`) + router.push(`/mobile/chat/session?title=新对话&role_id=${options[0].value}&model_id=${options[1].value}`) } const changeChat = (chat) => { diff --git a/web/src/views/mobile/ChatSession.vue b/web/src/views/mobile/ChatSession.vue index 7a458bdd..e96ff6fe 100644 --- a/web/src/views/mobile/ChatSession.vue +++ b/web/src/views/mobile/ChatSession.vue @@ -123,7 +123,7 @@ diff --git a/web/src/utils/libs.js b/web/src/utils/libs.js index 06b65d5c..d57cbbd1 100644 --- a/web/src/utils/libs.js +++ b/web/src/utils/libs.js @@ -232,4 +232,9 @@ export const replaceImg =(img) => { } return img } +export function isChrome() { + const userAgent = navigator.userAgent.toLowerCase(); + return /chrome/.test(userAgent) && !/edg/.test(userAgent); +} + From 158db83965439db118943a19e820e002607ccbae Mon Sep 17 00:00:00 2001 From: RockYang Date: Wed, 18 Sep 2024 07:03:46 +0800 Subject: [PATCH 13/25] add geek payment --- CHANGELOG.md | 2 + api/core/types/config.go | 34 ++--- api/handler/payment_handler.go | 193 ++++++++++++------------- api/handler/test_handler.go | 4 +- api/res/img/geek-pay.jpg | Bin 0 -> 27461 bytes api/res/img/qq-pay.jpg | Bin 0 -> 17178 bytes api/service/payment/geekpay_service.go | 136 +++++++++++++++++ api/service/payment/payjs_service.go | 153 -------------------- api/store/model/order.go | 3 +- api/store/vo/order.go | 1 + database/update-v4.1.4.sql | 3 +- web/src/assets/css/member.styl | 8 +- web/src/assets/iconfont/iconfont.css | 22 ++- web/src/assets/iconfont/iconfont.js | 2 +- web/src/assets/iconfont/iconfont.json | 28 ++++ web/src/assets/iconfont/iconfont.ttf | Bin 29056 -> 30468 bytes web/src/assets/iconfont/iconfont.woff | Bin 19360 -> 20304 bytes web/src/assets/iconfont/iconfont.woff2 | Bin 16812 -> 17612 bytes web/src/views/Member.vue | 157 ++++++-------------- 19 files changed, 355 insertions(+), 391 deletions(-) create mode 100644 api/res/img/geek-pay.jpg create mode 100644 api/res/img/qq-pay.jpg create mode 100644 api/service/payment/geekpay_service.go delete mode 100644 api/service/payment/payjs_service.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 04a64d10..f610f799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ * 功能优化:首次登录不需要验证码,直接登录,登录失败之后才弹出验证码 * 功能新增:给 AI 应用(角色)增加分类,前端支持分类筛选 * 功能优化:允许用户在聊天页面设置是否使用流式输出或者一次性输出,兼容 GPT-O1 模型。 +* 功能优化:移除PayJS支付渠道支持,PayJs已经关闭注册服务,请使用其他支付方式。 +* 功能新增:新增GeeK易支付支付渠道,支持支付宝,微信支付,QQ钱包,京东支付,抖音支付,Paypal支付等支付方式 ## v4.1.3 * 功能优化:重构用户登录模块,给所有的登录组件增加行为验证码功能,支持用户绑定手机,邮箱和微信 diff --git a/api/core/types/config.go b/api/core/types/config.go index 9638f620..f6e59727 100644 --- a/api/core/types/config.go +++ b/api/core/types/config.go @@ -12,24 +12,23 @@ import ( ) type AppConfig struct { - Path string `toml:"-"` - Listen string - Session Session - AdminSession Session - ProxyURL string - MysqlDns string // mysql 连接地址 - StaticDir string // 静态资源目录 - StaticUrl string // 静态资源 URL - Redis RedisConfig // redis 连接信息 - ApiConfig ApiConfig // ChatPlus API authorization configs - SMS SMSConfig // send mobile message config - OSS OSSConfig // OSS config - + Path string `toml:"-"` + Listen string + Session Session + AdminSession Session + ProxyURL string + MysqlDns string // mysql 连接地址 + StaticDir string // 静态资源目录 + StaticUrl string // 静态资源 URL + Redis RedisConfig // redis 连接信息 + ApiConfig ApiConfig // ChatPlus API authorization configs + SMS SMSConfig // send mobile message config + OSS OSSConfig // OSS config + SmtpConfig SmtpConfig // 邮件发送配置 XXLConfig XXLConfig AlipayConfig AlipayConfig // 支付宝支付渠道配置 HuPiPayConfig HuPiPayConfig // 虎皮椒支付配置 - SmtpConfig SmtpConfig // 邮件发送配置 - JPayConfig JPayConfig // payjs 支付配置 + GeekPayConfig GeekPayConfig // GEEK 支付配置 WechatPayConfig WechatPayConfig // 微信支付渠道配置 TikaHost string // TiKa 服务器地址 } @@ -83,10 +82,9 @@ type HuPiPayConfig struct { //虎皮椒第四方支付配置 ReturnURL string // 支付成功返回地址 } -// JPayConfig PayJs 支付配置 -type JPayConfig struct { +// GeekPayConfig GEEK支付配置 +type GeekPayConfig struct { Enabled bool - Name string // 支付名称,默认 wechat AppId string // 商户 ID PrivateKey string // 私钥 ApiURL string // API 网关 diff --git a/api/handler/payment_handler.go b/api/handler/payment_handler.go index 5d807f79..8a0112a9 100644 --- a/api/handler/payment_handler.go +++ b/api/handler/payment_handler.go @@ -19,7 +19,6 @@ import ( "geekai/utils" "geekai/utils/resp" "github.com/shopspring/decimal" - "math" "net/http" "net/url" "sync" @@ -34,19 +33,12 @@ type PayWay struct { Value string `json:"value"` } -var ( - PayWayAlipay = PayWay{Name: "支付宝", Value: "alipay"} - PayWayXunHu = PayWay{Name: "虎皮椒", Value: "hupi"} - PayWayJs = PayWay{Name: "PayJS", Value: "payjs"} - PayWayWechat = PayWay{Name: "微信支付", Value: "wechat"} -) - // PaymentHandler 支付服务回调 handler type PaymentHandler struct { BaseHandler alipayService *payment.AlipayService huPiPayService *payment.HuPiPayService - jsPayService *payment.JPayService + geekPayService *payment.GeekPayService wechatPayService *payment.WechatPayService snowflake *service.Snowflake fs embed.FS @@ -58,7 +50,7 @@ func NewPaymentHandler( server *core.AppServer, alipayService *payment.AlipayService, huPiPayService *payment.HuPiPayService, - jsPayService *payment.JPayService, + geekPayService *payment.GeekPayService, wechatPayService *payment.WechatPayService, db *gorm.DB, snowflake *service.Snowflake, @@ -66,7 +58,7 @@ func NewPaymentHandler( return &PaymentHandler{ alipayService: alipayService, huPiPayService: huPiPayService, - jsPayService: jsPayService, + geekPayService: geekPayService, wechatPayService: wechatPayService, snowflake: snowflake, fs: fs, @@ -81,10 +73,9 @@ func NewPaymentHandler( func (h *PaymentHandler) DoPay(c *gin.Context) { orderNo := h.GetTrim(c, "order_no") - payWay := h.GetTrim(c, "pay_way") t := h.GetInt(c, "t", 0) sign := h.GetTrim(c, "sign") - signStr := fmt.Sprintf("%s-%s-%d-%s", orderNo, payWay, t, h.signKey) + signStr := fmt.Sprintf("%s-%d-%s", orderNo, t, h.signKey) newSign := utils.Sha256(signStr) if newSign != sign { resp.ERROR(c, "订单签名错误!") @@ -118,7 +109,7 @@ func (h *PaymentHandler) DoPay(c *gin.Context) { // 更新扫码状态 h.DB.Model(&order).UpdateColumn("status", types.OrderScanned) - if payWay == "alipay" { // 支付宝 + if order.PayWay == "alipay" { // 支付宝 amount := fmt.Sprintf("%.2f", order.Amount) uri, err := h.alipayService.PayUrlMobile(order.OrderNo, amount, order.Subject) if err != nil { @@ -128,7 +119,7 @@ func (h *PaymentHandler) DoPay(c *gin.Context) { c.Redirect(302, uri) return - } else if payWay == "hupi" { // 虎皮椒支付 + } else if order.PayWay == "hupi" { // 虎皮椒支付 params := payment.HuPiPayReq{ Version: "1.1", TradeOrderId: orderNo, @@ -144,15 +135,41 @@ func (h *PaymentHandler) DoPay(c *gin.Context) { } c.Redirect(302, r.URL) + } else if order.PayWay == "wechat" { + uri, err := h.wechatPayService.PayUrlNative(order.OrderNo, int(order.Amount*100), order.Subject) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + c.Redirect(302, uri) + } else if order.PayWay == "geek" { + params := payment.GeekPayParams{ + OutTradeNo: orderNo, + Method: "web", + Name: order.Subject, + Money: fmt.Sprintf("%f", order.Amount), + ClientIP: c.ClientIP(), + Device: "pc", + Type: "alipay", + } + + s, err := h.geekPayService.Pay(params) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + resp.SUCCESS(c, s) } - resp.ERROR(c, "Invalid operations") + //resp.ERROR(c, "Invalid operations") } // PayQrcode 生成支付 URL 二维码 func (h *PaymentHandler) PayQrcode(c *gin.Context) { var data struct { - PayWay string `json:"pay_way"` // 支付方式 - ProductId uint `json:"product_id"` + PayWay string `json:"pay_way"` // 支付方式 + PayType string `json:"pay_type"` // 支付类别:wechat,alipay,qq... + ProductId uint `json:"product_id"` // 支付产品ID } if err := c.ShouldBindJSON(&data); err != nil { resp.ERROR(c, types.InvalidArgs) @@ -177,24 +194,22 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) { return } - var payWay string var notifyURL string switch data.PayWay { case "hupi": - payWay = PayWayXunHu.Value notifyURL = h.App.Config.HuPiPayConfig.NotifyURL break - case "payjs": - payWay = PayWayJs.Value - notifyURL = h.App.Config.JPayConfig.NotifyURL + case "geek": + notifyURL = h.App.Config.GeekPayConfig.NotifyURL break - case "alipay": - payWay = PayWayAlipay.Value + case "alipay": // 支付宝商户支付 notifyURL = h.App.Config.AlipayConfig.NotifyURL break - default: - payWay = PayWayWechat.Value + case "wechat": // 微信商户支付 notifyURL = h.App.Config.WechatPayConfig.NotifyURL + default: + resp.ERROR(c, "Invalid pay way") + return } // 创建订单 @@ -215,7 +230,8 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) { Subject: product.Name, Amount: amount, Status: types.OrderNotPaid, - PayWay: payWay, + PayWay: data.PayWay, + PayType: data.PayType, Remark: utils.JsonEncode(remark), } res = h.DB.Create(&order) @@ -224,36 +240,26 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) { return } - // PayJs 单独处理,只能用官方生成的二维码 - if data.PayWay == "payjs" { - params := payment.JPayReq{ - TotalFee: int(math.Ceil(order.Amount * 100)), - OutTradeNo: order.OrderNo, - Subject: product.Name, - } - r := h.jsPayService.Pay(params) - if r.IsOK() { - resp.SUCCESS(c, gin.H{"order_no": order.OrderNo, "image": r.Qrcode}) - return - } else { - resp.ERROR(c, "error with generating payment qrcode: "+r.ReturnMsg) - return - } - } - var logo string - if data.PayWay == "alipay" { + switch data.PayType { + case "alipay": logo = "res/img/alipay.jpg" - } else if data.PayWay == "hupi" { - if h.App.Config.HuPiPayConfig.Name == "wechat" { - logo = "res/img/wechat-pay.jpg" - } else { - logo = "res/img/alipay.jpg" - } - } else if data.PayWay == "wechat" { + break + case "wechat": + logo = "res/img/wechat-pay.jpg" + break + case "qq": + logo = "res/img/qq-pay.jpg" + break + default: + logo = "res/img/geek-pay.jpg" + + } + if data.PayType == "alipay" { + logo = "res/img/alipay.jpg" + } else if data.PayType == "wechat" { logo = "res/img/wechat-pay.jpg" } - file, err := h.fs.Open(logo) if err != nil { resp.ERROR(c, "error with open qrcode log file: "+err.Error()) @@ -268,31 +274,21 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) { timestamp := time.Now().Unix() signStr := fmt.Sprintf("%s-%s-%d-%s", orderNo, data.PayWay, timestamp, h.signKey) sign := utils.Sha256(signStr) - var imageURL string - if data.PayWay == "wechat" { - payUrl, err := h.wechatPayService.PayUrlNative(order.OrderNo, int(math.Floor(order.Amount*100)), product.Name) - if err != nil { - resp.ERROR(c, "error with generating wechat payment qrcode: "+err.Error()) - return - } else { - imageURL = payUrl - } - } else { - imageURL = fmt.Sprintf("%s://%s/api/payment/doPay?order_no=%s&pay_way=%s&t=%d&sign=%s", parse.Scheme, parse.Host, orderNo, data.PayWay, timestamp, sign) - } - imgData, err := utils.GenQrcode(imageURL, 400, file) + payUrl := fmt.Sprintf("%s://%s/api/payment/doPay?order_no=%s&pay_way=%s&pay_type=%s&t=%d&sign=%s", parse.Scheme, parse.Host, orderNo, data.PayWay, data.PayType, timestamp, sign) + imgData, err := utils.GenQrcode(payUrl, 400, file) if err != nil { resp.ERROR(c, err.Error()) return } imgDataBase64 := base64.StdEncoding.EncodeToString(imgData) - resp.SUCCESS(c, gin.H{"order_no": orderNo, "image": fmt.Sprintf("data:image/jpg;base64, %s", imgDataBase64), "url": imageURL}) + resp.SUCCESS(c, gin.H{"order_no": orderNo, "image": fmt.Sprintf("data:image/jpg;base64, %s", imgDataBase64), "url": payUrl}) } // Mobile 移动端支付 func (h *PaymentHandler) Mobile(c *gin.Context) { var data struct { - PayWay string `json:"pay_way"` // 支付方式 + PayWay string `json:"pay_way"` // 支付方式 + PayType string `json:"pay_type"` // 支付类别:wechat,alipay,qq... ProductId uint `json:"product_id"` } if err := c.ShouldBindJSON(&data); err != nil { @@ -319,12 +315,10 @@ func (h *PaymentHandler) Mobile(c *gin.Context) { } amount, _ := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Float64() - var payWay string var notifyURL, returnURL string var payURL string switch data.PayWay { case "hupi": - payWay = PayWayXunHu.Name notifyURL = h.App.Config.HuPiPayConfig.NotifyURL returnURL = h.App.Config.HuPiPayConfig.ReturnURL parse, _ := url.Parse(h.App.Config.HuPiPayConfig.ReturnURL) @@ -349,20 +343,16 @@ func (h *PaymentHandler) Mobile(c *gin.Context) { return } payURL = r.URL - case "payjs": - payWay = PayWayJs.Name - notifyURL = h.App.Config.JPayConfig.NotifyURL - returnURL = h.App.Config.JPayConfig.ReturnURL - totalFee := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Mul(decimal.NewFromInt(100)).IntPart() - params := url.Values{} - params.Add("total_fee", fmt.Sprintf("%d", totalFee)) - params.Add("out_trade_no", orderNo) - params.Add("body", product.Name) - params.Add("notify_url", notifyURL) - params.Add("auto", "0") - payURL = h.jsPayService.PayH5(params) + case "geek": + //totalFee := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Mul(decimal.NewFromInt(100)).IntPart() + //params := url.Values{} + //params.Add("total_fee", fmt.Sprintf("%d", totalFee)) + //params.Add("out_trade_no", orderNo) + //params.Add("body", product.Name) + //params.Add("notify_url", notifyURL) + //params.Add("auto", "0") + //payURL = h.geekPayService.Pay(params) case "alipay": - payWay = PayWayAlipay.Name payURL, err = h.alipayService.PayUrlMobile(orderNo, fmt.Sprintf("%.2f", amount), product.Name) if err != nil { errMsg := "error with generating Alipay URL: " + err.Error() @@ -370,7 +360,6 @@ func (h *PaymentHandler) Mobile(c *gin.Context) { return } case "wechat": - payWay = PayWayWechat.Name payURL, err = h.wechatPayService.PayUrlH5(orderNo, int(amount*100), product.Name, c.ClientIP()) if err != nil { errMsg := "error with generating Wechat URL: " + err.Error() @@ -399,7 +388,8 @@ func (h *PaymentHandler) Mobile(c *gin.Context) { Subject: product.Name, Amount: amount, Status: types.OrderNotPaid, - PayWay: payWay, + PayWay: data.PayWay, + PayType: data.PayType, Remark: utils.JsonEncode(remark), } res = h.DB.Create(&order) @@ -506,20 +496,25 @@ func (h *PaymentHandler) notify(orderNo string, tradeNo string) error { // GetPayWays 获取支付方式 func (h *PaymentHandler) GetPayWays(c *gin.Context) { - data := gin.H{} + payWays := make([]gin.H, 0) if h.App.Config.AlipayConfig.Enabled { - data["alipay"] = gin.H{"name": "alipay"} + payWays = append(payWays, gin.H{"pay_way": "alipay", "pay_type": "alipay"}) } if h.App.Config.HuPiPayConfig.Enabled { - data["hupi"] = gin.H{"name": h.App.Config.HuPiPayConfig.Name} + payWays = append(payWays, gin.H{"pay_way": "hupi", "pay_type": h.App.Config.HuPiPayConfig.Name}) } - if h.App.Config.JPayConfig.Enabled { - data["payjs"] = gin.H{"name": h.App.Config.JPayConfig.Name} + if h.App.Config.GeekPayConfig.Enabled { + payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "alipay"}) + payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "wechat"}) + payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "qq"}) + payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "jd"}) + payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "douyin"}) + payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "paypal"}) } if h.App.Config.WechatPayConfig.Enabled { - data["wechat"] = gin.H{"name": "wechat"} + payWays = append(payWays, gin.H{"pay_way": "wechat", "pay_type": "wechat"}) } - resp.SUCCESS(c, data) + resp.SUCCESS(c, payWays) } // HuPiPayNotify 虎皮椒支付异步回调 @@ -593,12 +588,12 @@ func (h *PaymentHandler) PayJsNotify(c *gin.Context) { // 校验订单支付状态 tradeNo := c.Request.Form.Get("payjs_order_id") - err = h.jsPayService.TradeVerify(tradeNo) - if err != nil { - logger.Error("订单校验失败:", err) - c.String(http.StatusOK, "fail") - return - } + //err = h.geekPayService.TradeVerify(tradeNo) + //if err != nil { + // logger.Error("订单校验失败:", err) + // c.String(http.StatusOK, "fail") + // return + //} err = h.notify(orderNo, tradeNo) if err != nil { diff --git a/api/handler/test_handler.go b/api/handler/test_handler.go index fe38bc38..88e95a2f 100644 --- a/api/handler/test_handler.go +++ b/api/handler/test_handler.go @@ -11,10 +11,10 @@ import ( type TestHandler struct { db *gorm.DB snowflake *service.Snowflake - js *payment.JPayService + js *payment.GeekPayService } -func NewTestHandler(db *gorm.DB, snowflake *service.Snowflake, js *payment.JPayService) *TestHandler { +func NewTestHandler(db *gorm.DB, snowflake *service.Snowflake, js *payment.GeekPayService) *TestHandler { return &TestHandler{db: db, snowflake: snowflake, js: js} } diff --git a/api/res/img/geek-pay.jpg b/api/res/img/geek-pay.jpg new file mode 100644 index 0000000000000000000000000000000000000000..48baf80d101b4ebd13d02ea0b6066dc082fe2b5a GIT binary patch literal 27461 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 b!CNu*|9I{A8}R760b;up{C^zzejEG`INjh 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 apVj@?{rKxp|JN}8dqnO3 0 { - pList = append(pList, key+"="+value) - } - } - var src = strings.Join(pList, "&") - src += "&key=" + js.config.PrivateKey - - md5bs := md5.Sum([]byte(src)) - md5res := hex.EncodeToString(md5bs[:]) - return strings.ToUpper(md5res) -} - -// TradeVerify 查询订单支付状态 -// @param tradeNo 支付平台交易 ID -func (js *JPayService) TradeVerify(tradeNo string) error { - apiURL := fmt.Sprintf("%s/api/check", js.config.ApiURL) - params := url.Values{} - params.Add("payjs_order_id", tradeNo) - params.Add("sign", js.sign(params)) - data := strings.NewReader(params.Encode()) - resp, err := http.Post(apiURL, "application/x-www-form-urlencoded", data) - if err != nil { - return fmt.Errorf("error with http reqeust: %v", err) - } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("error with reading response: %v", err) - } - - var r struct { - ReturnCode int `json:"return_code"` - Status int `json:"status"` - } - err = utils.JsonDecode(string(body), &r) - if err != nil { - return fmt.Errorf("error with decode response: %v", err) - } - - if r.ReturnCode == 1 && r.Status == 1 { - return nil - } else { - logger.Errorf("PayJs 支付验证响应:%s", string(body)) - return errors.New("order not paid") - } -} diff --git a/api/store/model/order.go b/api/store/model/order.go index a1c6929f..86e81ca7 100644 --- a/api/store/model/order.go +++ b/api/store/model/order.go @@ -18,6 +18,7 @@ type Order struct { Status types.OrderStatus Remark string PayTime int64 - PayWay string // 支付方式 + PayWay string // 支付渠道 + PayType string // 支付类型 DeletedAt gorm.DeletedAt } diff --git a/api/store/vo/order.go b/api/store/vo/order.go index c076d002..39693649 100644 --- a/api/store/vo/order.go +++ b/api/store/vo/order.go @@ -16,5 +16,6 @@ type Order struct { Status types.OrderStatus `json:"status"` PayTime int64 `json:"pay_time"` PayWay string `json:"pay_way"` + PayType string `json:"pay_type"` Remark types.OrderRemark `json:"remark"` } diff --git a/database/update-v4.1.4.sql b/database/update-v4.1.4.sql index 28d93e5c..0ea7f36f 100644 --- a/database/update-v4.1.4.sql +++ b/database/update-v4.1.4.sql @@ -11,4 +11,5 @@ ALTER TABLE `chatgpt_app_types`ADD PRIMARY KEY (`id`); ALTER TABLE `chatgpt_app_types` MODIFY `id` int NOT NULL AUTO_INCREMENT; ALTER TABLE `chatgpt_chat_roles` ADD `tid` INT NOT NULL COMMENT '分类ID' AFTER `name`; -ALTER TABLE `chatgpt_chat_history` ADD `total_tokens` INT NOT NULL COMMENT '消耗总Token长度' AFTER `tokens`; \ No newline at end of file +ALTER TABLE `chatgpt_chat_history` ADD `total_tokens` INT NOT NULL COMMENT '消耗总Token长度' AFTER `tokens`; +ALTER TABLE `chatgpt_orders` ADD `pay_type` VARCHAR(30) NOT NULL COMMENT '支付类型' AFTER `pay_way`; \ No newline at end of file diff --git a/web/src/assets/css/member.styl b/web/src/assets/css/member.styl index c1f61bb5..9dab8325 100644 --- a/web/src/assets/css/member.styl +++ b/web/src/assets/css/member.styl @@ -110,6 +110,7 @@ overflow hidden cursor pointer transition: all 0.3s ease; /* 添加过渡效果 */ + margin-bottom 20px .image-container { display flex @@ -175,10 +176,11 @@ .pay-way { padding 10px 0 display flex - justify-content: space-between + justify-content: center + flex-wrap wrap - .iconfont { - margin-right 5px + .el-button { + margin 10px 5px 0 5px } } } diff --git a/web/src/assets/iconfont/iconfont.css b/web/src/assets/iconfont/iconfont.css index 31901bb5..a0959ced 100644 --- a/web/src/assets/iconfont/iconfont.css +++ b/web/src/assets/iconfont/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 4125778 */ - src: url('iconfont.woff2?t=1725929120246') format('woff2'), - url('iconfont.woff?t=1725929120246') format('woff'), - url('iconfont.ttf?t=1725929120246') format('truetype'); + src: url('iconfont.woff2?t=1726612860394') format('woff2'), + url('iconfont.woff?t=1726612860394') format('woff'), + url('iconfont.ttf?t=1726612860394') format('truetype'); } .iconfont { @@ -13,6 +13,22 @@ -moz-osx-font-smoothing: grayscale; } +.icon-douyin:before { + content: "\e852"; +} + +.icon-paypal:before { + content: "\e60f"; +} + +.icon-qq:before { + content: "\e69f"; +} + +.icon-jd-pay:before { + content: "\e8dd"; +} + .icon-luma:before { content: "\e704"; } diff --git a/web/src/assets/iconfont/iconfont.js b/web/src/assets/iconfont/iconfont.js index ad94b5e2..32973473 100644 --- a/web/src/assets/iconfont/iconfont.js +++ b/web/src/assets/iconfont/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_4125778='',(a=>{var l=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var t,h,i,o,z,m=function(l,c){c.parentNode.insertBefore(l,c)};if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}t=function(){var l,c=document.createElement("div");c.innerHTML=a._iconfont_svg_string_4125778,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(l=document.body).firstChild?m(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(t,0):(h=function(){document.removeEventListener("DOMContentLoaded",h,!1),t()},document.addEventListener("DOMContentLoaded",h,!1)):document.attachEvent&&(i=t,o=a.document,z=!1,p(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,s())})}function s(){z||(z=!0,i())}function p(){try{o.documentElement.doScroll("left")}catch(l){return void setTimeout(p,50)}s()}})(window); \ No newline at end of file +window._iconfont_svg_string_4125778='',(a=>{var l=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,z,m=function(l,c){c.parentNode.insertBefore(l,c)};if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}h=function(){var l,c=document.createElement("div");c.innerHTML=a._iconfont_svg_string_4125778,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(l=document.body).firstChild?m(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=a.document,z=!1,s(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){z||(z=!0,i())}function s(){try{o.documentElement.doScroll("left")}catch(l){return void setTimeout(s,50)}p()}})(window); \ No newline at end of file diff --git a/web/src/assets/iconfont/iconfont.json b/web/src/assets/iconfont/iconfont.json index f114daa2..7dc3877f 100644 --- a/web/src/assets/iconfont/iconfont.json +++ b/web/src/assets/iconfont/iconfont.json @@ -5,6 +5,34 @@ "css_prefix_text": "icon-", "description": "", "glyphs": [ + { + "icon_id": "22174321", + "name": "抖音支付", + "font_class": "douyin", + "unicode": "e852", + "unicode_decimal": 59474 + }, + { + "icon_id": "1238433", + "name": "social-paypal", + "font_class": "paypal", + "unicode": "e60f", + "unicode_decimal": 58895 + }, + { + "icon_id": "1244217", + "name": "qq", + "font_class": "qq", + "unicode": "e69f", + "unicode_decimal": 59039 + }, + { + "icon_id": "18166714", + "name": "京东支付", + "font_class": "jd-pay", + "unicode": "e8dd", + "unicode_decimal": 59613 + }, { "icon_id": "41645421", "name": "luma-logo", diff --git a/web/src/assets/iconfont/iconfont.ttf b/web/src/assets/iconfont/iconfont.ttf index f00663eccf08e1b716563b5d864c8fdaa3285968..8b2283596f825847456513e0dc78c13b446c83fb 100644 GIT binary patch delta 3422 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 diff --git a/web/src/assets/iconfont/iconfont.woff b/web/src/assets/iconfont/iconfont.woff index ff63b66a4981c48ca2526ecbb3b8c06394358231..426fce0c2100df24c4d077b53b5391fa230d46af 100644 GIT binary patch delta 19950 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 diff --git a/web/src/assets/iconfont/iconfont.woff2 b/web/src/assets/iconfont/iconfont.woff2 index a6df0ee8c0360ad8b6a3fb72f289f09e646827b9..906588c2f5ac1d27de06f38d88671691e531f21e 100644 GIT binary patch literal 17612 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 literal 16812 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 fZMbq&mNBJcx=f{<&Oc)cqH7LZ)2hwvby>$9 - - - + + +

消费账单

@@ -132,7 +138,7 @@ - 请打开手机{{ payName }}扫码支付 + 请打开手机扫码支付 @@ -146,7 +152,6 @@ import {onMounted, ref} from "vue" import {ElMessage} from "element-plus"; import {httpGet, httpPost} from "@/utils/http"; -import ItemList from "@/components/ItemList.vue"; import {InfoFilled, SuccessFilled} from "@element-plus/icons-vue"; import {checkSession, getSystemInfo} from "@/store/cache"; import UserProfile from "@/components/UserProfile.vue"; @@ -176,19 +181,16 @@ const text = ref("") const user = ref(null) const isLogin = ref(false) const router = useRouter() -const curPayProduct = ref(null) const activeOrderNo = ref("") const countDownRef = ref(null) const orderTimeout = ref(1800) const loading = ref(true) const orderPayInfoText = ref("") -const vipMonthPower = ref(0) -const powerPrice = ref(0) -const payWays = ref({}) +const payWays = ref([]) const amount = ref(0) -const payName = ref("支付宝") -const curPay = ref("alipay") // 当前支付方式 +const curPayProduct = ref(null) +const curPayWay = ref({pay_way: "", pay_type:""}) const vipInfoText = ref("") const store = useSharedStore() const profileKey = ref(0) @@ -215,8 +217,6 @@ onMounted(() => { if (res.data['order_pay_timeout'] > 0) { orderTimeout.value = res.data['order_pay_timeout'] } - vipMonthPower.value = res.data['vip_month_power'] - powerPrice.value = res.data['power_price'] vipInfoText.value = res.data['vip_info_text'] }).catch(e => { ElMessage.error("获取系统配置失败:" + e.message) @@ -231,22 +231,24 @@ onMounted(() => { // refresh payment qrcode const refreshPayCode = () => { - if (curPay.value === 'alipay') { - alipay(curPayProduct.value) - } else if (curPay.value === 'hupi') { - huPiPay(curPayProduct.value) - } else if (curPay.value === 'payjs') { - PayJs(curPayProduct.value) - } + genPayQrcode(curPayProduct.value, curPayWay.value) } -const genPayQrcode = () => { +const genPayQrcode = (product, payWay) => { + if (!isLogin.value) { + store.setShowLoginDialog(true) + return + } + loading.value = true text.value = "" + curPayProduct.value = product + curPayWay.value = payWay + amount.value = (product.price - product.discount).toFixed(2) httpPost("/api/payment/qrcode", { - pay_way: curPay.value, + pay_way: payWay.pay_way, + pay_type: payWay.pay_type, product_id: curPayProduct.value.id, - user_id: user.value.id }).then(res => { showPayDialog.value = true qrcode.value = res.data['image'] @@ -262,67 +264,6 @@ const genPayQrcode = () => { }) } -const alipay = (row) => { - payName.value = "支付宝" - curPay.value = "alipay" - amount.value = (row.price - row.discount).toFixed(2) - if (!isLogin.value) { - store.setShowLoginDialog(true) - return - } - - if (row) { - curPayProduct.value = row - } - genPayQrcode() -} - -// 虎皮椒支付 -const huPiPay = (row) => { - payName.value = payWays.value["hupi"]["name"] === "wechat" ? '微信' : '支付宝' - curPay.value = "hupi" - amount.value = (row.price - row.discount).toFixed(2) - if (!isLogin.value) { - store.setShowLoginDialog(true) - return - } - - if (row) { - curPayProduct.value = row - } - genPayQrcode() -} - -// PayJS 支付 -const PayJs = (row) => { - payName.value = '微信' - curPay.value = "payjs" - amount.value = (row.price - row.discount).toFixed(2) - if (!isLogin.value) { - store.setShowLoginDialog(true) - return - } - - if (row) { - curPayProduct.value = row - } - genPayQrcode() -} - -const wechatPay = (row) => { - payName.value = '微信' - curPay.value = "wechat" - amount.value = (row.price - row.discount).toFixed(2) - if (!isLogin.value) { - store.setShowLoginDialog(true) - return - } - - if (row) { - curPayProduct.value = row - } - genPayQrcode() -} const queryOrder = (orderNo) => { httpGet("/api/order/query", {order_no: orderNo}).then(res => { @@ -331,11 +272,7 @@ const queryOrder = (orderNo) => { queryOrder(orderNo) } else if (res.data.status === 2) { text.value = "支付成功,正在刷新页面" - if (curPay.value === "payjs") { - setTimeout(() => location.reload(), 3000) - } else { - setTimeout(() => location.reload(), 500) - } + setTimeout(() => location.reload(), 500) } else { // 如果当前订单没有过期,继续等待订单的下一个状态 if (activeOrderNo.value === orderNo) { From d8cb92d8d4e1eac5d4c3adf64ba7dced24b93227 Mon Sep 17 00:00:00 2001 From: RockYang Date: Wed, 18 Sep 2024 18:07:49 +0800 Subject: [PATCH 14/25] Geek Pay notify is ready --- api/core/app_server.go | 5 +- api/core/types/config.go | 22 +- api/handler/payment_handler.go | 416 +++++++------------------ api/main.go | 11 +- api/service/payment/alipay_service.go | 34 +- api/service/payment/geekpay_service.go | 92 +++--- api/service/payment/wepay_service.go | 33 +- web/src/assets/css/member.styl | 21 ++ web/src/assets/iconfont/iconfont.css | 14 +- web/src/assets/iconfont/iconfont.js | 2 +- web/src/assets/iconfont/iconfont.json | 20 +- web/src/assets/iconfont/iconfont.ttf | Bin 30468 -> 30892 bytes web/src/assets/iconfont/iconfont.woff | Bin 20304 -> 20536 bytes web/src/assets/iconfont/iconfont.woff2 | Bin 17612 -> 17888 bytes web/src/components/UserOrder.vue | 2 +- web/src/views/Member.vue | 142 ++------- 16 files changed, 270 insertions(+), 544 deletions(-) diff --git a/api/core/app_server.go b/api/core/app_server.go index 9990ddba..efa63091 100644 --- a/api/core/app_server.go +++ b/api/core/app_server.go @@ -219,10 +219,6 @@ func needLogin(c *gin.Context) bool { c.Request.URL.Path == "/api/product/list" || c.Request.URL.Path == "/api/menu/list" || c.Request.URL.Path == "/api/markMap/client" || - c.Request.URL.Path == "/api/payment/alipay/notify" || - c.Request.URL.Path == "/api/payment/hupipay/notify" || - c.Request.URL.Path == "/api/payment/payjs/notify" || - c.Request.URL.Path == "/api/payment/wechat/notify" || c.Request.URL.Path == "/api/payment/doPay" || c.Request.URL.Path == "/api/payment/payWays" || c.Request.URL.Path == "/api/suno/client" || @@ -231,6 +227,7 @@ func needLogin(c *gin.Context) bool { c.Request.URL.Path == "/api/download" || c.Request.URL.Path == "/api/video/client" || strings.HasPrefix(c.Request.URL.Path, "/api/test") || + strings.HasPrefix(c.Request.URL.Path, "/api/payment/notify/") || strings.HasPrefix(c.Request.URL.Path, "/api/user/clogin") || strings.HasPrefix(c.Request.URL.Path, "/api/config/") || strings.HasPrefix(c.Request.URL.Path, "/api/function/") || diff --git a/api/core/types/config.go b/api/core/types/config.go index f6e59727..dda50c00 100644 --- a/api/core/types/config.go +++ b/api/core/types/config.go @@ -57,8 +57,7 @@ type AlipayConfig struct { PublicKey string // 用户公钥文件路径 AlipayPublicKey string // 支付宝公钥文件路径 RootCert string // Root 秘钥路径 - NotifyURL string // 异步通知回调 - ReturnURL string // 支付成功返回地址 + NotifyHost string // 通知回调地址 } type WechatPayConfig struct { @@ -68,18 +67,16 @@ type WechatPayConfig struct { SerialNo string // 商户证书的证书序列号 PrivateKey string // 用户私钥文件路径 ApiV3Key string // API V3 秘钥 - NotifyURL string // 异步通知回调 - ReturnURL string // 支付成功返回地址 + NotifyHost string // 通知回调地址 } type HuPiPayConfig struct { //虎皮椒第四方支付配置 - Enabled bool // 是否启用该支付通道 - Name string // 支付名称,如:wechat/alipay - AppId string // App ID - AppSecret string // app 密钥 - ApiURL string // 支付网关 - NotifyURL string // 异步通知回调 - ReturnURL string // 支付成功返回地址 + Enabled bool // 是否启用该支付通道 + Name string // 支付名称,如:wechat/alipay + AppId string // App ID + AppSecret string // app 密钥 + ApiURL string // 支付网关 + NotifyHost string // 通知回调地址 } // GeekPayConfig GEEK支付配置 @@ -88,8 +85,7 @@ type GeekPayConfig struct { AppId string // 商户 ID PrivateKey string // 私钥 ApiURL string // API 网关 - NotifyURL string // 异步回调地址 - ReturnURL string // 支付成功返回地址 + NotifyHost string // 通知回调地址 } type XXLConfig struct { // XXL 任务调度配置 diff --git a/api/handler/payment_handler.go b/api/handler/payment_handler.go index 8a0112a9..0fba56b4 100644 --- a/api/handler/payment_handler.go +++ b/api/handler/payment_handler.go @@ -9,7 +9,6 @@ package handler import ( "embed" - "encoding/base64" "fmt" "geekai/core" "geekai/core/types" @@ -20,7 +19,6 @@ import ( "geekai/utils/resp" "github.com/shopspring/decimal" "net/http" - "net/url" "sync" "time" @@ -71,62 +69,94 @@ func NewPaymentHandler( } } -func (h *PaymentHandler) DoPay(c *gin.Context) { - orderNo := h.GetTrim(c, "order_no") - t := h.GetInt(c, "t", 0) - sign := h.GetTrim(c, "sign") - signStr := fmt.Sprintf("%s-%d-%s", orderNo, t, h.signKey) - newSign := utils.Sha256(signStr) - if newSign != sign { - resp.ERROR(c, "订单签名错误!") +func (h *PaymentHandler) Pay(c *gin.Context) { + payWay := c.Query("pay_way") + payType := c.Query("pay_type") + productId := c.Query("pid") + device := c.Query("device") + userId := c.Query("user_id") + + var product model.Product + err := h.DB.First(&product, productId).Error + if err != nil { + resp.ERROR(c, "Product not found") return } - // 检查二维码是否过期 - if time.Now().Unix()-int64(t) > int64(h.App.SysConfig.OrderPayTimeout) { - resp.ERROR(c, "支付二维码已过期,请重新生成!") + orderNo, err := h.snowflake.Next(false) + if err != nil { + resp.ERROR(c, "error with generate trade no: "+err.Error()) + return + } + var user model.User + err = h.DB.Where("id", userId).First(&user).Error + if err != nil { + resp.NotAuth(c) + return + } + // 创建订单 + remark := types.OrderRemark{ + Days: product.Days, + Power: product.Power, + Name: product.Name, + Price: product.Price, + Discount: product.Discount, + } + + amount, _ := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Float64() + + order := model.Order{ + UserId: user.Id, + Username: user.Username, + ProductId: product.Id, + OrderNo: orderNo, + Subject: product.Name, + Amount: amount, + Status: types.OrderNotPaid, + PayWay: payWay, + PayType: payType, + Remark: utils.JsonEncode(remark), + } + err = h.DB.Create(&order).Error + if err != nil { + resp.ERROR(c, "error with create order: "+err.Error()) return } - if orderNo == "" { - resp.ERROR(c, types.InvalidArgs) - return - } - - var order model.Order - res := h.DB.Where("order_no = ?", orderNo).First(&order) - if res.Error != nil { - resp.ERROR(c, "Order not found") - return - } - - // fix: 这里先检查一下订单状态,如果已经支付了,就直接返回 - if order.Status == types.OrderPaidSuccess { - resp.ERROR(c, "订单已支付成功,无需重复支付!") - return - } - - // 更新扫码状态 - h.DB.Model(&order).UpdateColumn("status", types.OrderScanned) - - if order.PayWay == "alipay" { // 支付宝 - amount := fmt.Sprintf("%.2f", order.Amount) - uri, err := h.alipayService.PayUrlMobile(order.OrderNo, amount, order.Subject) + var payURL string + if payWay == "alipay" { // 支付宝 + money := fmt.Sprintf("%.2f", order.Amount) + notifyURL := fmt.Sprintf("%s/api/payment/notify/alipay", h.App.Config.AlipayConfig.NotifyHost) + returnURL := fmt.Sprintf("%s/member", h.App.Config.AlipayConfig.NotifyHost) + if device == "mobile" { + payURL, err = h.alipayService.PayMobile(payment.AlipayParams{ + OutTradeNo: orderNo, + Subject: product.Name, + TotalFee: money, + ReturnURL: returnURL, + NotifyURL: notifyURL, + }) + } else { + payURL, err = h.alipayService.PayPC(payment.AlipayParams{ + OutTradeNo: orderNo, + Subject: product.Name, + TotalFee: money, + ReturnURL: returnURL, + NotifyURL: notifyURL, + }) + } if err != nil { resp.ERROR(c, "error with generate pay url: "+err.Error()) return } - - c.Redirect(302, uri) - return } else if order.PayWay == "hupi" { // 虎皮椒支付 params := payment.HuPiPayReq{ Version: "1.1", TradeOrderId: orderNo, TotalFee: fmt.Sprintf("%f", order.Amount), Title: order.Subject, - NotifyURL: h.App.Config.HuPiPayConfig.NotifyURL, - WapName: "极客学长", + NotifyURL: fmt.Sprintf("%s/api/payment/notify/hupi", h.App.Config.HuPiPayConfig.NotifyHost), + WapName: "GeekAI助手", } r, err := h.huPiPayService.Pay(params) if err != nil { @@ -136,7 +166,8 @@ func (h *PaymentHandler) DoPay(c *gin.Context) { c.Redirect(302, r.URL) } else if order.PayWay == "wechat" { - uri, err := h.wechatPayService.PayUrlNative(order.OrderNo, int(order.Amount*100), order.Subject) + //uri, err := h.wechatPayService.PayUrlNative(order.OrderNo, int(order.Amount*100), order.Subject) + uri, err := h.wechatPayService.PayUrlNative(payment.WechatPayParams{}) if err != nil { resp.ERROR(c, err.Error()) return @@ -144,261 +175,28 @@ func (h *PaymentHandler) DoPay(c *gin.Context) { c.Redirect(302, uri) } else if order.PayWay == "geek" { + notifyURL := fmt.Sprintf("%s/api/payment/notify/geek", h.App.Config.GeekPayConfig.NotifyHost) + returnURL := fmt.Sprintf("%s/member", h.App.Config.GeekPayConfig.NotifyHost) params := payment.GeekPayParams{ OutTradeNo: orderNo, Method: "web", Name: order.Subject, Money: fmt.Sprintf("%f", order.Amount), ClientIP: c.ClientIP(), - Device: "pc", - Type: "alipay", + Device: device, + Type: payType, + ReturnURL: returnURL, + NotifyURL: notifyURL, } - s, err := h.geekPayService.Pay(params) + res, err := h.geekPayService.Pay(params) if err != nil { resp.ERROR(c, err.Error()) return } - resp.SUCCESS(c, s) + payURL = res.PayURL } - //resp.ERROR(c, "Invalid operations") -} - -// PayQrcode 生成支付 URL 二维码 -func (h *PaymentHandler) PayQrcode(c *gin.Context) { - var data struct { - PayWay string `json:"pay_way"` // 支付方式 - PayType string `json:"pay_type"` // 支付类别:wechat,alipay,qq... - ProductId uint `json:"product_id"` // 支付产品ID - } - if err := c.ShouldBindJSON(&data); err != nil { - resp.ERROR(c, types.InvalidArgs) - return - } - - var product model.Product - res := h.DB.First(&product, data.ProductId) - if res.Error != nil { - resp.ERROR(c, "Product not found") - return - } - - orderNo, err := h.snowflake.Next(false) - if err != nil { - resp.ERROR(c, "error with generate trade no: "+err.Error()) - return - } - user, err := h.GetLoginUser(c) - if err != nil { - resp.NotAuth(c) - return - } - - var notifyURL string - switch data.PayWay { - case "hupi": - notifyURL = h.App.Config.HuPiPayConfig.NotifyURL - break - case "geek": - notifyURL = h.App.Config.GeekPayConfig.NotifyURL - break - case "alipay": // 支付宝商户支付 - notifyURL = h.App.Config.AlipayConfig.NotifyURL - break - case "wechat": // 微信商户支付 - notifyURL = h.App.Config.WechatPayConfig.NotifyURL - default: - resp.ERROR(c, "Invalid pay way") - return - - } - // 创建订单 - remark := types.OrderRemark{ - Days: product.Days, - Power: product.Power, - Name: product.Name, - Price: product.Price, - Discount: product.Discount, - } - - amount, _ := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Float64() - order := model.Order{ - UserId: user.Id, - Username: user.Username, - ProductId: product.Id, - OrderNo: orderNo, - Subject: product.Name, - Amount: amount, - Status: types.OrderNotPaid, - PayWay: data.PayWay, - PayType: data.PayType, - Remark: utils.JsonEncode(remark), - } - res = h.DB.Create(&order) - if res.Error != nil || res.RowsAffected == 0 { - resp.ERROR(c, "error with create order: "+res.Error.Error()) - return - } - - var logo string - switch data.PayType { - case "alipay": - logo = "res/img/alipay.jpg" - break - case "wechat": - logo = "res/img/wechat-pay.jpg" - break - case "qq": - logo = "res/img/qq-pay.jpg" - break - default: - logo = "res/img/geek-pay.jpg" - - } - if data.PayType == "alipay" { - logo = "res/img/alipay.jpg" - } else if data.PayType == "wechat" { - logo = "res/img/wechat-pay.jpg" - } - file, err := h.fs.Open(logo) - if err != nil { - resp.ERROR(c, "error with open qrcode log file: "+err.Error()) - return - } - - parse, err := url.Parse(notifyURL) - if err != nil { - resp.ERROR(c, err.Error()) - return - } - timestamp := time.Now().Unix() - signStr := fmt.Sprintf("%s-%s-%d-%s", orderNo, data.PayWay, timestamp, h.signKey) - sign := utils.Sha256(signStr) - payUrl := fmt.Sprintf("%s://%s/api/payment/doPay?order_no=%s&pay_way=%s&pay_type=%s&t=%d&sign=%s", parse.Scheme, parse.Host, orderNo, data.PayWay, data.PayType, timestamp, sign) - imgData, err := utils.GenQrcode(payUrl, 400, file) - if err != nil { - resp.ERROR(c, err.Error()) - return - } - imgDataBase64 := base64.StdEncoding.EncodeToString(imgData) - resp.SUCCESS(c, gin.H{"order_no": orderNo, "image": fmt.Sprintf("data:image/jpg;base64, %s", imgDataBase64), "url": payUrl}) -} - -// Mobile 移动端支付 -func (h *PaymentHandler) Mobile(c *gin.Context) { - var data struct { - PayWay string `json:"pay_way"` // 支付方式 - PayType string `json:"pay_type"` // 支付类别:wechat,alipay,qq... - ProductId uint `json:"product_id"` - } - if err := c.ShouldBindJSON(&data); err != nil { - resp.ERROR(c, types.InvalidArgs) - return - } - - var product model.Product - res := h.DB.First(&product, data.ProductId) - if res.Error != nil { - resp.ERROR(c, "Product not found") - return - } - - orderNo, err := h.snowflake.Next(false) - if err != nil { - resp.ERROR(c, "error with generate trade no: "+err.Error()) - return - } - user, err := h.GetLoginUser(c) - if err != nil { - resp.NotAuth(c) - return - } - - amount, _ := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Float64() - var notifyURL, returnURL string - var payURL string - switch data.PayWay { - case "hupi": - notifyURL = h.App.Config.HuPiPayConfig.NotifyURL - returnURL = h.App.Config.HuPiPayConfig.ReturnURL - parse, _ := url.Parse(h.App.Config.HuPiPayConfig.ReturnURL) - baseURL := fmt.Sprintf("%s://%s", parse.Scheme, parse.Host) - params := payment.HuPiPayReq{ - Version: "1.1", - TradeOrderId: orderNo, - TotalFee: fmt.Sprintf("%f", amount), - Title: product.Name, - NotifyURL: notifyURL, - ReturnURL: returnURL, - CallbackURL: returnURL, - WapName: "极客学长", - WapUrl: baseURL, - Type: "WAP", - } - r, err := h.huPiPayService.Pay(params) - if err != nil { - errMsg := "error with generating Pay Hupi URL: " + err.Error() - logger.Error(errMsg) - resp.ERROR(c, errMsg) - return - } - payURL = r.URL - case "geek": - //totalFee := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Mul(decimal.NewFromInt(100)).IntPart() - //params := url.Values{} - //params.Add("total_fee", fmt.Sprintf("%d", totalFee)) - //params.Add("out_trade_no", orderNo) - //params.Add("body", product.Name) - //params.Add("notify_url", notifyURL) - //params.Add("auto", "0") - //payURL = h.geekPayService.Pay(params) - case "alipay": - payURL, err = h.alipayService.PayUrlMobile(orderNo, fmt.Sprintf("%.2f", amount), product.Name) - if err != nil { - errMsg := "error with generating Alipay URL: " + err.Error() - resp.ERROR(c, errMsg) - return - } - case "wechat": - payURL, err = h.wechatPayService.PayUrlH5(orderNo, int(amount*100), product.Name, c.ClientIP()) - if err != nil { - errMsg := "error with generating Wechat URL: " + err.Error() - logger.Error(errMsg) - resp.ERROR(c, errMsg) - return - } - default: - resp.ERROR(c, "Unsupported pay way: "+data.PayWay) - return - } - // 创建订单 - remark := types.OrderRemark{ - Days: product.Days, - Power: product.Power, - Name: product.Name, - Price: product.Price, - Discount: product.Discount, - } - - order := model.Order{ - UserId: user.Id, - Username: user.Username, - ProductId: product.Id, - OrderNo: orderNo, - Subject: product.Name, - Amount: amount, - Status: types.OrderNotPaid, - PayWay: data.PayWay, - PayType: data.PayType, - Remark: utils.JsonEncode(remark), - } - res = h.DB.Create(&order) - if res.Error != nil || res.RowsAffected == 0 { - resp.ERROR(c, "error with create order: "+res.Error.Error()) - return - } - - resp.SUCCESS(c, gin.H{"url": payURL, "order_no": orderNo}) + resp.SUCCESS(c, payURL) } // 异步通知回调公共逻辑 @@ -505,14 +303,14 @@ func (h *PaymentHandler) GetPayWays(c *gin.Context) { } if h.App.Config.GeekPayConfig.Enabled { payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "alipay"}) - payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "wechat"}) - payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "qq"}) - payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "jd"}) + payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "wxpay"}) + payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "qqpay"}) + payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "jdpay"}) payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "douyin"}) payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": "paypal"}) } if h.App.Config.WechatPayConfig.Enabled { - payWays = append(payWays, gin.H{"pay_way": "wechat", "pay_type": "wechat"}) + payWays = append(payWays, gin.H{"pay_way": "wechat", "pay_type": "wxpay"}) } resp.SUCCESS(c, payWays) } @@ -570,32 +368,28 @@ func (h *PaymentHandler) AlipayNotify(c *gin.Context) { c.String(http.StatusOK, "success") } -// PayJsNotify PayJs 支付异步回调 -func (h *PaymentHandler) PayJsNotify(c *gin.Context) { - err := c.Request.ParseForm() - if err != nil { +// GeekPayNotify 支付异步回调 +func (h *PaymentHandler) GeekPayNotify(c *gin.Context) { + var params = make(map[string]string) + for k := range c.Request.URL.Query() { + params[k] = c.Query(k) + } + + logger.Infof("收到GeekPay订单支付回调:%+v", params) + // 检查支付状态 + if params["trade_status"] != "TRADE_SUCCESS" { + c.String(http.StatusOK, "success") + return + } + + sign := h.geekPayService.Sign(params) + if sign != c.Query("sign") { + logger.Errorf("签名验证失败, %s, %s", sign, c.Query("sign")) c.String(http.StatusOK, "fail") return } - orderNo := c.Request.Form.Get("out_trade_no") - returnCode := c.Request.Form.Get("return_code") - logger.Infof("收到PayJs订单支付回调,订单 NO:%s,支付结果代码:%v", orderNo, returnCode) - // 支付失败 - if returnCode != "1" { - return - } - - // 校验订单支付状态 - tradeNo := c.Request.Form.Get("payjs_order_id") - //err = h.geekPayService.TradeVerify(tradeNo) - //if err != nil { - // logger.Error("订单校验失败:", err) - // c.String(http.StatusOK, "fail") - // return - //} - - err = h.notify(orderNo, tradeNo) + err := h.notify(params["out_trade_no"], params["trade_no"]) if err != nil { c.String(http.StatusOK, "fail") return diff --git a/api/main.go b/api/main.go index 544abc1e..6d00d3fd 100644 --- a/api/main.go +++ b/api/main.go @@ -372,14 +372,11 @@ func main() { }), fx.Invoke(func(s *core.AppServer, h *handler.PaymentHandler) { group := s.Engine.Group("/api/payment/") - group.GET("doPay", h.DoPay) + group.GET("doPay", h.Pay) group.GET("payWays", h.GetPayWays) - group.POST("qrcode", h.PayQrcode) - group.POST("mobile", h.Mobile) - group.POST("alipay/notify", h.AlipayNotify) - group.POST("hupipay/notify", h.HuPiPayNotify) - group.POST("payjs/notify", h.PayJsNotify) - group.POST("wechat/notify", h.WechatPayNotify) + group.POST("notify/alipay", h.AlipayNotify) + group.GET("notify/geek", h.GeekPayNotify) + group.POST("notify/wechat", h.WechatPayNotify) }), fx.Invoke(func(s *core.AppServer, h *admin.ProductHandler) { group := s.Engine.Group("/api/admin/product/") diff --git a/api/service/payment/alipay_service.go b/api/service/payment/alipay_service.go index 8ab93eda..d6c8a139 100644 --- a/api/service/payment/alipay_service.go +++ b/api/service/payment/alipay_service.go @@ -44,9 +44,7 @@ func NewAlipayService(appConfig *types.AppConfig) (*AlipayService, error) { //client.DebugSwitch = gopay.DebugOn // 开启调试模式 client.SetLocation(alipay.LocationShanghai). // 设置时区,不设置或出错均为默认服务器时间 SetCharset(alipay.UTF8). // 设置字符编码,不设置默认 utf-8 - SetSignType(alipay.RSA2). // 设置签名类型,不设置默认 RSA2 - SetReturnUrl(config.ReturnURL). // 设置返回URL - SetNotifyUrl(config.NotifyURL) + SetSignType(alipay.RSA2) // 设置签名类型,不设置默认 RSA2 if err = client.SetCertSnByPath(config.PublicKey, config.RootCert, config.AlipayPublicKey); err != nil { return nil, fmt.Errorf("error with load payment public key: %v", err) @@ -55,23 +53,33 @@ func NewAlipayService(appConfig *types.AppConfig) (*AlipayService, error) { return &AlipayService{config: &config, client: client}, nil } -func (s *AlipayService) PayUrlMobile(outTradeNo string, amount string, subject string) (string, error) { +type AlipayParams struct { + OutTradeNo string `json:"out_trade_no"` + Subject string `json:"subject"` + TotalFee string `json:"total_fee"` + ReturnURL string `json:"return_url"` + NotifyURL string `json:"notify_url"` +} + +func (s *AlipayService) PayMobile(params AlipayParams) (string, error) { bm := make(gopay.BodyMap) - bm.Set("subject", subject) - bm.Set("out_trade_no", outTradeNo) - bm.Set("quit_url", s.config.ReturnURL) - bm.Set("total_amount", amount) + bm.Set("subject", params.Subject) + bm.Set("out_trade_no", params.OutTradeNo) + bm.Set("quit_url", params.ReturnURL) + bm.Set("total_amount", params.TotalFee) + bm.Set("return_url", params.ReturnURL) + bm.Set("notify_url", params.NotifyURL) bm.Set("product_code", "QUICK_WAP_WAY") return s.client.TradeWapPay(context.Background(), bm) } -func (s *AlipayService) PayUrlPc(outTradeNo string, amount string, subject string) (string, error) { +func (s *AlipayService) PayPC(params AlipayParams) (string, error) { bm := make(gopay.BodyMap) - bm.Set("subject", subject) - bm.Set("out_trade_no", outTradeNo) - bm.Set("total_amount", amount) + bm.Set("subject", params.Subject) + bm.Set("out_trade_no", params.OutTradeNo) + bm.Set("total_amount", params.TotalFee) bm.Set("product_code", "FAST_INSTANT_TRADE_PAY") - return s.client.TradePagePay(context.Background(), bm) + return s.client.SetNotifyUrl(params.NotifyURL).SetReturnUrl(params.ReturnURL).TradePagePay(context.Background(), bm) } // TradeVerify 交易验证 diff --git a/api/service/payment/geekpay_service.go b/api/service/payment/geekpay_service.go index 2018f43f..7348c0d8 100644 --- a/api/service/payment/geekpay_service.go +++ b/api/service/payment/geekpay_service.go @@ -8,15 +8,11 @@ package payment // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( - "crypto" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "crypto/x509" - "encoding/base64" - "encoding/pem" + "encoding/json" + "errors" "fmt" "geekai/core/types" + "geekai/utils" "io" "net/http" "net/url" @@ -46,41 +42,37 @@ type GeekPayParams struct { ClientIP string `json:"clientip"` //用户IP地址 SubOpenId string `json:"sub_openid"` // 微信用户 openid,仅小程序支付需要 SubAppId string `json:"sub_appid"` // 小程序 AppId,仅小程序支付需要 + NotifyURL string `json:"notify_url"` + ReturnURL string `json:"return_url"` } // Pay 支付订单 -func (s *GeekPayService) Pay(params GeekPayParams) (string, error) { - if params.Type == "wechat" { - params.Type = "wxpay" - } +func (s *GeekPayService) Pay(params GeekPayParams) (*GeekPayResp, error) { p := map[string]string{ - "pid": s.config.AppId, - "method": params.Method, + "pid": s.config.AppId, + //"method": params.Method, "device": params.Device, "type": params.Type, "out_trade_no": params.OutTradeNo, "name": params.Name, "money": params.Money, "clientip": params.ClientIP, - "sub_openid": params.SubOpenId, - "sub_appid": params.SubAppId, - "notify_url": s.config.NotifyURL, - "return_url": s.config.ReturnURL, + "notify_url": params.NotifyURL, + "return_url": params.ReturnURL, "timestamp": fmt.Sprintf("%d", time.Now().Unix()), } - sign, err := s.Sign(p) - if err != nil { - return "", err - } - p["sign"] = sign - p["sign_type"] = "RSA" + p["sign"] = s.Sign(p) + p["sign_type"] = "MD5" return s.sendRequest(s.config.ApiURL, p) } -func (s *GeekPayService) Sign(params map[string]string) (string, error) { +func (s *GeekPayService) Sign(params map[string]string) string { // 按字母顺序排序参数 var keys []string for k := range params { + if params[k] == "" || k == "sign" || k == "sign_type" { + continue + } keys = append(keys, k) } sort.Strings(keys) @@ -93,44 +85,48 @@ func (s *GeekPayService) Sign(params map[string]string) (string, error) { signStr.WriteString(params[k]) signStr.WriteString("&") } - signString := strings.TrimSuffix(signStr.String(), "&") + signString := strings.TrimSuffix(signStr.String(), "&") + s.config.PrivateKey - // 使用RSA私钥签名 - block, _ := pem.Decode([]byte(s.config.PrivateKey)) - if block == nil { - return "", fmt.Errorf("failed to decode private key") - } - - priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - return "", fmt.Errorf("failed to parse private key: %v", err) - } - - hashed := sha256.Sum256([]byte(signString)) - signature, err := rsa.SignPKCS1v15(rand.Reader, priKey, crypto.SHA256, hashed[:]) - if err != nil { - panic(fmt.Sprintf("failed to sign: %v", err)) - } - - return base64.StdEncoding.EncodeToString(signature), nil + return utils.Md5(signString) } -func (s *GeekPayService) sendRequest(apiEndpoint string, params map[string]string) (string, error) { +type GeekPayResp struct { + Code int `json:"code"` + Msg string `json:"msg"` + TradeNo string `json:"trade_no"` + PayURL string `json:"payurl"` + QrCode string `json:"qrcode"` + UrlScheme string `json:"urlscheme"` // 小程序跳转支付链接 +} + +func (s *GeekPayService) sendRequest(endpoint string, params map[string]string) (*GeekPayResp, error) { form := url.Values{} for k, v := range params { form.Add(k, v) } - resp, err := http.PostForm(apiEndpoint, form) + apiURL := fmt.Sprintf("%s/mapi.php", endpoint) + logger.Infof(apiURL) + + resp, err := http.PostForm(apiURL, form) if err != nil { - return "", err + return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) + logger.Debugf(string(body)) if err != nil { - return "", err + return nil, err } - return string(body), nil + var r GeekPayResp + err = json.Unmarshal(body, &r) + if err != nil { + return nil, errors.New("当前支付渠道暂不支持") + } + if r.Code != 1 { + return nil, errors.New(r.Msg) + } + return &r, nil } diff --git a/api/service/payment/wepay_service.go b/api/service/payment/wepay_service.go index b141a8e9..11554e9f 100644 --- a/api/service/payment/wepay_service.go +++ b/api/service/payment/wepay_service.go @@ -46,18 +46,28 @@ func NewWechatService(appConfig *types.AppConfig) (*WechatPayService, error) { return &WechatPayService{config: &config, client: client}, nil } -func (s *WechatPayService) PayUrlNative(outTradeNo string, amount int, subject string) (string, error) { +type WechatPayParams struct { + OutTradeNo string `json:"out_trade_no"` + TotalFee int `json:"total_fee"` + Subject string `json:"subject"` + ClientIP string `json:"client_ip"` + ReturnURL string `json:"return_url"` + NotifyURL string `json:"notify_url"` +} + +func (s *WechatPayService) PayUrlNative(params WechatPayParams) (string, error) { expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) // 初始化 BodyMap bm := make(gopay.BodyMap) bm.Set("appid", s.config.AppId). Set("mchid", s.config.MchId). - Set("description", subject). - Set("out_trade_no", outTradeNo). + Set("description", params.Subject). + Set("out_trade_no", params.OutTradeNo). Set("time_expire", expire). - Set("notify_url", s.config.NotifyURL). + Set("notify_url", params.NotifyURL). + Set("return_url", params.ReturnURL). SetBodyMap("amount", func(bm gopay.BodyMap) { - bm.Set("total", amount). + bm.Set("total", params.TotalFee). Set("currency", "CNY") }) @@ -71,22 +81,23 @@ func (s *WechatPayService) PayUrlNative(outTradeNo string, amount int, subject s return wxRsp.Response.CodeUrl, nil } -func (s *WechatPayService) PayUrlH5(outTradeNo string, amount int, subject string, ip string) (string, error) { +func (s *WechatPayService) PayUrlH5(params WechatPayParams) (string, error) { expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) // 初始化 BodyMap bm := make(gopay.BodyMap) bm.Set("appid", s.config.AppId). Set("mchid", s.config.MchId). - Set("description", subject). - Set("out_trade_no", outTradeNo). + Set("description", params.Subject). + Set("out_trade_no", params.OutTradeNo). Set("time_expire", expire). - Set("notify_url", s.config.NotifyURL). + Set("notify_url", params.NotifyURL). + Set("return_url", params.ReturnURL). SetBodyMap("amount", func(bm gopay.BodyMap) { - bm.Set("total", amount). + bm.Set("total", params.TotalFee). Set("currency", "CNY") }). SetBodyMap("scene_info", func(bm gopay.BodyMap) { - bm.Set("payer_client_ip", ip). + bm.Set("payer_client_ip", params.ClientIP). SetBodyMap("h5_info", func(bm gopay.BodyMap) { bm.Set("type", "Wap") }) diff --git a/web/src/assets/css/member.styl b/web/src/assets/css/member.styl index 9dab8325..4dc2209f 100644 --- a/web/src/assets/css/member.styl +++ b/web/src/assets/css/member.styl @@ -181,6 +181,27 @@ .el-button { margin 10px 5px 0 5px + padding 0 + + .icon-alipay,.icon-wechat-pay { + color #ffffff + } + .icon-qq { + color #15A6E8 + font-size 24px + } + .icon-jd-pay { + color #ffffff + font-size 24px + } + .icon-douyin { + color #0a0a0a + font-size 22px + } + .icon-paypal { + font-size 14px + color #009CDE + } } } } diff --git a/web/src/assets/iconfont/iconfont.css b/web/src/assets/iconfont/iconfont.css index a0959ced..e0dee322 100644 --- a/web/src/assets/iconfont/iconfont.css +++ b/web/src/assets/iconfont/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 4125778 */ - src: url('iconfont.woff2?t=1726612860394') format('woff2'), - url('iconfont.woff?t=1726612860394') format('woff'), - url('iconfont.ttf?t=1726612860394') format('truetype'); + src: url('iconfont.woff2?t=1726622198991') format('woff2'), + url('iconfont.woff?t=1726622198991') format('woff'), + url('iconfont.ttf?t=1726622198991') format('truetype'); } .iconfont { @@ -13,12 +13,12 @@ -moz-osx-font-smoothing: grayscale; } -.icon-douyin:before { - content: "\e852"; +.icon-paypal:before { + content: "\e666"; } -.icon-paypal:before { - content: "\e60f"; +.icon-douyin:before { + content: "\e8db"; } .icon-qq:before { diff --git a/web/src/assets/iconfont/iconfont.js b/web/src/assets/iconfont/iconfont.js index 32973473..74291ab4 100644 --- a/web/src/assets/iconfont/iconfont.js +++ b/web/src/assets/iconfont/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_4125778='',(a=>{var l=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,z,m=function(l,c){c.parentNode.insertBefore(l,c)};if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}h=function(){var l,c=document.createElement("div");c.innerHTML=a._iconfont_svg_string_4125778,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(l=document.body).firstChild?m(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=a.document,z=!1,s(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){z||(z=!0,i())}function s(){try{o.documentElement.doScroll("left")}catch(l){return void setTimeout(s,50)}p()}})(window); \ No newline at end of file +window._iconfont_svg_string_4125778='',(a=>{var l=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,z,m=function(l,c){c.parentNode.insertBefore(l,c)};if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}h=function(){var l,c=document.createElement("div");c.innerHTML=a._iconfont_svg_string_4125778,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(l=document.body).firstChild?m(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=a.document,z=!1,s(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){z||(z=!0,i())}function s(){try{o.documentElement.doScroll("left")}catch(l){return void setTimeout(s,50)}p()}})(window); \ No newline at end of file diff --git a/web/src/assets/iconfont/iconfont.json b/web/src/assets/iconfont/iconfont.json index 7dc3877f..a38cbab5 100644 --- a/web/src/assets/iconfont/iconfont.json +++ b/web/src/assets/iconfont/iconfont.json @@ -6,18 +6,18 @@ "description": "", "glyphs": [ { - "icon_id": "22174321", - "name": "抖音支付", - "font_class": "douyin", - "unicode": "e852", - "unicode_decimal": 59474 + "icon_id": "7443846", + "name": "PayPal", + "font_class": "paypal", + "unicode": "e666", + "unicode_decimal": 58982 }, { - "icon_id": "1238433", - "name": "social-paypal", - "font_class": "paypal", - "unicode": "e60f", - "unicode_decimal": 58895 + "icon_id": "18166694", + "name": "抖音", + "font_class": "douyin", + "unicode": "e8db", + "unicode_decimal": 59611 }, { "icon_id": "1244217", diff --git a/web/src/assets/iconfont/iconfont.ttf b/web/src/assets/iconfont/iconfont.ttf index 8b2283596f825847456513e0dc78c13b446c83fb..e5b3d7deac1d95400b67e65ab91f69700136c6c8 100644 GIT binary patch delta 1442 zcma)*e@s(X6vxkfzwYaMudgj_U+JT?w%8xg0=7W=hX$%fL?CV&lf}(t;E%!tq1h@9 z|DbGh(?sGVFS?izXLQMMV@^W0EE$Pre=KZsL^u9$sM(@~RD&inaX2&W?nB77#3g$- z=biiR{haf?C-=Mq_t401=xE8VM*wI509tnJX-yCI9=QTQX8`zgTW4?T$^$hGAdLW| zZ0);RcUFZT-XZL-#NzE_!E(v8pUlUJEpOkGdE@<4`O75V9I->4-8))G`%WAO5N8Gt zxkImA1fbf|-LGeMfAbSj+#4cFZvrCk{(L`dGo8-=o_}DRADmQu6DHOBifPpY5E+sh z;Ff8+XUP+4pK)Fj&^xG9JBnH;)9JDM=oku4>A8r;B+v`92=M~RJ_QVQnrvhs2`p4= z=jb3xX;XB<(~@h;b>t4_zRrE0yOz6=o4bwjd4O4%6=tPbvsT7<2-(0SjQ=9F0j5_Z zC6bH>^r6XYiB`$BXoGAcLfSOjH#mR-1|;wSCL!FJ)g01l@rkipcpMp0 zM2r}qu_40+q_v3+hHC&hVnAslVs#cZq5-O!-b|cd=Edt&lyxhm*J%0j_|xNB{qwP#3|`rqh#zdnN9c2TP&9fhO9Sjg?5|0);_LO zC>I?Y9bXi77LGer=a*_k{jexjH10~ejuiWfFS~v2lOD>Gt@m8_p7K8Qoi901a&>v1 zKjyy|RD=DYq0n3@SDGqQ%cjD<@JNJ-oQuqsuPUFe*jBlsa(Km&=>F(r^k%HDirj#H z3;7$yd0GW0l*4*>3Ho3FK7$MJGu$TCm-sk4ZBtgFSiB}t9oKy#9tc*TEavnPMo(8A ziaz}Ri9d^Gbs7f)B|aHpPI0(VRV)!tMDe1YwK{-ha)MwWQA@TK(=P@P{r?g1G{%I; zfy2nhIjkgVp#~HW_?PM;I{K!--WQea67Ca>E~~Mr=dgu07usL+Vn>137V*0zESFk6 zW+O%Y5x6U6?QJ5T5tT(0n+lb!#YDhs3;T-*T4wc_1d5_YGgx8`#k-T;4F+s?x#}yD zaaCqS+#xEfrL(vxTBE8wZqyy#v;=o;Fkq#~wZ1$VFOpf2?+_N?#%O#A&WarSXLE+P z&>yU03dFP^F>m!^LDW~!+qA@7*)H;}3k9WlfmREI$HxR&!lR-kla6gFDqc2IX18$k z87$GP*Oll`B?utckts^JLv#v&AbIEh0KB%fCeH zylTygx@GFe|Ad~+n5@bK#&>7*;3o$^5*41KDh1_-#{+^~AW$8GY|wvU$Y<$K=zb_8 zAHG1)qUb$DnjsjdCS9^4PCCNA&^tCaiq)bTOBI<^+3B-&`>Ig5w7y}up}sV%PqsLn zg-(Cc?@u<>`9H1gs6$snfhVW7HgAZKXG8O7b7MHdd?kd|JhwI&TpOgu*Q8f}4lw4D an^VSgYj3)>liS(7xA)bqvAvo1sJ{Sz5LL$j delta 1002 zcmZ8fZD?Cn7=F*aC->&7iEVCjlQy~OO=glMS=*b$OtV?yjI(5|V_~cex0$rvT3Oq~ zElwk{#4@$R(Y0JGDpLI6Dh!EY+}xnsP^5n>r9XtptWp?+>j=fE6H(iEGWCaYczC~_ z_j%vLIUn9dXV%fQ`{=g-=o|o~2gfqGiz^op0G$U=j|`6%j$M80XET8QeSo3-$kEJ@ zU-;-3!d)YXj*x`<23{oo0>PG%vHXi~Y}QwZUX5UKG&`6%J9%OjK({(GDfphV0#L)b z?8*FrTZ?Z4oGg(^uRvp7sa}FZc)k8>{T{wEv)t+`95}fB8RO1-puq@&K1 zl2(S5(+tRT3fE| zx;^CpN6N7*goNTLr@nnf^oeV(Pu*&FdCS0qf+y+u*t_Jj`=Y+ttw}%jr~IpdNZ|XR zB{;fGyX{;@y5pP9>s=SSHl-G+9EvsW2v9HK#>1N6VE~gD5|Fu>Ga$F<>Oc=QN4gPm zdAyAbiAfX=@n(|>m% zg>y&_hZK_>uSqG1F5M^o-9V*C!)C%MY_2EriA4TvK0(j0Lgdki(-{#cXShF1K0Hkp zEz+0CqCA9Q7Zd@BA>P6=tN{g(i!maGb-T8kx{=CDY&T(U7)+BS#8eVtDR+uN}w7??UZWwNTg*z}1}hfCOgJVe{#79{TO zG;{dtZla})K==6nY*htc@%PMxRz0$ODbkN^Mx diff --git a/web/src/assets/iconfont/iconfont.woff b/web/src/assets/iconfont/iconfont.woff index 426fce0c2100df24c4d077b53b5391fa230d46af..c20755b7a8f6759f87b0f00101eeb4617e0c5c54 100644 GIT binary patch delta 19959 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 G0002;hnkH5 delta 19730 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 P(dw>innk;mX&$NyeA%qa literal 17612 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 diff --git a/web/src/components/UserOrder.vue b/web/src/components/UserOrder.vue index dd8a6c59..13432f27 100644 --- a/web/src/components/UserOrder.vue +++ b/web/src/components/UserOrder.vue @@ -1,5 +1,5 @@