Compare commits

...

16 Commits

Author SHA1 Message Date
9f220dc025 🎨 添加微信支付证书 2025-07-25 23:03:11 +08:00
7951f77f47 🎨 优化文章相关接口 2025-07-25 23:02:57 +08:00
bb2a68fb61 🎨 完善订单和微信支付功能 2025-07-25 23:02:43 +08:00
8fd1968cf6 🎨 优化微信登录流程 2025-07-23 02:54:43 +08:00
666527b976 🎨 修改临时数据库,新增跨域中间件 2025-07-23 02:38:53 +08:00
502a9b82d7 🎨 新增短信宝发送平台接口 2025-07-23 02:35:04 +08:00
7a729211d1 🎨 优化微信登录流程&新增手机和微信绑定接口 2025-07-23 02:33:39 +08:00
f0a67822ef 🎨 完善用户讲师申请接口,新增order模块 2025-07-20 21:52:11 +08:00
70f65c96bd 🎨 新增讲师相关接口(待完善) 2025-07-20 03:07:42 +08:00
48ddb72cf2 🎨 完善注册逻辑 2025-07-20 02:11:35 +08:00
73e97ba151 🎨 完善注册逻辑 2025-07-20 02:11:27 +08:00
c3d5d532cd 🎨 完善注册逻辑 2025-07-20 02:11:19 +08:00
f86b56a79d 🎨 新增随机邀请码工具 2025-07-20 02:10:55 +08:00
fa0234d385 🎨 优化路由跨域,修改微信商户配置 2025-07-19 06:19:09 +08:00
8106282c8b 🎨 新增订单模块 2025-07-19 06:18:44 +08:00
aac13d369c 🎨 优化文章相关接口,新增app专用接口 2025-07-19 06:18:33 +08:00
37 changed files with 1467 additions and 167 deletions

View File

@@ -5,8 +5,10 @@ import "git.echol.cn/loser/lckt/service"
type ApiGroup struct {
AppUserApi
BannerApi
OrderApi
}
var userService = service.ServiceGroupApp.UserServiceGroup.UserService
var appUserService = service.ServiceGroupApp.AppServiceGroup.AppUserService
var bannerService = service.ServiceGroupApp.AppServiceGroup.BannerService
var orderService = service.ServiceGroupApp.AppServiceGroup.OrderService

131
api/v1/app/order.go Normal file
View File

