diff --git a/api/core/types/order.go b/api/core/types/order.go index 41096d37..39d7c8c4 100644 --- a/api/core/types/order.go +++ b/api/core/types/order.go @@ -11,9 +11,8 @@ type OrderStatus int const ( OrderNotPaid = OrderStatus(0) - OrderScanned = OrderStatus(1) // 已扫码 - OrderPaidSuccess = OrderStatus(2) - OrderPaidFailed = OrderStatus(3) + OrderPaidSuccess = OrderStatus(2) // 已支付 + OrderPaidFailed = OrderStatus(3) // 已关闭 ) type OrderRemark struct { diff --git a/api/handler/admin/admin_handler.go b/api/handler/admin/admin_handler.go index 6cc60e9d..002096ae 100644 --- a/api/handler/admin/admin_handler.go +++ b/api/handler/admin/admin_handler.go @@ -81,19 +81,6 @@ func (h *ManagerHandler) Login(c *gin.Context) { return } - // if h.App.SysConfig.Base.EnabledVerify { - // var check bool - // if data.X != 0 { - // check = h.captcha.SlideCheck(data) - // } else { - // check = h.captcha.Check(data) - // } - // if !check { - // resp.ERROR(c, "请先完人机验证") - // return - // } - // } - var manager model.AdminUser res := h.DB.Model(&model.AdminUser{}).Where("username = ?", data.Username).First(&manager) if res.Error != nil { diff --git a/api/handler/admin/product_handler.go b/api/handler/admin/product_handler.go index 333ccc76..2202fa6f 100644 --- a/api/handler/admin/product_handler.go +++ b/api/handler/admin/product_handler.go @@ -15,9 +15,10 @@ import ( "geekai/store/vo" "geekai/utils" "geekai/utils/resp" + "time" + "github.com/gin-gonic/gin" "gorm.io/gorm" - "time" ) type ProductHandler struct { @@ -43,9 +44,7 @@ func (h *ProductHandler) Save(c *gin.Context) { Id uint `json:"id"` Name string `json:"name"` Price float64 `json:"price"` - Discount float64 `json:"discount"` Enabled bool `json:"enabled"` - Days int `json:"days"` Power int `json:"power"` CreatedAt int64 `json:"created_at"` } @@ -55,12 +54,10 @@ func (h *ProductHandler) Save(c *gin.Context) { } item := model.Product{ - Name: data.Name, - Price: data.Price, - Discount: data.Discount, - Days: data.Days, - Power: data.Power, - Enabled: data.Enabled} + Name: data.Name, + Price: data.Price, + Power: data.Power, + Enabled: data.Enabled} item.Id = data.Id if item.Id > 0 { item.CreatedAt = time.Unix(data.CreatedAt, 0) diff --git a/api/handler/order_handler.go b/api/handler/order_handler.go index 1dc8d08e..bcdea2d4 100644 --- a/api/handler/order_handler.go +++ b/api/handler/order_handler.go @@ -15,7 +15,6 @@ import ( "geekai/store/vo" "geekai/utils" "geekai/utils/resp" - "time" "github.com/gin-gonic/gin" "gorm.io/gorm" @@ -96,17 +95,8 @@ func (h *OrderHandler) Query(c *gin.Context) { return } - counter := 0 - for { - time.Sleep(time.Second) - var item model.Order - h.DB.Where("order_no = ?", orderNo).First(&item) - if counter >= 15 || item.Status == types.OrderPaidSuccess || item.Status != order.Status { - order.Status = item.Status - break - } - counter++ - } + var item model.Order + h.DB.Where("order_no = ?", orderNo).First(&item) resp.SUCCESS(c, gin.H{"status": order.Status}) } diff --git a/api/handler/payment_handler.go b/api/handler/payment_handler.go index 5d0eeeb8..4069f3bf 100644 --- a/api/handler/payment_handler.go +++ b/api/handler/payment_handler.go @@ -9,7 +9,6 @@ package handler import ( "embed" - "errors" "fmt" "geekai/core" "geekai/core/middleware" @@ -77,17 +76,14 @@ func (h *PaymentHandler) RegisterRoutes() { // 支付回调接口(公开) rg.POST("notify/alipay", h.AlipayNotify) - rg.GET("notify/geek", h.GeekPayNotify) - rg.POST("notify/wechat", h.WechatPayNotify) + rg.GET("notify/epay", h.EPayNotify) + rg.POST("notify/wxpay", h.WxpayNotify) // 需要用户登录的接口 rg.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) { - rg.POST("create", h.Pay) + rg.POST("create", h.CreateOrder) } - - // 同步订单状态 - h.StartSyncOrders() } func (h *PaymentHandler) StartSyncOrders() { @@ -116,10 +112,11 @@ func (h *PaymentHandler) SyncOrders() error { } for _, order := range orders { - // 超时15分钟的订单,直接标记为已关闭 + //超时15分钟的订单,直接标记为已关闭 if time.Now().After(order.CreatedAt.Add(time.Minute * 15)) { h.DB.Model(&model.Order{}).Where("id", order.Id).Update("checked", true) - return errors.New("订单超时") + logger.Errorf("订单超时:%v", order) + continue } // 查询订单状态 var res payment.OrderInfo @@ -127,18 +124,22 @@ func (h *PaymentHandler) SyncOrders() error { case payment.PayChannelEpay: res, err = h.epayService.Query(order.OrderNo) if err != nil { - return fmt.Errorf("error with query order info: %v", err) + logger.Errorf("error with query order info: %v", err) + continue } // 微信支付 case payment.PayChannelWX: res, err = h.wxpayService.Query(order.OrderNo) + logger.Debugf("微信支付订单状态:%+v", res) if err != nil { - return fmt.Errorf("error with query order info: %v", err) + logger.Errorf("error with query order info: %v", err) + continue } case payment.PayChannelAL: res, err = h.alipayService.Query(order.OrderNo) if err != nil { - return fmt.Errorf("error with query order info: %v", err) + logger.Errorf("error with query order info: %v", err) + continue } } @@ -148,24 +149,26 @@ func (h *PaymentHandler) SyncOrders() error { "checked": true, "status": types.OrderPaidFailed, }) - return errors.New("订单已关闭") + logger.Errorf("订单已关闭:%v", order) + continue } // 订单未支付,不处理,继续轮询 if !res.Success() { - return nil + continue } // 订单支付成功 err = h.paySuccess(res) if err != nil { - return fmt.Errorf("error with deal order: %v", err) + logger.Errorf("error with deal order: %v", err) + continue } } return nil } -func (h *PaymentHandler) Pay(c *gin.Context) { +func (h *PaymentHandler) CreateOrder(c *gin.Context) { var data struct { PayWay string `json:"pay_way,omitempty"` // 支付方式:支付宝,微信 Pid int `json:"pid,omitempty"` @@ -210,7 +213,7 @@ func (h *PaymentHandler) Pay(c *gin.Context) { if h.config.WxPay.Domain != "" { data.Domain = h.config.WxPay.Domain } - notifyURL = fmt.Sprintf("%s/api/payment/notify/wechat", data.Domain) + notifyURL = fmt.Sprintf("%s/api/payment/notify/wxpay", data.Domain) payURL, err = h.wxpayService.Pay(payment.PayRequest{ OutTradeNo: orderNo, TotalFee: fmt.Sprintf("%d", int(amount*100)), @@ -230,7 +233,7 @@ func (h *PaymentHandler) Pay(c *gin.Context) { if h.config.Epay.Domain != "" { data.Domain = h.config.Epay.Domain } - notifyURL = fmt.Sprintf("%s/api/payment/notify/geek", data.Domain) + notifyURL = fmt.Sprintf("%s/api/payment/notify/epay", data.Domain) params := payment.PayRequest{ OutTradeNo: orderNo, Subject: product.Name, @@ -280,7 +283,7 @@ func (h *PaymentHandler) Pay(c *gin.Context) { if h.config.Epay.Domain != "" { data.Domain = h.config.Epay.Domain } - notifyURL = fmt.Sprintf("%s/api/payment/notify/geek", data.Domain) + notifyURL = fmt.Sprintf("%s/api/payment/notify/epay", data.Domain) params := payment.PayRequest{ OutTradeNo: orderNo, Subject: product.Name, @@ -309,7 +312,6 @@ func (h *PaymentHandler) Pay(c *gin.Context) { // 创建订单 remark := types.OrderRemark{ - Days: product.Days, Power: product.Power, Name: product.Name, Price: product.Price, @@ -377,7 +379,7 @@ func (h *PaymentHandler) paySuccess(info payment.OrderInfo) error { order.Status = types.OrderPaidSuccess order.TradeNo = info.TradeId order.Checked = true - err = h.DB.Updates(&order).Error + err = h.DB.Debug().Updates(&order).Error if err != nil { return fmt.Errorf("error with update order info: %v", err) } @@ -418,14 +420,14 @@ func (h *PaymentHandler) AlipayNotify(c *gin.Context) { c.String(http.StatusOK, "success") } -// GeekPayNotify 支付异步回调 -func (h *PaymentHandler) GeekPayNotify(c *gin.Context) { +// EPayNotify 易支付支付异步回调 +func (h *PaymentHandler) EPayNotify(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) + logger.Infof("收到易支付订单支付回调:%+v", params) // 检查支付状态, 如果未支付,则返回成功 if params["trade_status"] != "TRADE_SUCCESS" { c.String(http.StatusOK, "success") @@ -456,8 +458,8 @@ func (h *PaymentHandler) GeekPayNotify(c *gin.Context) { c.String(http.StatusOK, "success") } -// WechatPayNotify 微信商户支付异步回调 -func (h *PaymentHandler) WechatPayNotify(c *gin.Context) { +// WxpayNotify 微信商户支付异步回调 +func (h *PaymentHandler) WxpayNotify(c *gin.Context) { err := c.Request.ParseForm() if err != nil { c.String(http.StatusOK, "fail") diff --git a/api/main.go b/api/main.go index 71c2221e..8d3e9cd3 100644 --- a/api/main.go +++ b/api/main.go @@ -303,6 +303,7 @@ func main() { }), fx.Invoke(func(s *core.AppServer, h *handler.PaymentHandler) { h.RegisterRoutes() + h.StartSyncOrders() }), fx.Invoke(func(s *core.AppServer, h *admin.ProductHandler) { h.RegisterRoutes() diff --git a/api/service/migration_service.go b/api/service/migration_service.go index 1288af95..223963bf 100644 --- a/api/service/migration_service.go +++ b/api/service/migration_service.go @@ -118,6 +118,18 @@ func (s *MigrationService) TableMigration() { if s.db.Migrator().HasColumn(&model.ChatModel{}, "description") { s.db.Migrator().DropColumn(&model.ChatModel{}, "description") } + if s.db.Migrator().HasColumn(&model.Product{}, "discount") { + s.db.Migrator().DropColumn(&model.Product{}, "discount") + } + if s.db.Migrator().HasColumn(&model.Product{}, "days") { + s.db.Migrator().DropColumn(&model.Product{}, "days") + } + if s.db.Migrator().HasColumn(&model.Product{}, "app_url") { + s.db.Migrator().DropColumn(&model.Product{}, "app_url") + } + if s.db.Migrator().HasColumn(&model.Product{}, "url") { + s.db.Migrator().DropColumn(&model.Product{}, "url") + } } // 迁移配置数据 diff --git a/api/service/payment/wxpay_service.go b/api/service/payment/wxpay_service.go index fc4e100a..89ca27a9 100644 --- a/api/service/payment/wxpay_service.go +++ b/api/service/payment/wxpay_service.go @@ -74,6 +74,7 @@ func (s *WxPayService) Pay(params PayRequest) (string, error) { bm.Set("total", utils.IntValue(params.TotalFee, 0)). Set("currency", "CNY") }) + logger.Debugf("wxpay params: %+v", bm) if params.Device == "mobile" { bm.SetBodyMap("scene_info", func(bm gopay.BodyMap) { bm.Set("payer_client_ip", params.ClientIP) diff --git a/api/store/model/order.go b/api/store/model/order.go index 3fc2a06b..7fb5ce72 100644 --- a/api/store/model/order.go +++ b/api/store/model/order.go @@ -17,7 +17,7 @@ type Order struct { Amount float64 `gorm:"column:amount;type:decimal(10,2);not null;default:0.00;comment:订单金额" json:"amount"` Status types.OrderStatus `gorm:"column:status;type:tinyint(1);not null;default:0;comment:订单状态(0:待支付,1:已扫码,2:支付成功)" json:"status"` Remark string `gorm:"column:remark;type:varchar(255);not null;comment:备注" json:"remark"` - PayTime int64 `gorm:"column:pay_time;type:int;comment:支付时间" json:"pay_time"` + PayTime int64 `gorm:"column:pay_time;type:int(11);comment:支付时间" json:"pay_time"` PayWay string `gorm:"column:pay_way;type:varchar(20);not null;comment:支付方式" json:"pay_way"` Channel string `gorm:"column:channel;type:varchar(30);not null;comment:支付类型渠道:支付宝,微信,聚合支付"` // 支付类型渠道 CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` diff --git a/api/store/model/product.go b/api/store/model/product.go index f1bcc744..1278ecbb 100644 --- a/api/store/model/product.go +++ b/api/store/model/product.go @@ -6,19 +6,15 @@ import ( // Product 充值产品 type Product struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` Name string `gorm:"column:name;type:varchar(30);not null;comment:名称" json:"name"` - Price float64 `gorm:"column:price;type:decimal(10,2);not null;default:0.00;comment:价格" json:"price"` - Discount float64 `gorm:"column:discount;type:decimal(10,2);not null;default:0.00;comment:优惠金额" json:"discount"` - Days int `gorm:"column:days;type:smallint;not null;default:0;comment:延长天数" json:"days"` - Power int `gorm:"column:power;type:int;not null;default:0;comment:增加算力值" json:"power"` - Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;default:0;comment:是否启动" json:"enabled"` - Sales int `gorm:"column:sales;type:int;not null;default:0;comment:销量" json:"sales"` - SortNum int `gorm:"column:sort_num;type:tinyint;not null;default:0;comment:排序" json:"sort_num"` - CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` - UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"` - AppUrl string `gorm:"column:app_url;type:varchar(255);comment:App跳转地址" json:"app_url"` - Url string `gorm:"column:url;type:varchar(255);comment:跳转地址" json:"url"` + Price float64 `gorm:"column:price;type:decimal(10,2);not null;default:0.00;comment:价格" json:"price"` + Power int `gorm:"column:power;type:int;not null;default:0;comment:增加算力值" json:"power"` + Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;default:0;comment:是否启动" json:"enabled"` + Sales int `gorm:"column:sales;type:int;not null;default:0;comment:销量" json:"sales"` + SortNum int `gorm:"column:sort_num;type:tinyint;not null;default:0;comment:排序" json:"sort_num"` + CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"` } func (m *Product) TableName() string { diff --git a/api/utils/strings.go b/api/utils/strings.go index 0538163d..1e198a59 100644 --- a/api/utils/strings.go +++ b/api/utils/strings.go @@ -17,8 +17,9 @@ import ( "time" "unicode" - "golang.org/x/crypto/sha3" rand2 "math/rand" + + "golang.org/x/crypto/sha3" ) // RandString generate rand string with specified length @@ -72,7 +73,16 @@ func Str2stamp(str string) int64 { return 0 } - layout := "2006-01-02 15:04:05" + var layout string + if strings.Contains(str, "T") { + layout = "2006-01-02T15:04:05-07:00" + } else { + if len(str) < 12 { + str = str + " 00:00:00" + } + layout = "2006-01-02 15:04:05" + } + t, err := time.ParseInLocation(layout, str, time.Local) if err != nil { return 0 diff --git a/web/src/assets/css/member.scss b/web/src/assets/css/member.scss index db37e31b..83cf9593 100644 --- a/web/src/assets/css/member.scss +++ b/web/src/assets/css/member.scss @@ -81,7 +81,8 @@ width: 100%; height: 100%; z-index: 0; - background: url('data:image/svg+xml;utf8,') no-repeat center/cover; + background: url('data:image/svg+xml;utf8,') + no-repeat center/cover; opacity: 0.08; pointer-events: none; } @@ -99,115 +100,259 @@ } .list-box { + .product-col { + animation: fadeInUp 0.6s ease-out; + animation-fill-mode: both; + + &:nth-child(1) { + animation-delay: 0.1s; + } + &:nth-child(2) { + animation-delay: 0.2s; + } + &:nth-child(3) { + animation-delay: 0.3s; + } + &:nth-child(4) { + animation-delay: 0.4s; + } + &:nth-child(5) { + animation-delay: 0.5s; + } + &:nth-child(6) { + animation-delay: 0.6s; + } + } + .product-item { - // border: 1px solid #666666; - background-color: var(--chat-bg); - border-radius: 6px; + background: linear-gradient(135deg, var(--panel-bg) 0%, var(--chat-bg) 100%); + border-radius: 16px; overflow: hidden; cursor: pointer; - transition: all 0.3s ease; /* 添加过渡效果 */ - margin-bottom: 20px; + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); + margin-bottom: 24px; + border: 1px solid rgba(255, 255, 255, 0.1); + position: relative; - .image-container { - display: flex; - justify-content: center; + .product-header { + position: relative; - .el-image { - padding: 6px; - - .el-image__inner { - border-radius: 10px; - } - } - } - - .product-title { - display: flex; - padding: 10px; - - .name { - width: 100%; - text-align: center; - font-size: 16px; - font-weight: bold; - color: var(--el-color-primary); - } - } - - .product-info { - padding: 10px 20px; - font-size: 14px; - color: #999999; - - .info-line { - display: flex; - width: 100%; - padding: 5px 0; - - .label { - display: flex; - width: 100%; - } - - .price, .expire, calls { - display: flex; - width: 90px; - justify-content: right; - } - - .discount { - color: #f56c6c; - font-size: 20px; - } - - .expire { - color: #409eff; - } - - .power { - color: #f2cb51; - } - } - - .pay-way { - padding: 10px 0; + .image-container { + position: relative; display: flex; justify-content: center; - flex-wrap: wrap; + padding: 20px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - .el-button { - margin: 10px 5px 0 5px; - height: 32px; - filter: none; + .el-image { + width: 80px; + height: 80px; + border-radius: 50%; + overflow: hidden; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); + transition: transform 0.3s ease; - .icon-alipay, .icon-wechat-pay { - color: #ffffff; + &:hover { + transform: scale(1.1); } - .icon-qq { - color: #15a6e8; - font-size: 24px; + } + + .image-overlay { + position: absolute; + top: 10px; + right: 10px; + + .vip-badge { + background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%); + color: #333; + padding: 4px 12px; + border-radius: 20px; + font-size: 12px; + font-weight: bold; + box-shadow: 0 4px 12px rgba(255, 215, 0, 0.4); + animation: pulse 2s infinite; } - .icon-jd-pay { - color: var(--text-theme-color); - font-size: 24px; + } + } + + .product-title { + padding: 20px 20px 0; + text-align: center; + + .name { + font-size: 20px; + font-weight: 700; + color: var(--text-theme-color); + margin: 0 0 8px 0; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + } + + .description { + font-size: 14px; + color: var(--text-secondary-color, #666); + margin: 0; + line-height: 1.4; + } + } + } + + .product-content { + padding: 20px; + + .price-section { + text-align: center; + margin-bottom: 20px; + + .price-info { + display: flex; + align-items: baseline; + justify-content: center; + margin-bottom: 8px; + + .currency { + font-size: 18px; + color: #f56c6c; + font-weight: 600; + margin-right: 2px; } - .icon-douyin { - color: #0a0a0a; - font-size: 22px; + + .price-value { + font-size: 32px; + font-weight: 800; + color: #f56c6c; + line-height: 1; } - .icon-paypal { + + .price-unit { font-size: 14px; - color: #009cde; + color: var(--text-secondary-color, #666); + margin-left: 4px; + } + } + + .original-price { + font-size: 12px; + color: #999; + text-decoration: line-through; + } + } + + .features-list { + .feature-item { + display: flex; + align-items: center; + margin-bottom: 8px; + font-size: 14px; + color: var(--text-secondary-color, #666); + + i { + color: #67c23a; + margin-right: 8px; + font-size: 16px; + } + } + } + } + + .product-actions { + padding: 0 20px 20px; + + .payment-buttons { + display: flex; + gap: 12px; + + .payment-btn { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 12px 16px; + border: none; + border-radius: 12px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + overflow: hidden; + transform: translateZ(0); + + &::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(255, 255, 255, 0.2), + transparent + ); + transition: left 0.5s; + } + + &:hover::before { + left: 100%; + } + + &:active { + transform: translateY(1px) scale(0.98); + } + + i { + font-size: 18px; + transition: transform 0.3s ease; + } + + span { + font-weight: 600; + transition: transform 0.3s ease; + } + + &:hover { + i, + span { + transform: scale(1.05); + } + } + + &.wechat-btn { + background: linear-gradient(135deg, #07c160 0%, #06ad56 100%); + color: white; + box-shadow: 0 4px 16px rgba(7, 193, 96, 0.3); + + &:hover { + transform: translateY(-2px); + box-shadow: 0 8px 24px rgba(7, 193, 96, 0.4); + background: linear-gradient(135deg, #06ad56 0%, #07c160 100%); + } + } + + &.alipay-btn { + background: linear-gradient(135deg, #1677ff 0%, #0e5fd8 100%); + color: white; + box-shadow: 0 4px 16px rgba(22, 119, 255, 0.3); + + &:hover { + transform: translateY(-2px); + box-shadow: 0 8px 24px rgba(22, 119, 255, 0.4); + background: linear-gradient(135deg, #0e5fd8 0%, #1677ff 100%); + } } } } } &:hover { - // box-shadow: 0 0 10px rgba(71, 255, 241, 0.6); /* 添加阴影效果 */ - transform: translateY(-10px); /* 向上移动10像素 */ - box-shadow: 0 0 10px var(--shadow-color); - background-color: var(--hover-deep-color); + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15); + border-color: rgba(102, 126, 234, 0.3); } } } @@ -234,4 +379,168 @@ font-weight: 700; } } -} \ No newline at end of file +} + +// 添加动画效果 +@keyframes pulse { + 0%, + 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.05); + opacity: 0.8; + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +// 响应式优化 +@media (max-width: 768px) { + .member { + .inner { + .product-box { + .list-box { + .el-col { + width: 100% !important; + margin-bottom: 16px; + } + + .product-item { + .product-header { + .image-container { + padding: 16px; + + .el-image { + width: 60px; + height: 60px; + } + } + + .product-title { + padding: 16px 16px 0; + + .name { + font-size: 18px; + } + } + } + + .product-content { + padding: 16px; + + .price-section { + .price-info { + .price-value { + font-size: 28px; + } + } + } + } + + .product-actions { + padding: 0 16px 16px; + + .payment-buttons { + flex-direction: column; + gap: 8px; + + .payment-btn { + padding: 10px 14px; + } + } + } + } + } + } + } + } +} + +@media (max-width: 480px) { + .member { + .inner { + padding: 10px 0 10px 10px; + + .product-box { + padding: 0 10px; + + .list-box { + .product-item { + margin-bottom: 16px; + + .product-header { + .image-container { + padding: 12px; + + .el-image { + width: 50px; + height: 50px; + } + } + + .product-title { + padding: 12px 12px 0; + + .name { + font-size: 16px; + } + + .description { + font-size: 12px; + } + } + } + + .product-content { + padding: 12px; + + .price-section { + .price-info { + .price-value { + font-size: 24px; + } + + .currency { + font-size: 16px; + } + } + } + + .features-list { + .feature-item { + font-size: 12px; + } + } + } + + .product-actions { + padding: 0 12px 12px; + + .payment-buttons { + .payment-btn { + padding: 8px 12px; + font-size: 12px; + + i { + font-size: 16px; + } + } + } + } + } + } + } + } + } +} diff --git a/web/src/components/UserOrder.vue b/web/src/components/UserOrder.vue index 2923eb9d..eef50bf8 100644 --- a/web/src/components/UserOrder.vue +++ b/web/src/components/UserOrder.vue @@ -18,7 +18,7 @@ {{ scope.row.remark && scope.row.remark.power }} - + diff --git a/web/src/views/ChatPlus.vue b/web/src/views/ChatPlus.vue index 83c391e1..817f2a4a 100644 --- a/web/src/views/ChatPlus.vue +++ b/web/src/views/ChatPlus.vue @@ -340,7 +340,7 @@ - + @@ -832,7 +832,7 @@ const sendSSERequest = async (message) => { } // 发送消息 -const sendMessage = (messageId) => { +const sendMessage = (messageId = 0) => { if (!isLogin.value) { console.log('未登录') store.setShowLoginDialog(true) @@ -895,7 +895,7 @@ const sendMessage = (messageId) => { tools: toolSelected.value, stream: stream.value, files: files.value, - last_msg_id: messageId, + last_msg_id: messageId || 0, }) prompt.value = '' diff --git a/web/src/views/Member.vue b/web/src/views/Member.vue index d3394156..cf6c007d 100644 --- a/web/src/views/Member.vue +++ b/web/src/views/Member.vue @@ -35,69 +35,56 @@ - - - - + + - - - - - {{ item.name }} - - - - 商品原价: - ¥{{ item.price }} + + + + + 热销 + - - 优惠价: - ¥{{ item.discount }} + + {{ item.name }} + {{ item.description || '全模型通用算力' }} - - 有效期: - {{ item.days }}天 - 长期有效 + + + + + + ¥ + {{ item.price }} + - - 算力值: - {{ item.power }} + + + + {{ feature }} + + - - - - - - - - - - - - - - - - - - - - - + + + + + 微信支付 + + + + 支付宝 + @@ -154,23 +141,20 @@ + - - - 请使用微信扫码支付:¥{{ price }} + + + {{ title }} - - - - 支付成功 - 支付失败 + + + @@ -188,7 +172,9 @@ import { useSharedStore } from '@/store/sharedata' import { httpGet, httpPost } from '@/utils/http' import { ElMessage } from 'element-plus' import QRCode from 'qrcode' -import { onMounted, ref } from 'vue' +import { onMounted, ref, onUnmounted } from 'vue' +import { showLoading, closeLoading } from '@/utils/dialog' +import { isMobile } from '@/utils/libs' const list = ref([]) const vipImg = ref('/images/menu/member.png') @@ -203,15 +189,13 @@ const isLogin = ref(false) const orderTimeout = ref(1800) const loading = ref(true) const loadingText = ref('加载中...') -const orderPayInfoText = ref('') -const payWays = ref([]) -const vipInfoText = ref('') const store = useSharedStore() const userOrderKey = ref(0) -const showDialog = ref(false) +const showQrCode = ref(false) const qrImg = ref('') -const price = ref(0) +const title = ref('') +const handler = ref(null) onMounted(() => { checkSession() @@ -236,68 +220,78 @@ onMounted(() => { .then((res) => { rewardImg.value = res.data['reward_img'] enableReward.value = res.data['enabled_reward'] - orderPayInfoText.value = res.data['order_pay_info_text'] if (res.data['order_pay_timeout'] > 0) { orderTimeout.value = res.data['order_pay_timeout'] } - vipInfoText.value = res.data['vip_info_text'] }) .catch((e) => { ElMessage.error('获取系统配置失败:' + e.message) }) - - httpGet('/api/payment/payWays') - .then((res) => { - payWays.value = res.data - }) - .catch((e) => { - ElMessage.error('获取支付方式失败:' + e.message) - }) }) -const pay = (product, payWay) => { - if (!isLogin.value) { - store.setShowLoginDialog(true) - return - } - loading.value = true - loadingText.value = '正在生成支付订单...' - let host = import.meta.env.VITE_API_HOST - if (host === '') { - host = `${location.protocol}//${location.host}` - } - httpPost(`${import.meta.env.VITE_API_HOST}/api/payment/doPay`, { - product_id: product.id, - pay_way: payWay.pay_way, - pay_type: payWay.pay_type, - user_id: user.value.id, - host: host, - device: 'jump', +const selectedPid = ref(0) +const wxPay = (product) => { + selectedPid.value = product.id + title.value = '请打开微信扫码支付' + generateOrder('wxpay') +} + +const alipay = (product) => { + selectedPid.value = product.id + title.value = '请打开支付宝扫码支付' + generateOrder('alipay') +} + +const generateOrder = (payWay) => { + showLoading('正在生成支付订单...') + // 生成支付订单 + httpPost('/api/payment/create', { + pid: selectedPid.value, + pay_way: payWay, + domain: `${window.location.protocol}//${window.location.host}`, + device: isMobile() ? 'mobile' : 'pc', }) .then((res) => { - showDialog.value = true - loading.value = false - if (payWay.pay_way === 'wechat') { - price.value = Number(product.discount) - QRCode.toDataURL(res.data, { width: 300, height: 300, margin: 2 }, (error, url) => { + closeLoading() + + if (isMobile()) { + window.location.href = res.data.pay_url + } else { + QRCode.toDataURL(res.data.pay_url, { width: 300, height: 300, margin: 2 }, (error, url) => { if (error) { console.error(error) } else { qrImg.value = url } }) - } else { - window.open(res.data, '_blank') + // 查询订单状态 + if (handler.value) { + clearTimeout(handler.value) + } + handler.value = setTimeout(() => queryOrder(res.data.order_no), 3000) + showQrCode.value = true } }) .catch((e) => { - setTimeout(() => { - ElMessage.error('生成支付订单失败:' + e.message) - loading.value = false - }, 500) + closeLoading() + ElMessage.error('生成支付订单失败:' + e.message) }) } +const queryOrder = async (orderNo) => { + const res = await httpGet('/api/order/query?order_no=' + orderNo) + if (res?.data.status === 2) { + // 订单支付成功 + clearTimeout(handler.value) + ElMessage.success('支付成功') + showQrCode.value = false + // 更新用户积分 + user.value.scores += res.data.credit + } else { + handler.value = setTimeout(() => queryOrder(orderNo), 3000) + } +} + const redeemCallback = (success) => { showRedeemVerifyDialog.value = false @@ -306,15 +300,67 @@ const redeemCallback = (success) => { } } -const payCallback = (success) => { - showDialog.value = false - if (success) { - userOrderKey.value += 1 +// 组件卸载时清理定时器 +onUnmounted(() => { + if (handler.value) { + clearTimeout(handler.value) + handler.value = null } -} +}) diff --git a/web/src/views/admin/Order.vue b/web/src/views/admin/Order.vue index f8b4e401..3ae4c57b 100644 --- a/web/src/views/admin/Order.vue +++ b/web/src/views/admin/Order.vue @@ -42,14 +42,23 @@ - - - {{ dateFormat(scope.row['pay_time']) }} - 未支付 + + + 已支付 + 已关闭 + 未支付 - - + + + + + {{ dateFormat(row.pay_time) || '--' }} + + + + + diff --git a/web/src/views/admin/Product.vue b/web/src/views/admin/Product.vue index 2eb87fd4..675a0166 100644 --- a/web/src/views/admin/Product.vue +++ b/web/src/views/admin/Product.vue @@ -14,14 +14,7 @@ - - - - - 长期有效 - {{ scope.row.days }} - - + @@ -55,18 +48,10 @@ - + - - - - - - - -
{{ item.description || '全模型通用算力' }}