@@ -0,0 +1,131 @@
package app
import (
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/model/app"
"git.echol.cn/loser/lckt/model/app/request"
r "git.echol.cn/loser/lckt/model/common/response"
"git.echol.cn/loser/lckt/utils/user_jwt"
"git.echol.cn/loser/lckt/utils/wechat"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type OrderApi struct{}
// CreateOrder APP新建订单
func (o *OrderApi) CreateOrder(c *gin.Context) {
var p app.Order
if err := c.ShouldBind(&p); err != nil {
global.GVA_LOG.Error("支付订单参数错误", zap.Error(err))
r.FailWithMessage("支付订单参数错误", c)
return
}
order, err := orderService.Create(&p)
if err != nil {
global.GVA_LOG.Error("创建订单失败", zap.Error(err))
r.FailWithMessage("创建订单失败", c)
return
}
r.OkWithData(order, c)
}
func (o *OrderApi) PayOrder(context *gin.Context) {
var p request.PayReq
if err := context.ShouldBind(&p); err != nil {
global.GVA_LOG.Error("支付订单参数错误", zap.Error(err))
r.FailWithMessage("支付订单参数错误", context)
return
}
pay, err := orderService.Pay(p, context)
if err != nil {
global.GVA_LOG.Error("支付订单失败", zap.Error(err))
r.FailWithMessage("支付订单失败:"+err.Error(), context)
return
}
r.OkWithDetailed(pay, "支付订单成功", context)
}
// NotifyOrder 微信支付回调通知
func (o *OrderApi) NotifyOrder(context *gin.Context) {
err := wechat.NotifyHandle(context)
if err != nil {
global.GVA_LOG.Error("微信支付回调处理失败", zap.Error(err))
r.FailWithMessage("微信支付回调处理失败:"+err.Error(), context)
return
}
r.OkWithMessage("微信支付回调处理成功", context)
}
func (o *OrderApi) GetOrderDetail(context *gin.Context) {
id := context.Param("id")
if id == "" {
global.GVA_LOG.Error("获取订单详情参数错误: ID不能为空")
r.FailWithMessage("获取订单详情参数错误: ID不能为空", context)
return
}
order, err := orderService.GetOrderDetail(id)
if err != nil {
global.GVA_LOG.Error("获取订单详情失败", zap.Error(err))
r.FailWithMessage("获取订单详情失败", context)
return
}
r.OkWithData(order, context)
}
func (o *OrderApi) GetOrderList(context *gin.Context) {
var p request.GetOrderList
if err := context.ShouldBind(&p); err != nil {
global.GVA_LOG.Error("获取订单列表参数错误", zap.Error(err))
r.FailWithMessage("获取订单列表参数错误", context)
return
}
// 获取当前用户ID
userId := user_jwt.GetUserID(context)
if userId == 0 {
global.GVA_LOG.Error("获取用户ID失败")
r.FailWithMessage("获取用户ID失败", context)
return
}
// 获取订单列表
orders, total, err := orderService.GetOrderList(p, userId)
if err != nil {
global.GVA_LOG.Error("获取订单列表失败", zap.Error(err))
r.FailWithMessage("获取订单列表失败:"+err.Error(), context)
return
}
r.OkWithDetailed(
r.PageResult{
List: orders,
Total: total,
Page: p.Page,
PageSize: p.PageSize,
}, "获取订单列表成功", context)
}
func (o *OrderApi) BalancePay(context *gin.Context) {
var p request.BalancePay
if err := context.ShouldBind(&p); err != nil {
global.GVA_LOG.Error("余额支付参数错误", zap.Error(err))
r.FailWithMessage("余额支付参数错误", context)
return
}
err := orderService.BalancePay(p)
if err != nil {
global.GVA_LOG.Error("余额支付失败", zap.Error(err))
r.FailWithMessage("余额支付失败:"+err.Error(), context)
return
}
r.OkWithMessage("余额支付成功", context)
}

View File

@@ -3,7 +3,11 @@ package app
import (
"errors"
"fmt"
"strconv"
"time"
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/model/app"
r "git.echol.cn/loser/lckt/model/common/response"
"git.echol.cn/loser/lckt/model/user/request"
"git.echol.cn/loser/lckt/utils"
@@ -12,7 +16,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
"time"
)
type AppUserApi struct{}
@@ -62,14 +65,14 @@ func (*AppUserApi) Login(ctx *gin.Context) {
return
}
if _, err = global.GVA_REDIS.Get(ctx, user.Phone).Result(); errors.Is(err, redis.Nil) {
if _, err = global.GVA_REDIS.Get(ctx, strconv.Itoa(int(user.ID))).Result(); errors.Is(err, redis.Nil) {
// 此处过期时间等于jwt过期时间
dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
if err != nil {
return
}
timer := dr
if err := global.GVA_REDIS.Set(ctx, user.Phone, token, timer).Err(); err != nil {
if err := global.GVA_REDIS.Set(ctx, strconv.Itoa(int(user.ID)), token, timer).Err(); err != nil {
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
r.FailWithMessage("设置登录状态失败", ctx)
return
@@ -114,26 +117,25 @@ func (*AppUserApi) WechatLogin(ctx *gin.Context) {
r.FailWithMessage("获取token失败", ctx)
return
}
if _, err = global.GVA_REDIS.Get(ctx, user.Phone).Result(); errors.Is(err, redis.Nil) {
// 此处过期时间等于jwt过期时间
dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
if err != nil {
return
}
timer := dr
if err := global.GVA_REDIS.Set(ctx, user.Phone, token, timer).Err(); err != nil {
if err := global.GVA_REDIS.Set(ctx, strconv.Itoa(int(user.ID)), token, timer).Err(); err != nil {
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
r.FailWithMessage("设置登录状态失败", ctx)
return
}
user_jwt.SetToken(ctx, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
r.OkWithDetailed(gin.H{
"User": user,
"Token": token,
"ExpiresAt": claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "登录成功", ctx)
}
}
// GetUserInfo 获取用户信息
func (*AppUserApi) GetUserInfo(ctx *gin.Context) {
@@ -181,7 +183,7 @@ func (*AppUserApi) PwdLogin(ctx *gin.Context) {
return
}
timer := dr
if err := global.GVA_REDIS.Set(ctx, user.Phone, token, timer).Err(); err != nil {
if err := global.GVA_REDIS.Set(ctx, strconv.Itoa(int(user.ID)), token, timer).Err(); err != nil {
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
r.FailWithMessage("设置登录状态失败", ctx)
return
@@ -193,3 +195,169 @@ func (*AppUserApi) PwdLogin(ctx *gin.Context) {
"ExpiresAt": claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "登录成功", ctx)
}
// Register 用户手机注册
func (a *AppUserApi) Register(context *gin.Context) {
var p request.RegisterReq
if err := context.ShouldBind(&p); err != nil {
r.FailWithMessage(err.Error(), context)
global.GVA_LOG.Error("参数错误,注册失败", zap.Error(err))
return
}
if result, _ := global.GVA_REDIS.Get(context, fmt.Sprintf("VerifyCode:%s", p.Phone)).Result(); result != p.Code {
global.GVA_LOG.Error("验证码错误", zap.String("phone", p.Phone))
r.FailWithMessage("验证码错误", context)
return
}
user, err := appUserService.Register(p)
if err != nil {
global.GVA_LOG.Error("注册失败", zap.Error(err))
r.FailWithMessage("注册失败", context)
return
}
// 生成token
token, claims, err := user_jwt.LoginToken(user)
if err != nil {
global.GVA_LOG.Error("获取token失败!", zap.Error(err))
r.FailWithMessage("获取token失败", context)
return
}
// 此处过期时间等于jwt过期时间
dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
if err != nil {
return
}
timer := dr
if err := global.GVA_REDIS.Set(context, user.Phone, token, timer).Err(); err != nil {
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
r.FailWithMessage("设置登录状态失败", context)
return
}
user_jwt.SetToken(context, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
r.OkWithDetailed(gin.H{
"User": user,
"Token": token,
"ExpiresAt": claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "注册成功", context)
}
// ApplyTeacher 申请成为教师
func (a *AppUserApi) ApplyTeacher(context *gin.Context) {
var p app.TeacherApply
if err := context.ShouldBind(&p); err != nil {
r.FailWithMessage(err.Error(), context)
global.GVA_LOG.Error("参数错误,申请失败", zap.Error(err))
return
}
id := user_jwt.GetUserID(context)
if id == 0 {
global.GVA_LOG.Error("获取用户ID失败")
r.FailWithMessage("获取用户ID失败", context)
return
}
p.UserId = id
if err := appUserService.ApplyTeacher(p); err != nil {
global.GVA_LOG.Error("申请失败", zap.Error(err))
r.FailWithMessage("申请失败", context)
return
}
r.OkWithMessage("申请成功", context)
}
// GetTeacherApply 获取教师申请状态
func (a *AppUserApi) GetTeacherApply(context *gin.Context) {
id := user_jwt.GetUserID(context)
if id == 0 {
global.GVA_LOG.Error("获取用户ID失败")
r.FailWithMessage("获取用户ID失败", context)
return
}
status, err := appUserService.GetTeacherApplyStatus(id)
if err != nil {
global.GVA_LOG.Error("获取教师申请状态失败", zap.Error(err))
r.FailWithMessage("获取教师申请状态失败", context)
return
}
r.OkWithDetailed(status, "获取教师申请状态成功", context)
}
// BindWechat 手机登录用户绑定微信
func (a *AppUserApi) BindWechat(context *gin.Context) {
var p request.BindWechatReq
if err := context.ShouldBind(&p); err != nil {
global.GVA_LOG.Error("绑定微信请求参数有误")
r.FailWithMessage("绑定微信请求参数有误", context)
return
}
user, err := appUserService.BindWechat(p)
if err != nil {
global.GVA_LOG.Error("绑定微信失败", zap.Error(err))
}
// 生成新token
token, claims, err := user_jwt.LoginToken(*user)
if err != nil {
global.GVA_LOG.Error("获取token失败!", zap.Error(err))
r.FailWithMessage("获取token失败", context)
return
}
// 此处过期时间等于jwt过期时间
dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
if err != nil {
global.GVA_LOG.Error("解析JWT过期时间失败", zap.Error(err))
r.FailWithMessage("解析JWT过期时间失败", context)
return
}
timer := dr
if err := global.GVA_REDIS.Set(context, user.Phone, token, timer).Err(); err != nil {
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
r.FailWithMessage("设置登录状态失败", context)
return
}
user_jwt.SetToken(context, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
r.OkWithDetailed(gin.H{
"User": user,
"Token": token,
"ExpiresAt": claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "绑定微信成功", context)
}
// BindPhone 微信登录用户绑定手机号s
func (a *AppUserApi) BindPhone(context *gin.Context) {
var p request.BindPhoneReq
if err := context.ShouldBind(&p); err != nil {
global.GVA_LOG.Error("绑定手机号请求参数有误")
r.FailWithMessage("绑定手机号请求参数有误", context)
return
}
// 验证码检查
if result, _ := global.GVA_REDIS.Get(context, fmt.Sprintf("VerifyCode:%s", p.Phone)).Result(); result != p.Code {
global.GVA_LOG.Error("验证码错误", zap.String("phone", p.Phone))
r.FailWithMessage("验证码错误", context)
return
}
user, err := appUserService.BindPhone(p)
if err != nil {
global.GVA_LOG.Error("绑定手机号失败", zap.Error(err))
r.FailWithMessage("绑定手机号失败", context)
return
}
r.OkWithDetailed(user, "绑定手机号成功", context)
}

View File

@@ -5,6 +5,7 @@ import (
"git.echol.cn/loser/lckt/model/article"
"git.echol.cn/loser/lckt/model/article/request"
r "git.echol.cn/loser/lckt/model/common/response"
"git.echol.cn/loser/lckt/utils/user_jwt"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
@@ -88,3 +89,37 @@ func (ArticleApi) ById(ctx *gin.Context) {
}
r.OkWithData(article, ctx)
}
func (ArticleApi) APPGetList(ctx *gin.Context) {
var p request.GetList
if err := ctx.ShouldBind(&p); err != nil {
r.FailWithMessage(err.Error(), ctx)
global.GVA_LOG.Error("参数有误!", zap.Error(err))
return
}
list, total, err := articleService.APPGetArticleList(p)
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
r.FailWithMessage("查询失败:"+err.Error(), ctx)
return
}
r.OkWithDetailed(gin.H{"list": list, "total": total}, "查询成功", ctx)
}
// AppById 根据ID获取文章
func (ArticleApi) AppById(ctx *gin.Context) {
id := ctx.Param("id")
if id == "" {
r.FailWithMessage("参数错误", ctx)
return
}
userId := user_jwt.GetUserID(ctx)
article, err := articleService.APPGetArticle(id, userId)
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
r.FailWithMessage("查询失败:"+err.Error(), ctx)
return
}
r.OkWithData(article, ctx)
}

View File

@@ -2,6 +2,7 @@ package user
import (
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/model/app"
common "git.echol.cn/loser/lckt/model/common/request"
r "git.echol.cn/loser/lckt/model/common/response"
"git.echol.cn/loser/lckt/model/user/request"
@@ -114,3 +115,43 @@ func (a *UserApi) GetTeachers(context *gin.Context) {
PageSize: p.PageSize,
}, "讲师列表获取成功", context)
}
// GetTeacherApplyList 获取教师申请列表
func (a *UserApi) GetTeacherApplyList(context *gin.Context) {
var p request.GetTeacherApplyListReq
if err := context.ShouldBind(&p); err != nil {
r.FailWithMessage(err.Error(), context)
global.GVA_LOG.Error("参数错误,获取教师申请列表失败", zap.Error(err))
return
}
list, total, err := userService.GetTeacherApplyList(p)
if err != nil {
global.GVA_LOG.Error("获取教师申请列表失败", zap.Error(err))
r.FailWithMessage("获取教师申请列表失败", context)
return
}
r.OkWithDetailed(
r.PageResult{
List: list,
Total: total,
Page: p.Page,
PageSize: p.PageSize,
}, "获取教师申请列表成功", context)
}
// UpdateTeacherApplyStatus 更新教师申请状态
func (a *UserApi) UpdateTeacherApplyStatus(context *gin.Context) {
var p app.TeacherApply
if err := context.ShouldBind(&p); err != nil {
r.FailWithMessage(err.Error(), context)
global.GVA_LOG.Error("参数错误,更新教师申请状态失败", zap.Error(err))
return
}
if err := userService.UpdateTeacherApplyStatus(p); err != nil {
r.FailWithMessage("更新教师申请状态失败", context)
return
}
r.OkWithMessage("更新教师申请状态成功", context)
}

View File

@@ -129,18 +129,18 @@ mssql:
log-zap: false
mysql:
prefix: ""
port: "3307"
port: "3366"
config: charset=utf8mb4&parseTime=True&loc=Local
db-name: lckt
username: lckt
password: loser765911
path: 47.120.70.120
password: loser765911.
path: 219.152.55.29
engine: ""
log-mode: error
log-mode: info
max-idle-conns: 10
max-open-conns: 100
singular: false
log-zap: false
log-zap: true
oracle:
prefix: ""
port: ""
@@ -242,20 +242,20 @@ zap:
wechat:
app-id: wx3d21df18d7f8f9fc
app-secret: ffce59a9a9272c1aaee53950e96617d8
app-secret: 3ab19e9b6a5e155c25ac6457be650047
token: kjeldcsdz2phfwfxnevsajnzsboho1ev
aes-key: PiqqlGdEblw5Gv1RJ5qcTnhKUjFw9YNkBMAX6CIw6Me
callback: https://api.gin-vue-admin.com/wechat/callback
pay-list:
- type: wxpay
alias-name: wxpay-1
app-id: 1
mch-id: 1
app-id: wx3d21df18d7f8f9fc
mch-id: 1646874753
v3-key: 1a3sd8561d5179Df152D4789aD38wG9s
cert-path: 1
key-path: 1
notify-url: 1
serial-no: 1
cert-path: /resource/cert/apiclient_cert.pem
key-path: /resource/cert/apiclient_key.pem
notify-url: http://lckt.hnlc5588.cn/app_order/notify
serial-no: 59A891FB403EC7A1CF2090DB9C8EC704BD43B101
- type: wxpay2
alias-name: wxpay-2
app-id: 2

49
go.mod
View File

@@ -1,12 +1,12 @@
module git.echol.cn/loser/lckt
go 1.23
go 1.23.0
toolchain go1.23.2
require (
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.7
github.com/ArtisanCloud/PowerWeChat/v3 v3.4.6
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.8
github.com/ArtisanCloud/PowerWeChat/v3 v3.4.21
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7
github.com/alibabacloud-go/dysmsapi-20170525/v4 v4.1.2
github.com/alibabacloud-go/tea v1.3.8
@@ -17,13 +17,16 @@ require (
github.com/casbin/gorm-adapter/v3 v3.32.0
github.com/fsnotify/fsnotify v1.8.0
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
github.com/gin-gonic/gin v1.10.0
github.com/gin-contrib/cors v1.7.6
github.com/gin-gonic/gin v1.10.1
github.com/glebarez/sqlite v1.11.0
github.com/go-sql-driver/mysql v1.8.1
github.com/goccy/go-json v0.10.4
github.com/goccy/go-json v0.10.5
github.com/golang-jwt/jwt/v4 v4.5.2
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.6.0
github.com/gookit/color v1.5.4
github.com/gorilla/websocket v1.5.3
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
github.com/mholt/archiver/v4 v4.0.0-alpha.9
@@ -48,9 +51,9 @@ require (
go.mongodb.org/mongo-driver v1.17.2
go.uber.org/automaxprocs v1.6.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.32.0
golang.org/x/sync v0.10.0
golang.org/x/text v0.21.0
golang.org/x/crypto v0.39.0
golang.org/x/sync v0.15.0
golang.org/x/text v0.26.0
gorm.io/datatypes v1.2.5
gorm.io/driver/mysql v1.5.7
gorm.io/driver/postgres v1.5.11
@@ -76,8 +79,8 @@ require (
github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/sevenzip v1.6.0 // indirect
github.com/bodgit/windows v1.0.1 // indirect
github.com/bytedance/sonic v1.12.7 // indirect
github.com/bytedance/sonic/loader v0.2.3 // indirect
github.com/bytedance/sonic v1.13.3 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/casbin/govaluate v1.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
@@ -87,9 +90,9 @@ require (
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/gammazero/toposort v0.1.1 // indirect
github.com/gin-contrib/sse v1.0.0 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
@@ -99,15 +102,13 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.24.0 // indirect
github.com/go-playground/validator/v10 v10.26.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
@@ -122,7 +123,7 @@ require (
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
@@ -141,7 +142,7 @@ require (
github.com/nwaples/rardecode/v2 v2.0.1 // indirect
github.com/otiai10/mint v1.6.3 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
@@ -163,7 +164,7 @@ require (
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
@@ -177,15 +178,15 @@ require (
go.opentelemetry.io/otel/trace v1.29.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/arch v0.13.0 // indirect
golang.org/x/arch v0.18.0 // indirect
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
golang.org/x/image v0.23.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.29.0 // indirect
google.golang.org/protobuf v1.36.3 // indirect
golang.org/x/tools v0.33.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

87
go.sum
View File

@@ -19,10 +19,10 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/ArtisanCloud/PowerLibs/v3 v3.3.2 h1:IInr1YWwkhwOykxDqux1Goym0uFhrYwBjmgLnEwCLqs=
github.com/ArtisanCloud/PowerLibs/v3 v3.3.2/go.mod h1:xFGsskCnzAu+6rFEJbGVAlwhrwZPXAny6m7j71S/B5k=
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.7 h1:P+erNlErr+X2v7Et+yTWaTfIRhw+HfpAPdvNIEwk9Gw=
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.7/go.mod h1:VZQNCvcK/rldF3QaExiSl1gJEAkyc5/I8RLOd3WFZq4=
github.com/ArtisanCloud/PowerWeChat/v3 v3.4.6 h1:IOGdt8Lt8EDRl9qBTnX2TxU0Bjx7gdK9Qy+IegyHltY=
github.com/ArtisanCloud/PowerWeChat/v3 v3.4.6/go.mod h1:ybM3u4Lhso0X+ZsgoRCF4e5W1KT2fBc6plpjPZ2fop4=
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.8 h1:0v/CMFzz5/0K9mEMebyBzlmap1tidv2PaUFSnq/bJhk=
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.8/go.mod h1:VZQNCvcK/rldF3QaExiSl1gJEAkyc5/I8RLOd3WFZq4=
github.com/ArtisanCloud/PowerWeChat/v3 v3.4.21 h1:ZDLWTLGveYWpCL0wUHF+D8imEo783ux0OFCwczNvgAU=
github.com/ArtisanCloud/PowerWeChat/v3 v3.4.21/go.mod h1:boWl2cwbgXt1AbrYTWMXs9Ebby6ecbJ1CyNVRaNVqUY=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
@@ -125,11 +125,11 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.12.7 h1:CQU8pxOy9HToxhndH0Kx/S1qU/CuS9GnKYrGioDcU1Q=
github.com/bytedance/sonic v1.12.7/go.mod h1:tnbal4mxOMju17EGfknm2XyYcpyCnIROYOEYuemj13I=
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/casbin/casbin/v2 v2.103.0 h1:dHElatNXNrr8XcseUov0ZSiWjauwmZZE6YMV3eU1yic=
github.com/casbin/casbin/v2 v2.103.0/go.mod h1:Ee33aqGrmES+GNL17L0h9X28wXuo829wnNUnS0edAco=
github.com/casbin/gorm-adapter/v3 v3.32.0 h1:Au+IOILBIE9clox5BJhI2nA3p9t7Ep1ePlupdGbGfus=
@@ -177,16 +177,18 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc=
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6/go.mod h1:YxOVT5+yHzKvwhsiSIWmbAYM3Dr9AEEbER2dVayfBkg=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gammazero/toposort v0.1.1 h1:OivGxsWxF3U3+U80VoLJ+f50HcPU1MIqE1JlKzoJ2Eg=
github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw=
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
@@ -223,13 +225,13 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.7.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
@@ -275,8 +277,9 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
@@ -363,8 +366,8 @@ github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
@@ -437,8 +440,8 @@ github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
@@ -547,8 +550,8 @@ github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPD
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
@@ -602,8 +605,8 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA=
golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -627,8 +630,8 @@ golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -663,8 +666,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -698,8 +701,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -718,8 +721,9 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -758,8 +762,8 @@ golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -796,8 +800,9 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
@@ -832,8 +837,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -878,8 +883,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -65,6 +65,7 @@ func RegisterTables() {
example.ExaAttachmentCategory{},
app.Banner{},
app.Order{},
)
if err != nil {
global.GVA_LOG.Error("register table failed", zap.Error(err))

View File

@@ -1,18 +1,19 @@
package initialize
import (
"git.echol.cn/loser/lckt/plugin/customerservice"
"git.echol.cn/loser/lckt/plugin/picturelibrary"
"net/http"
"os"
"git.echol.cn/loser/lckt/docs"
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/middleware"
"git.echol.cn/loser/lckt/plugin/customerservice"
"git.echol.cn/loser/lckt/plugin/picturelibrary"
"git.echol.cn/loser/lckt/router"
gc "github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"net/http"
"os"
"time"
)
type justFilesFilesystem struct {
@@ -38,6 +39,17 @@ func (fs justFilesFilesystem) Open(name string) (http.File, error) {
func Routers() *gin.Engine {
Router := gin.New()
Router.Use(gin.Recovery())
Router.Use(middleware.AllCors())
Router.Use(gc.New(gc.Config{
AllowAllOrigins: true, // 允许所有来源
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"*"}, // 允许所有自定义header
ExposeHeaders: []string{"Content-Length", "Content-Type"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
if gin.Mode() == gin.DebugMode {
Router.Use(gin.Logger())
}
@@ -55,7 +67,7 @@ func Routers() *gin.Engine {
Router.StaticFS(global.GVA_CONFIG.Local.StorePath, justFilesFilesystem{http.Dir(global.GVA_CONFIG.Local.StorePath)}) // Router.Use(middleware.LoadTls()) // 如果需要使用https 请打开此中间件 然后前往 core/server.go 将启动模式 更变为 Router.RunTLS("端口","你的cre/pem文件","你的key文件")
// 跨域,如需跨域可以打开下面的注释
// Router.Use(middleware.Cors()) // 直接放行全部跨域请求
// Router.Use(middleware.AllCors()) // 直接放行全部跨域请求
// Router.Use(middleware.CorsByRules()) // 按照配置的规则放行跨域请求
// global.GVA_LOG.Info("use middleware cors")
docs.SwaggerInfo.BasePath = global.GVA_CONFIG.System.RouterPrefix
@@ -64,12 +76,13 @@ func Routers() *gin.Engine {
// 方便统一添加路由组前缀 多服务器上线使用
PublicGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
PublicGroup.Use(middleware.Cors()) // 直接放行全部跨域请求
PublicGroup.Use(middleware.AllCors()) // 直接放行全部跨域请求
PrivateGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()).Use(middleware.Cors())
PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()).Use(middleware.AllCors())
AppAuthGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
//AppAuthGroup.Use(middleware.UserJWTAuth()).Use(middleware.Cors())
AppAuthGroup.Use(middleware.AllCors())
//AppAuthGroup.Use(middleware.UserJWTAuth()).Use(middleware.AllCors())
{
// 健康监测
PublicGroup.GET("/health", func(c *gin.Context) {
@@ -106,6 +119,7 @@ func Routers() *gin.Engine {
{
appRouter.InitAppUserRouter(AppAuthGroup, PublicGroup)
appRouter.InitBannerRouter(PrivateGroup, PublicGroup) // Banner相关路由
appRouter.InitOrderRouter(AppAuthGroup) // 订单相关路由
}
//插件路由安装

View File

@@ -44,5 +44,6 @@ func main() {
}
wechat.InitWeOfficial() // 初始化微信公众号SDK
wechat.InitWechatPay() // 初始化微信支付SDK
core.RunWindowsServer()
}

View File

@@ -71,3 +71,19 @@ func checkCors(currentOrigin string) *config.CORSWhitelist {
}
return nil
}
func AllCors() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Writer.Header().Set("Access-Control-Allow-Headers", "*")
c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length, Content-Type")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}

25
model/app/order.go Normal file
View File

@@ -0,0 +1,25 @@
package app
import "git.echol.cn/loser/lckt/global"
type Order struct {
global.GVA_MODEL
OrderNo string `gorm:"column:order_no;type:varchar(24);comment:订单编号;NOT NULL" json:"order_no"`
UserId uint64 `gorm:"column:user_id;type:bigint(20) unsigned;comment:用户ID;NOT NULL" json:"user_id"`
OrderType int `gorm:"column:order_type;type:int(11);comment:订单类型 |1 课程|2 vip|;NOT NULL" json:"order_type"`
ArticleId uint `gorm:"column:article_id;type:bigint(20) unsigned;default:0;comment:文章ID;NOT NULL" json:"article_id"`
Title string `gorm:"column:title;type:varchar(255);comment:订单标题;NOT NULL" json:"title"`
Name string `gorm:"column:name;type:varchar(255);comment:名称;NOT NULL" json:"name"`
Price int64 `gorm:"column:price;type:int(11) unsigned;default:0;comment:订单价格;NOT NULL" json:"price"`
Phone string `gorm:"column:phone;type:varchar(11);comment:手机号;NOT NULL" json:"phone"`
TeacherId uint64 `gorm:"column:teacher_Id;type:bigint(20);comment:教师Id;NOT NULL" json:"teacher_Id"`
Status int `gorm:"column:status;type:int(11);default:1;comment:订单状态 |1 未付款|2 已付款|3 已过期|;NOT NULL" json:"status"`
Desc string `gorm:"column:desc;type:varchar(24);comment:订单描述;NOT NULL" json:"desc"`
OpenId string `gorm:"column:open_id;type:varchar(64);comment:用户OpenId;NOT NULL" json:"open_id"`
PayType int `gorm:"column:pay_type;type:int(11);default:1;comment:支付方式 |1 微信|2 支付宝|3 余额|;NOT NULL" json:"pay_type"`
}
// TableName Order表
func (Order) TableName() string {
return "app_order"
}

View File

@@ -0,0 +1,23 @@
package request
import common "git.echol.cn/loser/lckt/model/common/request"
type BalancePay struct {
UserId uint `json:"user_id" binding:"required"` // 用户ID
OrderNo string `json:"order_no" binding:"required"` // 订单号
OrderId uint `json:"order_id" binding:"required"` // 订单ID
}
type GetOrderList struct {
common.PageInfo
Status int `json:"status" form:"status"` // 订单状态
}
type PayReq struct {
UserId uint64 `json:"user_id"` // 用户ID
OrderId uint `json:"order_id"`
Mode string `json:"mode"` // h5 jsapi
WxCode string `json:"wx_code"`
PayMethod int `json:"pay_method" vd:"$>0; msg:'参数不能为空'"`
OrderNo string `json:"order_no" vd:"@:len($)>0; msg:'订单编号参数不能为空'"`
}

View File

@@ -0,0 +1,18 @@
package app
import "git.echol.cn/loser/lckt/global"
type TeacherApply struct {
global.GVA_MODEL
UserId uint `json:"userId"`
Reason string `json:"reason" gorm:"type:varchar(255);comment:申请理由"`
Status int8 `json:"status" gorm:"default:0;comment:申请状态 0 待审核 1 通过 2 拒绝"`
ExpectRate string `json:"expectRate" gorm:"type:varchar(255);comment:期望分成比例"`
Phone string `json:"phone" gorm:"type:varchar(20);comment:联系电话"`
Nickname string `json:"nickname" gorm:"type:varchar(255);comment:讲师名称"`
Note string `json:"note" gorm:"type:varchar(255);comment:审核备注"`
}
func (TeacherApply) TableName() string {
return "teacher_apply"
}

View File

@@ -11,8 +11,11 @@ type Article struct {
Content string `json:"content" gorm:"comment:文章内容;type:longtext"`
CoverImg string `json:"coverImg" gorm:"comment:文章封面图"`
TeacherId int `json:"teacherId" gorm:"comment:讲师ID"`
Price int64 `json:"price" gorm:"comment:文章价格(单位为分)"`
TeacherName string `json:"teacherName" gorm:"comment:讲师名称"`
Price int64 `json:"price" gorm:"comment:文章价格(单位为分)"`
IsFree int `json:"isFree" gorm:"comment:是否免费;default:0"` // 是否免费 0-否 1-是
// 分类ID
CategoryId int `json:"categoryId" gorm:"comment:分类ID"`
}
// TableName 文章表

View File

@@ -4,6 +4,10 @@ import "git.echol.cn/loser/lckt/model/common/request"
type GetList struct {
request.PageInfo
Title string `json:"title" form:"title"` // 文章标题
// 分类ID
CategoryId int `json:"categoryId" form:"categoryId"` // 分类ID
TeacherId int `json:"teacherId" form:"teacherId"` // 讲师ID
}
type DeleteIds struct {

View File

@@ -1,5 +1,18 @@
package vo
type ArticleListVo struct {
ID int `json:"id" gorm:"comment:文章ID"`
Title string `json:"title" gorm:"comment:文章标题"`
Desc string `json:"desc" gorm:"comment:文章描述"`
//Content string `json:"content" gorm:"comment:文章内容"`
CoverImg string `json:"coverImg" gorm:"comment:文章封面图"`
Price int64 `json:"price" gorm:"comment:文章价格(单位为分)"`
TeacherId int `json:"teacherId" gorm:"comment:讲师ID"`
TeacherName string `json:"teacherName" gorm:"comment:讲师名称"`
TeacherAvatar string `json:"teacherAvatar" gorm:"comment:讲师头像"`
IsFree int `json:"isFree" gorm:"comment:是否免费;default:0"` // 是否免费 0-否 1-是
}
type ArticleVo struct {
ID int `json:"id" gorm:"comment:文章ID"`
Title string `json:"title" gorm:"comment:文章标题"`
@@ -10,4 +23,6 @@ type ArticleVo struct {
TeacherId int `json:"teacherId" gorm:"comment:讲师ID"`
TeacherName string `json:"teacherName" gorm:"comment:讲师名称"`
TeacherAvatar string `json:"teacherAvatar" gorm:"comment:讲师头像"`
IsFree int `json:"isFree" gorm:"comment:是否免费;default:0"` // 是否免费 0-否 1-是
IsBuy int `json:"isBuy" gorm:"comment:是否购买;default:0"` // 是否购买 0-否 1-是
}

View File

@@ -7,7 +7,7 @@ type SendCodeReq struct {
}
type CodeLoginReq struct {
Phone string `json:"phone" form:"phone" vd:"@:len($)>0; msg:'请输入手机号码'"`
Phone string `json:"phone" form:"phone"`
Code string `json:"code" form:"code" vd:"@:len($)>0; msg:'请输入短信验证码'"`
}
@@ -37,6 +37,8 @@ type BindWechatReq struct {
type BindPhoneReq struct {
Id int `json:"id" form:"id" vd:"@:len($)>0; msg:'用户ID不能为空'"`
Phone string `json:"phone" form:"phone" vd:"@:len($)>0; msg:'手机号码不能为空'"`
Code string `json:"code" form:"code" vd:"@:len($)>0; msg:'验证码不能为空'"`
PassWord string `json:"password" form:"password" vd:"@:len($)>0; msg:'密码不能为空'"`
}
type GetUserListReq struct {
@@ -62,6 +64,21 @@ type RegisterReq struct {
Phone string `json:"phone" form:"phone" vd:"@:len($)>0; msg:'手机号码不能为空'"`
Password string `json:"password" form:"password" vd:"@:len($)>0; msg:'密码不能为空'"`
NickName string `json:"nick_name" form:"nick_name"`
UserType int8 `json:"user_type" form:"user_type" vd:"@:len($)>0; msg:'用户类型不能为空'"`
UserLabel int64 `json:"user_label" form:"user_label" vd:"@:len($)>0; msg:'用户标签不能为空'"`
Code string `json:"code" form:"code" vd:"@:len($)>0; msg:'验证码不能为空'"`
UserType int8 `json:"user_type" form:"user_type"`
UserLabel int64 `json:"user_label" form:"user_label"`
}
type ApplyTeacherReq struct {
UserId int `json:"user_id" form:"user_id" vd:"@:len($)>0; msg:'用户ID不能为空'"`
Phone string `json:"phone" form:"phone" vd:"@:len($)>0; msg:'手机号码不能为空'"`
NickName string `json:"nick_name" form:"nick_name" vd:"@:len($)>0; msg:'昵称不能为空'"`
Desc string `json:"desc" form:"desc" vd:"@:len($)>0; msg:'申请理由不能为空'"`
ExpectRate string `json:"expect_rate" form:"expect_rate" vd:"@:len($)>0; msg:'期望分成比例不能为空'"`
}
type GetTeacherApplyListReq struct {
request.PageInfo
Phone string `json:"phone" form:"phone"`
Nickname string `json:"nickname" form:"nickname"`
}

Binary file not shown.

View File

@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEKzCCAxOgAwIBAgIUWaiR+0A+x6HPIJDbnI7HBL1DsQEwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjUwNzIzMDU1MDU5WhcNMzAwNzIyMDU1MDU5WjCBhDETMBEGA1UEAwwK
MTY0Njg3NDc1MzEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTAwLgYDVQQL
DCfmtbflj6PpvpnljY7pk4HlnZrmiJDnlLXlrZDllYbliqHllYbooYwxCzAJBgNV
BAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMUD4rf7ct0zUxJKGww+8c38L/IBx9yeNpOc7lav+CEwqZvieesy
p1I2hbY+hwOZm23//0Vxcnl6fkr5wdidE/RsN808sGkYsetJ6q7LZsgsxnbaQd7w
rBc4GlOh1Q4teErbjNTUMuozYzikEjMjgZMxs7i5WJnFMMVvgJS4681UKH8MQG93
BnaaD5zEyofAnOov7mvC0KZ0RpqwiLdUsMaPzYILzccOv7Tj82Z/qLw8AFcI+7L1
mq8trZxmSPXP5Fypz+1VKLKR9MaELUuQLPouewDjXkKTBnxkhoWow6huuz3qyUUo
i7uJtnmDn6Eu+60ch61BB5ws3rhWDpltR/0CAwEAAaOBuTCBtjAJBgNVHRMEAjAA
MAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2
Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJD
MDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJC
MjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQBk
WmviQM60S/l4I4/RtQWtiZS+85fk2ePVSk5gBUS27W4yTw+aXy5RYaCLcOVXTXje
PpAfE7QvkjZxMg/k0SJR0/NbgDNF6xuCbMj78+czWVCkqHwPjY4hWRdsM9s44Orp
lwFrG/UNRhPgXcdwNQj8MNzy3nGeO0HWbJFBWcuRhSSwXpxv3Sh6pZviL3QA+lrV
7QB7d0fEu8sSGbwnIB0oXl7y78l2/D20p57TKHsq5IPfQfDdExNXKV60ikMt/MdP
C4ygbeRpSAobr6qMTXcpWlbH3KfIPOUFny3nkN8C6lm//QUl7ynOXkECcmIHY6XF
yMx70ORdoBl1DaZ369P9
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFA+K3+3LdM1MS
ShsMPvHN/C/yAcfcnjaTnO5Wr/ghMKmb4nnrMqdSNoW2PocDmZtt//9FcXJ5en5K
+cHYnRP0bDfNPLBpGLHrSequy2bILMZ22kHe8KwXOBpTodUOLXhK24zU1DLqM2M4
pBIzI4GTMbO4uViZxTDFb4CUuOvNVCh/DEBvdwZ2mg+cxMqHwJzqL+5rwtCmdEaa
sIi3VLDGj82CC83HDr+04/Nmf6i8PABXCPuy9ZqvLa2cZkj1z+Rcqc/tVSiykfTG
hC1LkCz6LnsA415CkwZ8ZIaFqMOobrs96slFKIu7ibZ5g5+hLvutHIetQQecLN64
Vg6ZbUf9AgMBAAECggEBAI4q66PQM2cj7kI4b6Q6l8sIvKBqYIr3MHL8v5CWkvuA
XiQ7Hbd3af6NkZedL1iNs/eAz/iQkQbQOepoqFVjpE6w+OOFc9ejFmCvikZwSM8S
YHTLstTp34Ux2u2WzmPYtAFwxQOfzM3sHyF0ZB269Xn+V65pMWJlRXhzqdmoR6B2
YYLzHVBZAbgj7IlS+f1WPkkbrb3BIs71tYyPS3Ng6nhGgXrnqmE5fhm93wR9KR5R
RcgzFGH/8qD2LndmJz8tYtjvlq016Pp40VrYabIrvX6apglayShLQ+2XUe8VKH2Q
dQPIKL3R0ZeDeqwn4PTMclBYd/nXFSbIQndufTDY84ECgYEA8RTcCPOFYkoDeQIr
yKqWo1P1cKsB/bGzvLmo+FqFQij5lmj9iDdvpSQJ58Uj+TFy5qNRT6VueF8U4pNd
Fmcbw05cH+9NzAXji6aYWCargbmhW4L+rzub+TQmdAfqXbBknVgrLenY4LFnFFm1
SIdJRid3vCwiRuhcjecg4FasR+kCgYEA0TTw4ZGxPCX5qt00PUFvuGpqFxFqZ7Ah
wapwQTUY48WeC0Pg7Iq6ipnhjggkgYLkGc1klmTwJXKV0acIS5/DNclepMWZPwIc
+GcKclQsy8MHm+L2tPGBQosRGiSrXi5LzBIQsYSyhBRDUSqu+yj7TTd2XqpUkLAw
qdrWiIjVBvUCgYAOkh0uaVGBfEmzcZ8l1LGgE339HkjThX8AhBQjVo1BT2quXZAd
QIR97ayvlmmzMPrp16sdbjk8Czse6pswtHCoID9PKs5/60cydJI2mbe58nc/Ka6s
9qRZrn44exX+LaAXJnINp1mVUwOQ5k8foBWcqNwCwoQb1wVpCjQhevuUqQKBgQC1
SA+3Fr0iprF6iqWastoxThzSEmhGowwNOjh9eJoxvOsfTdlYfzn3ojIeFhY0F4y6
gw03eQ3TFUCXZAq/JRhNwkl9tC//tkAOS5N00FXk1wH/5aLr1h2w4LqYEdBhEvLh
SYInoRnjc3+FlNv9jVx9Y6Lxkt0mZ1YzyQp/UzptBQKBgFTOonik05rMHGlEg9Ng
unjDAEK+7tXhM/RKxWDs7pZeGbdl1c/NDOX4wIbuK2cCkV0v/qfg6Ex2Obf5rqM4
R5lrs9y30rZbwmfms8G3cimTAsOcLqObKkBED+g07t+3bw44JXLwYLPm/z//bIcI
fkxyxKL5ZJhP3/bPJJj8UAq3
-----END PRIVATE KEY-----

View File

@@ -5,7 +5,9 @@ import api "git.echol.cn/loser/lckt/api/v1"
type RouterGroup struct {
UserRouter
BannerRouter
OrderRouter
}
var userApi = api.ApiGroupApp.AppApiGroup.AppUserApi
var bannerApi = api.ApiGroupApp.AppApiGroup.BannerApi
var orderApi = api.ApiGroupApp.AppApiGroup.OrderApi

18
router/app/order.go Normal file
View File

@@ -0,0 +1,18 @@
package app
import "github.com/gin-gonic/gin"
type OrderRouter struct{}
// InitOrderRouter 初始化订单路由
func (r *OrderRouter) InitOrderRouter(AppRouter *gin.RouterGroup) {
appRouter := AppRouter.Group("app_order")
{
appRouter.POST("", orderApi.CreateOrder) // 创建订单
appRouter.POST("/wechat/pay", orderApi.PayOrder) // 微信支付订单
appRouter.GET("/list", orderApi.GetOrderList) // 获取订单列表
appRouter.GET(":id", orderApi.GetOrderDetail) // 获取订单详情
appRouter.POST("/balance/pay", orderApi.BalancePay) // 余额支付
appRouter.POST("/notify", orderApi.NotifyOrder) // 微信支付回调通知
}
}

View File

@@ -11,11 +11,17 @@ func (s *UserRouter) InitAppUserRouter(AppAuthGroup, PublicRouter *gin.RouterGro
publicRouter := PublicRouter.Group("h5_user")
{
appUserRouter.GET("/info", userApi.GetUserInfo) // 获取用户信息
//申请成为讲师
appUserRouter.POST("/applyTeacher", userApi.ApplyTeacher) // 申请成为讲师
appUserRouter.GET("/applyTeacher", userApi.GetTeacherApply) // 获取教师列表
}
{
publicRouter.POST("wxLogin", userApi.WechatLogin) // 微信登录
publicRouter.POST("bindWX", userApi.BindWechat) // 绑定微信
publicRouter.POST("bindPhone", userApi.BindPhone) // 获取用户信息
publicRouter.POST("pwdlogin", userApi.PwdLogin) // 密码登录
publicRouter.POST("sms/send", userApi.SendCode) // 发送短信验证码
publicRouter.POST("login", userApi.Login) // 短信验证码登录
publicRouter.POST("register", userApi.Register) // 注册
}
}

View File

@@ -16,14 +16,15 @@ func (s *ArticleRouter) InitBotRouter(Router *gin.RouterGroup, PublicRouter *gin
articleRouter.POST("", artApi.Create) // 新建文章
articleRouter.DELETE("", artApi.Delete) // 批量删除文章
articleRouter.PUT("", artApi.Update) // 更新文章
articleRouter.GET("list", artApi.List) // 获取文章列表
articleRouter.GET("", artApi.ById) // 文章开放接口
}
{
articleRouterWithoutRecord.GET(":id", artApi.ById) // 根据ID获取文章
}
{
articleRouterWithoutAuth.GET("list", artApi.List) // 获取文章列表
articleRouterWithoutAuth.GET("", artApi.ById) // 文章开放接口
articleRouterWithoutAuth.GET("app/list", artApi.APPGetList) // 文章公开接口
articleRouterWithoutAuth.GET("app/:id", artApi.AppById) // 文章开放接口
}
}

View File

@@ -17,5 +17,7 @@ func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup, PublicRouter *gin.R
userRouter.PUT("status/:id", userApi.SetUserStatus) // 更新用户状态
userRouter.GET(":id", userApi.GetUserById) // 获取用户信息
userRouter.GET("/teachers", userApi.GetTeachers) // 获取教师列表
userRouter.GET("/teacherApplyList", userApi.GetTeacherApplyList) // 获取教师信息
userRouter.PUT("/teacherApply/status", userApi.UpdateTeacherApplyStatus) // 更新教师申请状态
}
}

View File

@@ -3,4 +3,5 @@ package app
type ServiceGroup struct {
AppUserService
BannerService
OrderService
}

160
service/app/order.go Normal file
View File

@@ -0,0 +1,160 @@
package app
import (
"fmt"
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/model/app"
"git.echol.cn/loser/lckt/model/app/request"
"git.echol.cn/loser/lckt/model/user"
"git.echol.cn/loser/lckt/utils/wechat"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type OrderService struct{}
// Pay 发起支付
func (s *OrderService) Pay(p request.PayReq, ctx *gin.Context) (interface{}, error) {
order := app.Order{}
err := global.GVA_DB.Where("id = ? AND order_no = ?", p.OrderId, p.OrderNo).First(&order).Error
if err != nil {
global.GVA_LOG.Error("查询订单失败", zap.Error(err))
return "", err
}
if order.Status == 3 {
global.GVA_LOG.Error("订单已过期", zap.Int64("order_id", int64(order.ID)))
return "", fmt.Errorf("订单已过期")
}
if p.Mode == "h5" {
payConf, err := wechat.H5Pay(&order, ctx)
if err != nil {
global.GVA_LOG.Error("微信支付订单失败", zap.Error(err))
return "", err
}
return payConf, nil
} else if p.Mode == "jsapi" {
payConf, err := wechat.JSAPIPay(&order, ctx)
if err != nil {
global.GVA_LOG.Error("微信支付订单失败", zap.Error(err))
return "", err
}
return payConf, nil
}
return "", nil
}
func (s *OrderService) Create(o *app.Order) (*app.Order, error) {
// 生成订单号
o.OrderNo = wechat.GenerateOrderNum()
// 查询订单商品价格
price := 0
if o.OrderType == 1 {
err := global.GVA_DB.Table("article").Select("price").Where("id = ?", o.ArticleId).Scan(&price).Error
if err != nil {
global.GVA_LOG.Error("查询商品价格失败", zap.Error(err))
return nil, err
}
} else {
err := global.GVA_DB.Table("lckt_vip").Select("price").Where("id = ?", o.ArticleId).Scan(&price).Error
if err != nil {
global.GVA_LOG.Error("查询VIP价格失败", zap.Error(err))
return nil, err
}
}
o.Price = int64(price)
o.Status = 1 // 设置订单状态为未付款
// 设置openid
openId := ""
err := global.GVA_DB.Table("app_user").Where("id = ?", o.UserId).Select("open_id").Scan(&openId).Error
if err != nil {
global.GVA_LOG.Error("查询用户OpenID失败,请检查微信是否绑定", zap.Error(err))
return nil, fmt.Errorf("查询用户OpenID失败,请检查微信是否绑定")
}
o.OpenId = openId
err = global.GVA_DB.Create(&o).Error
if err != nil {
global.GVA_LOG.Error("创建订单失败", zap.Error(err))
return nil, err
}
return o, nil
}
func (s *OrderService) GetOrderDetail(id string) (order app.Order, err error) {
err = global.GVA_DB.Where("id = ?", id).First(&order).Error
if err != nil {
global.GVA_LOG.Error("获取订单详情失败", zap.Error(err))
return app.Order{}, err
}
return order, nil
}
func (s *OrderService) BalancePay(p request.BalancePay) error {
order := app.Order{}
err := global.GVA_DB.Where("id = ? AND order_no = ?", p.OrderId, p.OrderNo).First(&order).Error
if err != nil {
global.GVA_LOG.Error("查询订单失败", zap.Error(err))
return err
}
if order.Status != 1 {
global.GVA_LOG.Error("订单状态错误,无法支付", zap.Int("status", order.Status))
return err
}
// 检查用户余额是否足够
var user user.User
err = global.GVA_DB.Where("id = ?", p.UserId).Select("id,balance").First(&user).Error
if err != nil {
global.GVA_LOG.Error("查询用户信息失败", zap.Error(err))
return err
}
// 将user.Balance转为int64类型进行比较
balance := int64(user.Balance)
if balance < order.Price/100 { // 订单价格是以分为单位存储的
global.GVA_LOG.Error("用户余额不足", zap.Int64("balance", balance), zap.Int64("order_price", order.Price))
return fmt.Errorf("用户余额不足")
}
// 扣除用户余额
balance -= order.Price / 100
err = global.GVA_DB.Model(&user).Where("id = ?", p.UserId).Update("balance", balance).Error
if err != nil {
global.GVA_LOG.Error("扣除用户余额失败", zap.Error(err))
return err
}
// 更新订单状态为已付款
order.Status = 2
err = global.GVA_DB.Model(&order).Where("id = ?", order.ID).Update("status", 2).Error
if err != nil {
global.GVA_LOG.Error("更新订单状态失败", zap.Error(err))
return err
}
global.GVA_LOG.Info("余额支付成功", zap.Int64("user_id", int64(p.UserId)), zap.String("order_no", order.OrderNo))
return nil
}
func (s *OrderService) GetOrderList(p request.GetOrderList, id uint) (orders []app.Order, total int64, err error) {
limit := p.PageSize
offset := p.PageSize * (p.Page - 1)
db := global.GVA_DB.Model(&app.Order{}).Where("user_id = ?", id)
if p.Keyword != "" {
db = db.Where("title LIKE ? ", "%"+p.Keyword+"%")
}
if p.Status != 0 {
db = db.Where("status = ?", p.Status)
}
err = db.Count(&total).Error
err = db.Limit(limit).Offset(offset).Order("created_at desc").Find(&orders).Error
if err != nil {
global.GVA_LOG.Error("获取订单列表失败", zap.Error(err))
return nil, 0, err
}
return
}

View File

@@ -3,12 +3,15 @@ package app
import (
"fmt"
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/model/app"
"git.echol.cn/loser/lckt/model/app/vo"
"git.echol.cn/loser/lckt/model/user"
"git.echol.cn/loser/lckt/model/user/request"
utils2 "git.echol.cn/loser/lckt/utils"
"github.com/ArtisanCloud/PowerSocialite/v3/src/providers"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
type AppUserService struct{}
@@ -110,3 +113,111 @@ func (u *AppUserService) GetUserInfo(id uint) (info vo.UserInfo, err error) {
}
return
}
func (u *AppUserService) Register(p request.RegisterReq) (userInfo user.User, err error) {
err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
// 1. 判断用户是否存在
var count int64
if err = tx.Model(&userInfo).Where("phone = ?", p.Phone).Count(&count).Error; err != nil {
global.GVA_LOG.Error("查询用户失败", zap.Error(err))
return err
}
if count > 0 {
global.GVA_LOG.Error("用户已存在")
return fmt.Errorf("用户已存在")
}
// 2. 如果用户不存在,则创建用户
password, _ := bcrypt.GenerateFromPassword([]byte(p.Password), bcrypt.DefaultCost)
userInfo = user.User{
Phone: p.Phone,
Password: string(password),
NickName: p.NickName,
UserLabel: 1,
Status: 1,
UserType: p.UserType,
Avatar: "https://ui-avatars.com/api/?name=" + p.NickName + "&background=0081ff&color=ffffff&rounded=true",
}
// 保存用户
if err = tx.Save(&userInfo).Error; err != nil {
global.GVA_LOG.Error("创建用户失败", zap.Error(err))
return err
}
// 生成邀请码
code := utils2.GenerateInviteCode(userInfo.ID)
userInfo.InviteCode = &code
if err = tx.Model(&userInfo).Where("id = ?", userInfo.ID).Update("invite_code", code).Error; err != nil {
global.GVA_LOG.Error("更新邀请码失败", zap.Error(err))
return err
}
return nil
})
return
}
func (u *AppUserService) ApplyTeacher(p app.TeacherApply) (err error) {
if err = global.GVA_DB.Save(&p).Error; err != nil {
global.GVA_LOG.Error("保存申请信息失败", zap.Error(err))
return err
}
return nil
}
func (u *AppUserService) GetTeacherApplyStatus(id uint) (teacherApply app.TeacherApply, err error) {
// 获取最后一条申请记录
err = global.GVA_DB.Model(&app.TeacherApply{}).Where("user_id = ?", id).Last(&teacherApply).Error
if err != nil {
global.GVA_LOG.Error("查询申请记录失败", zap.Error(err))
return teacherApply, err
}
return
}
// BindWechat 手机登录用户绑定微信
func (u *AppUserService) BindWechat(p request.BindWechatReq) (*user.User, error) {
var userInfo user.User
err := global.GVA_DB.Model(&user.User{}).Where("id = ?", p.Id).First(&userInfo).Error
if err != nil {
global.GVA_LOG.Error("查询用户失败", zap.Error(err))
return nil, err
}
if userInfo.OpenId != "" {
global.GVA_LOG.Error("用户已绑定微信")
return nil, fmt.Errorf("用户已绑定微信")
}
userInfo.OpenId = p.Openid
err = global.GVA_DB.Save(&userInfo).Error
if err != nil {
global.GVA_LOG.Error("绑定微信失败", zap.Error(err))
return nil, err
}
return &userInfo, nil
}
// BindPhone 微信登录用户绑定手机号
func (u *AppUserService) BindPhone(p request.BindPhoneReq) (*user.User, error) {
var userInfo user.User
err := global.GVA_DB.Model(&user.User{}).Where("id = ?", p.Id).First(&userInfo).Error
if err != nil {
global.GVA_LOG.Error("查询用户失败", zap.Error(err))
return nil, err
}
userInfo.Phone = p.Phone
pwd, _ := bcrypt.GenerateFromPassword([]byte(p.PassWord), bcrypt.DefaultCost)
userInfo.Password = string(pwd)
err = global.GVA_DB.Save(&userInfo).Error
if err != nil {
global.GVA_LOG.Error("绑定微信失败", zap.Error(err))
return nil, err
}
return &userInfo, nil
}

View File

@@ -2,8 +2,11 @@ package article
import (
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/model/app"
"git.echol.cn/loser/lckt/model/article"
"git.echol.cn/loser/lckt/model/article/request"
"git.echol.cn/loser/lckt/model/article/vo"
"go.uber.org/zap"
)
type ArticleService struct{}
@@ -39,3 +42,64 @@ func (ArticleService) GetArticleList(pageInfo request.GetList) (list []article.A
err = db.Limit(limit).Offset(offset).Find(&list).Error
return
}
func (s ArticleService) APPGetArticleList(pageInfo request.GetList) (list []vo.ArticleListVo, total int64, err error) {
limit := pageInfo.PageSize
offset := pageInfo.PageSize * (pageInfo.Page - 1)
db := global.GVA_DB.Model(&article.Article{})
err = db.Count(&total).Error
if err != nil {
return
}
if pageInfo.Title != "" {
db = db.Where("title LIKE ?", "%"+pageInfo.Title+"%")
}
if pageInfo.CategoryId != 0 {
db = db.Where("category_id = ?", pageInfo.CategoryId)
}
if pageInfo.TeacherId != 0 {
db = db.Where("teacher_id = ?", pageInfo.TeacherId)
}
if pageInfo.Keyword != "" {
db = db.Where("title LIKE ? OR article.desc LIKE ? OR teacher_name LIKE ?", "%"+pageInfo.Keyword+"%", "%"+pageInfo.Keyword+"%", "%"+pageInfo.Keyword+"%")
}
err = db.Limit(limit).Offset(offset).Omit("teacher_avatar").Order("created_at desc").Find(&list).Error
for i, a := range list {
global.GVA_DB.Table("app_user").Select("avatar").Where("id = ?", a.TeacherId).Scan(&list[i].TeacherAvatar)
}
return
}
func (s ArticleService) APPGetArticle(id string, userId uint) (article *vo.ArticleVo, err error) {
err = global.GVA_DB.Table("article").Where("id = ?", id).First(&article).Error
if err != nil {
global.GVA_LOG.Error("获取文章失败", zap.Error(err))
return
}
article.IsBuy = 1
global.GVA_DB.Table("app_user").Select("avatar").Where("id = ?", article.TeacherId).Scan(&article.TeacherAvatar)
// 判断是否免费
if article.IsFree == 0 {
// 如果不是免费文章,判断用户是否购买过
var count int64
err = global.GVA_DB.Model(&app.Order{}).Where("article_id = ? AND user_id = ? AND status = 2", id, userId).Count(&count).Error
if err != nil {
global.GVA_LOG.Error("查询用户购买记录失败", zap.Error(err))
return nil, err
}
if count == 0 {
// 用户没有购买过隐藏Content
article.Content = ""
article.IsBuy = 0 // 设置为未购买
}
}
return
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/model/app"
"git.echol.cn/loser/lckt/model/app/vo"
common "git.echol.cn/loser/lckt/model/common/request"
"git.echol.cn/loser/lckt/model/user"
@@ -36,18 +37,19 @@ func (u *UserService) SendCode(req request.SendCodeReq) (err error) {
result, err := rdb.Get(context.Background(), key).Result()
if result != "" {
global.GVA_LOG.Error("验证码发送过于频繁", zap.String("phone", req.Phone))
err = fmt.Errorf("验证码发送过于频繁,请稍后再试")
return
}
rand.New(rand.NewSource(time.Now().UnixNano()))
verifyCode := fmt.Sprintf("%06v", rand.Int31n(1000000))
if ok := sms.SendSMS(req.Phone, verifyCode); !ok {
if ok := sms.DxbSendSMS(req.Phone, verifyCode); !ok {
global.GVA_LOG.Error("发送验证码失败")
return
}
//
if err = rdb.Set(context.Background(), key, verifyCode, time.Duration(global.GVA_CONFIG.SMS.ExpireTime)*time.Second).Err(); err != nil {
if err = rdb.Set(context.Background(), key, verifyCode, 5*time.Minute).Err(); err != nil {
global.GVA_LOG.Error("设置验证码缓存失败", zap.Error(err))
return
}
@@ -177,3 +179,52 @@ func (u *UserService) GetTeachers(p common.PageInfo) (list []vo.UserInfo, total
err = db.Limit(limit).Offset(offset).Find(&list).Error
return
}
func (u *UserService) GetTeacherApplyList(p request.GetTeacherApplyListReq) (list []app.TeacherApply, total int64, err error) {
query := global.GVA_DB.Model(&app.TeacherApply{})
if p.Phone != "" {
query = query.Where("phone LIKE ?", "%"+p.Phone+"%")
}
if p.Nickname != "" {
query = query.Where("nick_name LIKE ?", "%"+p.Nickname+"%")
}
err = query.Count(&total).Error
if err != nil {
global.GVA_LOG.Error("查询申请列表总数失败", zap.Error(err))
return
}
err = query.Offset((p.Page - 1) * p.PageSize).Limit(p.PageSize).Order("create_at desc").Find(&list).Error
if err != nil {
global.GVA_LOG.Error("查询申请列表失败", zap.Error(err))
return
}
return
}
func (u *UserService) UpdateTeacherApplyStatus(p app.TeacherApply) (err error) {
err = global.GVA_DB.Save(&p).Error
if err != nil {
global.GVA_LOG.Error("更新教师申请状态失败", zap.Error(err))
return
}
// 如果申请通过,更新用户类型为教师
if p.Status == 1 {
var user user.User
err = global.GVA_DB.Model(&user).Where("id = ?", p.UserId).First(&user).Error
if err != nil {
global.GVA_LOG.Error("查询用户信息失败", zap.Error(err))
return
}
user.UserType = 2 // 设置为教师
err = global.GVA_DB.Save(&user).Error
if err != nil {
global.GVA_LOG.Error("更新用户类型失败", zap.Error(err))
return
}
}
return
}

View File

@@ -2,6 +2,7 @@ package test
import (
"fmt"
"git.echol.cn/loser/lckt/utils/sms"
"golang.org/x/crypto/bcrypt"
"math/rand"
"testing"
@@ -25,3 +26,16 @@ func TestPwd(t *testing.T) {
fmt.Println("密码正确")
}
}
func TestCode(t *testing.T) {
// 测试验证码生成
rand.New(rand.NewSource(time.Now().UnixNano()))
verifyCode := fmt.Sprintf("%06v", rand.Int31n(1000000))
test := sms.SendSMSTest("17754945397", verifyCode)
if test {
fmt.Println("短信发送成功")
} else {
fmt.Println("短信发送失败")
}
}

23
utils/rand_code.go Normal file
View File

@@ -0,0 +1,23 @@
package utils
import (
"crypto/md5"
"fmt"
"math/rand"
"time"
)
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
func GenerateInviteCode(userID uint) string {
rand.Seed(time.Now().UnixNano())
// 拼接用户ID和随机数
data := fmt.Sprintf("%d%d", userID, rand.Intn(1000000))
hash := md5.Sum([]byte(data))
code := ""
for i := 0; i < 6; i++ {
// 取哈希的前6位每位映射到字符集
code += string(charset[int(hash[i])%len(charset)])
}
return code
}

View File

@@ -2,10 +2,15 @@ package sms
// SMS短信服务
import (
"crypto/tls"
"encoding/json"
"fmt"
"git.echol.cn/loser/lckt/global"
"github.com/alibabacloud-go/tea/tea"
"go.uber.org/zap"
"net/http"
"net/url"
"strings"
util "github.com/alibabacloud-go/tea-utils/v2/service"
@@ -71,3 +76,67 @@ func SendSMS(phone string, code string) bool {
global.GVA_LOG.Info("短信[阿里云]", zap.String("发送成功", "手机号: "+phone))
return true
}
func SendSMSTest(phone, code string) bool {
endpoint := "https://dfsns.market.alicloudapi.com/data/send_sms"
templateID := "CST_ptdie100"
// 构造 POST 表单数据
form := url.Values{}
form.Set("content", fmt.Sprintf("code:%s", code))
form.Set("template_id", templateID)
form.Set("phone_number", phone)
// 创建 HTTP 请求
req, err := http.NewRequest("POST", endpoint, strings.NewReader(form.Encode()))
if err != nil {
return false
}
// 添加请求头
req.Header.Set("Authorization", "APPCODE "+"b8f46ced154b44c5a40a0a49a91e1634")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
// 创建 HTTP 客户端(跳过证书校验,模拟 curl -k
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
// 发送请求
resp, err := client.Do(req)
if err != nil {
return false
}
defer resp.Body.Close()
if resp.Status != "ok" {
return false
}
return true
}
func DxbSendSMS(phone, code string) bool {
// 内容 通过urlEncode编码
content := "【海口龙华铁坚成电子商务商行】您的验证码是" + code + "。如非本人操作,请忽略本短信"
// urlencode编码内容
content = url.QueryEscape(content)
api := "https://api.smsbao.com/sms?u=lchz5599&p=7ea114c87a224cd38a0d616b9be3faed&g=海口龙华铁坚成电子商务商行&m=" + phone + "&c=" + content
// 发送GET请求
resp, err := http.Get(api)
if err != nil {
global.GVA_LOG.Error("发送短信请求失败:", zap.Error(err))
return false
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
global.GVA_LOG.Info("发送短信请求成功,手机号:" + phone + ",验证码:" + code)
return true
} else {
global.GVA_LOG.Error("发送短信请求失败:", zap.Error(err))
return false
}
}

245
utils/wechat/pay.go Normal file
View File

@@ -0,0 +1,245 @@
package wechat
import (
gfmt "fmt"
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/model/app"
"github.com/ArtisanCloud/PowerLibs/v3/fmt"
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models"
"github.com/ArtisanCloud/PowerWeChat/v3/src/payment"
wxRequst "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/notify/request"
"github.com/ArtisanCloud/PowerWeChat/v3/src/payment/order/request"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"log"
"math/rand"
"time"
)
var WechatPay *payment.Payment
// InitWechatPay 初始化微信支付
func InitWechatPay() {
PaymentService, err := payment.NewPayment(&payment.UserConfig{
AppID: "wx3d21df18d7f8f9fc", // 小程序、公众号或者企业微信的appid
MchID: global.GVA_CONFIG.Pays[0].MchId, // 商户号 appID
MchApiV3Key: "1a3sd8561d5179Df152D4789aD38wG9s", // 微信V3接口调用必填
Key: "57s14dFG915486Sd5617f23d45f671Ad",
CertPath: `-----BEGIN CERTIFICATE-----
MIIEKzCCAxOgAwIBAgIUWaiR+0A+x6HPIJDbnI7HBL1DsQEwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjUwNzIzMDU1MDU5WhcNMzAwNzIyMDU1MDU5WjCBhDETMBEGA1UEAwwK
MTY0Njg3NDc1MzEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTAwLgYDVQQL
DCfmtbflj6PpvpnljY7pk4HlnZrmiJDnlLXlrZDllYbliqHllYbooYwxCzAJBgNV
BAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMUD4rf7ct0zUxJKGww+8c38L/IBx9yeNpOc7lav+CEwqZvieesy
p1I2hbY+hwOZm23//0Vxcnl6fkr5wdidE/RsN808sGkYsetJ6q7LZsgsxnbaQd7w
rBc4GlOh1Q4teErbjNTUMuozYzikEjMjgZMxs7i5WJnFMMVvgJS4681UKH8MQG93
BnaaD5zEyofAnOov7mvC0KZ0RpqwiLdUsMaPzYILzccOv7Tj82Z/qLw8AFcI+7L1
mq8trZxmSPXP5Fypz+1VKLKR9MaELUuQLPouewDjXkKTBnxkhoWow6huuz3qyUUo
i7uJtnmDn6Eu+60ch61BB5ws3rhWDpltR/0CAwEAAaOBuTCBtjAJBgNVHRMEAjAA
MAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2
Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJD
MDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJC
MjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQBk
WmviQM60S/l4I4/RtQWtiZS+85fk2ePVSk5gBUS27W4yTw+aXy5RYaCLcOVXTXje
PpAfE7QvkjZxMg/k0SJR0/NbgDNF6xuCbMj78+czWVCkqHwPjY4hWRdsM9s44Orp
lwFrG/UNRhPgXcdwNQj8MNzy3nGeO0HWbJFBWcuRhSSwXpxv3Sh6pZviL3QA+lrV
7QB7d0fEu8sSGbwnIB0oXl7y78l2/D20p57TKHsq5IPfQfDdExNXKV60ikMt/MdP
C4ygbeRpSAobr6qMTXcpWlbH3KfIPOUFny3nkN8C6lm//QUl7ynOXkECcmIHY6XF
yMx70ORdoBl1DaZ369P9
-----END CERTIFICATE-----
`, // 微信V2接口调用必填
KeyPath: `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFA+K3+3LdM1MS
ShsMPvHN/C/yAcfcnjaTnO5Wr/ghMKmb4nnrMqdSNoW2PocDmZtt//9FcXJ5en5K
+cHYnRP0bDfNPLBpGLHrSequy2bILMZ22kHe8KwXOBpTodUOLXhK24zU1DLqM2M4
pBIzI4GTMbO4uViZxTDFb4CUuOvNVCh/DEBvdwZ2mg+cxMqHwJzqL+5rwtCmdEaa
sIi3VLDGj82CC83HDr+04/Nmf6i8PABXCPuy9ZqvLa2cZkj1z+Rcqc/tVSiykfTG
hC1LkCz6LnsA415CkwZ8ZIaFqMOobrs96slFKIu7ibZ5g5+hLvutHIetQQecLN64
Vg6ZbUf9AgMBAAECggEBAI4q66PQM2cj7kI4b6Q6l8sIvKBqYIr3MHL8v5CWkvuA
XiQ7Hbd3af6NkZedL1iNs/eAz/iQkQbQOepoqFVjpE6w+OOFc9ejFmCvikZwSM8S
YHTLstTp34Ux2u2WzmPYtAFwxQOfzM3sHyF0ZB269Xn+V65pMWJlRXhzqdmoR6B2
YYLzHVBZAbgj7IlS+f1WPkkbrb3BIs71tYyPS3Ng6nhGgXrnqmE5fhm93wR9KR5R
RcgzFGH/8qD2LndmJz8tYtjvlq016Pp40VrYabIrvX6apglayShLQ+2XUe8VKH2Q
dQPIKL3R0ZeDeqwn4PTMclBYd/nXFSbIQndufTDY84ECgYEA8RTcCPOFYkoDeQIr
yKqWo1P1cKsB/bGzvLmo+FqFQij5lmj9iDdvpSQJ58Uj+TFy5qNRT6VueF8U4pNd
Fmcbw05cH+9NzAXji6aYWCargbmhW4L+rzub+TQmdAfqXbBknVgrLenY4LFnFFm1
SIdJRid3vCwiRuhcjecg4FasR+kCgYEA0TTw4ZGxPCX5qt00PUFvuGpqFxFqZ7Ah
wapwQTUY48WeC0Pg7Iq6ipnhjggkgYLkGc1klmTwJXKV0acIS5/DNclepMWZPwIc
+GcKclQsy8MHm+L2tPGBQosRGiSrXi5LzBIQsYSyhBRDUSqu+yj7TTd2XqpUkLAw
qdrWiIjVBvUCgYAOkh0uaVGBfEmzcZ8l1LGgE339HkjThX8AhBQjVo1BT2quXZAd
QIR97ayvlmmzMPrp16sdbjk8Czse6pswtHCoID9PKs5/60cydJI2mbe58nc/Ka6s
9qRZrn44exX+LaAXJnINp1mVUwOQ5k8foBWcqNwCwoQb1wVpCjQhevuUqQKBgQC1
SA+3Fr0iprF6iqWastoxThzSEmhGowwNOjh9eJoxvOsfTdlYfzn3ojIeFhY0F4y6
gw03eQ3TFUCXZAq/JRhNwkl9tC//tkAOS5N00FXk1wH/5aLr1h2w4LqYEdBhEvLh
SYInoRnjc3+FlNv9jVx9Y6Lxkt0mZ1YzyQp/UzptBQKBgFTOonik05rMHGlEg9Ng
unjDAEK+7tXhM/RKxWDs7pZeGbdl1c/NDOX4wIbuK2cCkV0v/qfg6Ex2Obf5rqM4
R5lrs9y30rZbwmfms8G3cimTAsOcLqObKkBED+g07t+3bw44JXLwYLPm/z//bIcI
fkxyxKL5ZJhP3/bPJJj8UAq3
-----END PRIVATE KEY-----
`,
SerialNo: global.GVA_CONFIG.Pays[0].SerialNo, // 商户支付证书序列号
NotifyURL: global.GVA_CONFIG.Pays[0].NotifyUrl, // 微信支付回调地址
HttpDebug: false,
Log: payment.Log{
Level: "debug",
// 可以重定向到你的目录下如果设置File和Error默认会在当前目录下的wechat文件夹下生成日志
File: "/Users/user/wechat/payment/info.log",
Error: "/Users/user/wechat/payment/error.log",
Stdout: false, // 是否打印在终端
},
Http: payment.Http{
Timeout: 30.0,
BaseURI: "https://api.mch.weixin.qq.com",
},
})
if err != nil {
log.Println("初始化微信支付 SDK失败", err)
}
WechatPay = PaymentService
}
// H5Pay 发送订单
func H5Pay(order *app.Order, ctx *gin.Context) (interface{}, error) {
options := &request.RequestH5Prepay{
Amount: &request.H5Amount{
Total: int(order.Price),
Currency: "CNY",
},
Attach: order.Desc,
Description: order.Desc,
OutTradeNo: order.OrderNo, // 这里是商户订单号,不能重复提交给微信
SceneInfo: &request.H5SceneInfo{
PayerClientIP: ctx.ClientIP(), // 用户终端IP
H5Info: &request.H5H5Info{
Type: "Wap",
AppName: "老陈课堂",
},
},
}
// 下单
response, err := WechatPay.Order.TransactionH5(ctx, options)
if err != nil {
global.GVA_LOG.Error("微信支付下单失败", zap.Error(err))
return "", err
}
/*
payConf, err := WechatPay.JSSDK.BridgeConfig(response.PrepayID, true)
if err != nil {
global.GVA_LOG.Error("获取微信支付配置失败", zap.Error(err))
return "", err
}*/
global.GVA_LOG.Info("微信支付配置", zap.Any("payConf", response))
return response, nil
}
func JSAPIPay(order *app.Order, ctx *gin.Context) (interface{}, error) {
options := &request.RequestJSAPIPrepay{
Amount: &request.JSAPIAmount{
Total: int(order.Price),
Currency: "CNY",
},
Attach: order.Name,
Description: order.Desc,
OutTradeNo: order.OrderNo, // 这里是商户订单号,不能重复提交给微信
Payer: &request.JSAPIPayer{
OpenID: order.OpenId, // 用户的openid 记得也是动态的。
},
}
// 下单
response, err := WechatPay.Order.JSAPITransaction(ctx, options)
if err != nil {
panic(err)
}
// payConf大致如下
// {
// "appId": "ww16143ea0101327c7",
// "nonceStr": "jcMfo7lsfM3LPWkLRJb7rQU6WeatStEU",
// "package": "prepay_id=xxxxx",
// "paySign": "hSPF2wU0aYeTq+DJ14ELM...省略部分数据...RrOmlEkZXVxqCdZmniLdA==",
// "signType": "RSA",
// "timeStamp": "1634305101"
// }
payConf, err := WechatPay.JSSDK.BridgeConfig(response.PrepayID, true)
if err != nil {
global.GVA_LOG.Error("获取微信支付配置失败", zap.Error(err))
return "", err
}
return payConf, nil
}
// NotifyHandle 微信支付回调处理
func NotifyHandle(ctx *gin.Context) error {
res, err := WechatPay.HandlePaidNotify(
ctx.Request,
func(message *wxRequst.RequestNotify, transaction *models.Transaction, fail func(message string)) interface{} {
// 看下支付通知事件状态
// 这里可能是微信支付失败的通知,所以可能需要在数据库做一些记录,然后告诉微信我处理完成了。
if message.EventType != "TRANSACTION.SUCCESS" {
fmt.Dump(transaction)
return true
}
if transaction.OutTradeNo != "" {
// 这里对照自有数据库里面的订单做查询以及支付状态改变
// 更新订单状态为已支付
order := app.Order{}
err := global.GVA_DB.Model(&app.Order{}).Where("order_no = ?", transaction.OutTradeNo).First(&order).Error
if err != nil {
global.GVA_LOG.Error("查询订单失败", zap.Error(err))
return nil
}
order.Status = 2 // 设置订单状态为已支付
err = global.GVA_DB.Save(&order).Error
if err != nil {
global.GVA_LOG.Error("更新订单状态失败", zap.Error(err))
return nil
}
log.Printf("订单号:%s 支付成功", transaction.OutTradeNo)
} else {
// 因为微信这个回调不存在订单号,所以可以告诉微信我还没处理成功,等会它会重新发起通知
// 如果不需要直接返回true即可
fail("payment fail")
return nil
}
return true
},
)
// 这里可能是因为不是微信官方调用的无法正常解析出transaction和message所以直接抛错。
if err != nil {
res.StatusCode = 502
err = res.Write(ctx.Writer)
return err
//panic(err)
}
// 这里根据之前返回的是true或者fail框架这边自动会帮你回复微信
err = res.Write(ctx.Writer)
if err != nil {
panic(err)
}
global.GVA_LOG.Info("微信支付回调处理成功", zap.Any("response", res))
return nil
}
func GenerateOrderNum() string {
now := time.Now()
dateStr := now.Format("060102") // 年的后2位+月+日
seconds := int(now.Sub(time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())).Seconds())
secondsStr := gfmt.Sprintf("%05d", seconds)
nanoStr := gfmt.Sprintf("%06d", now.Nanosecond()/1000) // 微秒级6位
rand.Seed(time.Now().UnixNano())
randStr := gfmt.Sprintf("%03d", rand.Intn(1000)) // 3位随机数
orderNo := gfmt.Sprintf("%s%s%s%s%s", "5258", dateStr, secondsStr, nanoStr, randStr)
return orderNo
}

View File

@@ -3,21 +3,18 @@ package wechat
import (
"git.echol.cn/loser/lckt/global"
"github.com/ArtisanCloud/PowerSocialite/v3/src/providers"
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount"
"github.com/ArtisanCloud/PowerWeChat/v3/src/payment"
"go.uber.org/zap"
"log"
)
var WeOfficial *officialAccount.OfficialAccount
var WechatPay *payment.Payment
// InitWeOfficial 初始化微信公众号
func InitWeOfficial() {
OfficialAccountApp, err := officialAccount.NewOfficialAccount(&officialAccount.UserConfig{
AppID: "wx3d21df18d7f8f9fc", // 公众号、小程序的appid
Secret: "ffce59a9a9272c1aaee53950e96617d8", //
Secret: "3ab19e9b6a5e155c25ac6457be650047", //
Log: officialAccount.Log{
Level: "debug",
@@ -37,43 +34,6 @@ func InitWeOfficial() {
WeOfficial = OfficialAccountApp
}
// InitWechatPay 初始化微信支付
func InitWechatPay() {
PaymentService, err := payment.NewPayment(&payment.UserConfig{
AppID: "[app_id]", // 小程序、公众号或者企业微信的appid
MchID: "[mch_id]", // 商户号 appID
MchApiV3Key: "[mch_api_v3_key]", // 微信V3接口调用必填
Key: "[key]", // 微信V2接口调用必填
CertPath: "[wx_cert_path]", // 商户后台支付的Cert证书路径
KeyPath: "[wx_key_path]", // 商户后台支付的Key证书路径
SerialNo: "[serial_no]", // 商户支付证书序列号
NotifyURL: "[notify_url]", // 微信支付回调地址
HttpDebug: true,
Log: payment.Log{
Level: "debug",
// 可以重定向到你的目录下如果设置File和Error默认会在当前目录下的wechat文件夹下生成日志
File: "/Users/user/wechat/payment/info.log",
Error: "/Users/user/wechat/payment/error.log",
Stdout: false, // 是否打印在终端
},
Http: payment.Http{
Timeout: 30.0,
BaseURI: "https://api.mch.weixin.qq.com",
},
// 可选,不传默认走程序内存
Cache: kernel.NewRedisClient(&kernel.UniversalOptions{
Addrs: []string{"127.0.0.1:6379"},
Password: "",
DB: 0,
}),
})
if err != nil {
log.Println("初始化微信支付 SDK失败", err)
}
WechatPay = PaymentService
}
func GetUserInfo(code string) *providers.User {
userFromCode, err := WeOfficial.OAuth.UserFromCode(code)
if err != nil {