فهرست منبع

对公转账&微信支付

Ethan 6 ماه پیش
والد
کامیت
a7d515e6f3

+ 74 - 0
app/controller/finance_controller.go

@@ -0,0 +1,74 @@
+package controller
+
+import (
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"github.com/sirupsen/logrus"
+	"youngee_b_api/app/service"
+	"youngee_b_api/app/util"
+	"youngee_b_api/app/vo"
+)
+
+type FinanceController struct{}
+
+// 充值管理——对公转账
+func (t FinanceController) TransferToPublic(c *gin.Context) {
+	param := &vo.RechargeTransferParam{}
+	err := c.BindJSON(param)
+	if err != nil {
+		logrus.Errorf("Request bind err:%+v\n", err)
+		returnError(c, 40000, "参数错误")
+		return
+	}
+	rechargeId, err := service.RechargeService{}.TransferToPublic(param)
+	if err != nil {
+		logrus.Errorf("[TransferToPublic] call Show err:%+v\n", err)
+		returnError(c, 40000, "error")
+		return
+	}
+	resultMap := make(map[string]string)
+	resultMap["rechargeId"] = *rechargeId
+	returnSuccess(c, 20000, resultMap)
+}
+
+// 获取微信支付CodeUrl
+func (t FinanceController) GetCodeUrl(c *gin.Context) {
+	param := &vo.GetCodeUrlParam{}
+	err := c.BindJSON(param)
+	if err != nil {
+		logrus.Errorf("Request bind err:%+v\n", err)
+		returnError(c, 40000, "参数错误")
+		return
+	}
+	tradeId := util.GetRandomString(32)
+	fmt.Println("amount:", param.Amount)
+	codeUrl, err := service.RechargeService{}.NativeApiServicePrepay(tradeId, param.Amount)
+	if err != nil {
+		logrus.Errorf("[GetCodeUrl] call Show err:%+v\n", err)
+		returnError(c, 40000, "error")
+		return
+	}
+	reCodeUrl := new(vo.ReCodeUrl)
+	reCodeUrl.CodeUrl = codeUrl
+	reCodeUrl.TradeId = tradeId
+
+	returnSuccess(c, 20000, reCodeUrl)
+}
+
+// 根据交易id查询微信是否扫码付款
+func (t FinanceController) QueryOrderByTradeId(c *gin.Context) {
+	param := &vo.QueryOrderByTradeIdParam{}
+	err := c.BindJSON(param)
+	if err != nil {
+		logrus.Errorf("Request bind err:%+v\n", err)
+		returnError(c, 40000, "参数错误")
+		return
+	}
+	tradeState, err := service.RechargeService{}.QueryOrderByTradeId(param.TradeId)
+	if err != nil {
+		logrus.Errorf("[QueryOrderByTradeId] call Show err:%+v\n", err)
+		returnError(c, 40000, "error")
+		return
+	}
+	returnSuccess(c, 20000, tradeState)
+}

+ 9 - 0
app/dao/enterprise_dao.go

@@ -12,3 +12,12 @@ func (d EnterpriseDao) GetEnterprise(enterpriseId string) (*entity.Enterprise, e
 	}
 	return &enterprise, nil
 }
+
+func (d EnterpriseDao) GetEnterprisePhone(enterpriseId string) (string, error) {
+	var phone string
+	err := Db.Model(&entity.Enterprise{}).Where("enterprise_id = ?", enterpriseId).Select("phone").First(&phone).Error
+	if err != nil {
+		return "", err
+	}
+	return phone, nil
+}

+ 2 - 2
app/dao/project_task_info_dao.go

@@ -67,7 +67,7 @@ func (d ProjectTaskInfoDao) GetListByDataDefault(param *vo.DefaultSearchParam) (
 func (d ProjectTaskInfoDao) GetListByTerminateDefault(param *vo.DefaultSearchParam) ([]entity.ProjectTaskInfo, int64, error) {
 	projectTaskInfos := []entity.ProjectTaskInfo{}
 	var total int64
-	query := Db.Model(&entity.ProjectTaskInfo{}).Where("project_id = ? AND task_stage = ?", param.TaskId, 17)
+	query := Db.Debug().Model(&entity.ProjectTaskInfo{}).Where("project_id = ? AND task_stage = ?", param.TaskId, 17)
 	query.Count(&total)
 	query = query.Select("task_id, talent_id, settle_amount, draft_fee, terminate_time, terminate_reason, terminate_operator_type, terminate_operator")
 	offset := (param.Page - 1) * param.PageSize
@@ -81,7 +81,7 @@ func (d ProjectTaskInfoDao) GetListByTerminateDefault(param *vo.DefaultSearchPar
 func (d ProjectTaskInfoDao) GetListByCancelDefault(param *vo.DefaultSearchParam) ([]entity.ProjectTaskInfo, int64, error) {
 	projectTaskInfos := []entity.ProjectTaskInfo{}
 	var total int64
-	query := Db.Model(&entity.ProjectTaskInfo{}).Where("project_id = ? AND task_stage = ?", param.TaskId, 16)
+	query := Db.Debug().Model(&entity.ProjectTaskInfo{}).Where("project_id = ? AND task_stage = ?", param.TaskId, 16)
 	query.Count(&total)
 	query = query.Select("task_id, talent_id, settle_amount, draft_fee, cancel_time, cancel_reason, cancel_operator_type, cancel_operator")
 	offset := (param.Page - 1) * param.PageSize

+ 13 - 0
app/dao/recharge_record_dao.go

@@ -0,0 +1,13 @@
+package dao
+
+import "youngee_b_api/app/entity"
+
+type RechargeRecordDao struct{}
+
+func (d RechargeRecordDao) Insert(rechargeRecord *entity.RechargeRecord) error {
+	err := Db.Debug().Model(entity.RechargeRecord{}).Omit("confirm_at").Create(rechargeRecord).Error
+	if err != nil {
+		return err
+	}
+	return nil
+}

+ 9 - 0
app/dao/sub_account_dao.go

@@ -12,3 +12,12 @@ func (d SubAccountDao) GetSubAccount(subAccountId int64) (*entity.SubAccount, er
 	}
 	return &subAccount, nil
 }
+
+func (d SubAccountDao) GetSubAccountPhone(subAccountId int64) (string, error) {
+	var phone string
+	err := Db.Model(&entity.SubAccount{}).Where("sub_account_id = ?", subAccountId).Select("phone").First(&phone).Error
+	if err != nil {
+		return "", err
+	}
+	return phone, nil
+}

+ 21 - 0
app/entity/recharge_record.go

@@ -0,0 +1,21 @@
+package entity
+
+import "time"
+
+type RechargeRecord struct {
+	RechargeID         string    `gorm:"column:recharge_id;primary_key"`       // 充值订单ID
+	EnterpriseID       string    `gorm:"column:enterprise_id;NOT NULL"`        // 企业id
+	SubAccountId       int64     `gorm:"column:sub_account_id;NOT NULL"`       // 子账号id
+	RechargeAmount     float64   `gorm:"column:recharge_amount;NOT NULL"`      // 充值金额
+	TransferVoucherUrl string    `gorm:"column:transfer_voucher_url;NOT NULL"` // 转账凭证图片链接
+	Phone              string    `gorm:"column:phone;NOT NULL"`                // 联系方式
+	RechargeMethod     int64     `gorm:"column:recharge_method;NOT NULL"`      // 充值方式:1为对公转账,2为支付宝在线支付,3为微信支付
+	Status             int64     `gorm:"column:status;NOT NULL"`               // 充值状态:0为充值待确认,1为充值已确认
+	InvoiceStatus      int       `gorm:"column:invoice_status;NOT NULL"`       // 开票状态:1为可开票,2为待开票,3为已开票
+	CommitAt           time.Time `gorm:"column:commit_at;NOT NULL"`            // 充值申请提交时间
+	ConfirmAt          time.Time `gorm:"column:confirm_at"`                    // 充值确认时间
+}
+
+func (m *RechargeRecord) TableName() string {
+	return "younggee_recharge_record"
+}

+ 147 - 0
app/service/recharge_service.go

@@ -0,0 +1,147 @@
+package service
+
+import (
+	"context"
+	"fmt"
+	log "github.com/sirupsen/logrus"
+	"github.com/wechatpay-apiv3/wechatpay-go/core"
+	"github.com/wechatpay-apiv3/wechatpay-go/core/option"
+	"github.com/wechatpay-apiv3/wechatpay-go/services/payments/native"
+	"github.com/wechatpay-apiv3/wechatpay-go/utils"
+	"strconv"
+	"time"
+	"youngee_b_api/app/dao"
+	"youngee_b_api/app/entity"
+	"youngee_b_api/app/util"
+	"youngee_b_api/app/vo"
+)
+
+type RechargeService struct{}
+
+// 充值管理——对公转账
+func (s RechargeService) TransferToPublic(param *vo.RechargeTransferParam) (*string, error) {
+	var rechargeId string
+	var phone string
+	if param.SubAccountId == 0 {
+		rechargeId = util.MakeRechargeId(param.EnterpriseId)
+		phone, _ = dao.EnterpriseDao{}.GetEnterprisePhone(param.EnterpriseId)
+	} else {
+		rechargeId = util.MakeRechargeId(strconv.FormatInt(param.SubAccountId, 10))
+		phone, _ = dao.SubAccountDao{}.GetSubAccountPhone(param.SubAccountId)
+	}
+	rechargeRecord := entity.RechargeRecord{
+		RechargeID:         rechargeId,
+		EnterpriseID:       param.EnterpriseId,
+		RechargeAmount:     param.Amount,
+		TransferVoucherUrl: param.TransferVoucherUrl,
+		Phone:              phone,
+		RechargeMethod:     1,
+		Status:             1,
+		InvoiceStatus:      1,
+		CommitAt:           time.Now(),
+	}
+	err := dao.RechargeRecordDao{}.Insert(&rechargeRecord)
+	if err != nil {
+		return nil, err
+	}
+	return &rechargeId, nil
+}
+
+func (s RechargeService) NativeApiServicePrepay(tradeId string, amount int64) (codeUrl string, err error) {
+	var (
+		mchID                      string = "1615933939"                               // 商户号
+		mchCertificateSerialNumber string = "33DDFEC51BF5412F663B9B56510FD567B625FC68" // 商户证书序列号
+		mchAPIv3Key                string = "V10987654321younggee12345678910V"         // 商户APIv3密钥,用于加密和解密平台证书
+	)
+	fmt.Println("充值的金额为:", amount)
+
+	// 使用 utils 提供的函数从本地文件中加载商户私钥
+	mchPrivateKey, err := utils.LoadPrivateKeyWithPath("./apiclient_key.pem") // 商户私钥,用于生成请求的签名
+	if err != nil {
+		log.Print("load merchant private key error")
+	}
+
+	ctx := context.Background()
+	// 使用商户私钥等初始化 微信支付client,并使它具有自动定时获取微信支付平台证书的能力
+	opts := []core.ClientOption{
+		option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
+	}
+	client, err := core.NewClient(ctx, opts...)
+	if err != nil {
+		log.Printf("new wechat pay client err: %s", err)
+	}
+
+	// 采用 Native 支付方式
+	svc := native.NativeApiService{Client: client}
+	// 发送请求
+	resp, result, err := svc.Prepay(ctx,
+		native.PrepayRequest{
+			Appid:         core.String("wxac396a3be7a16844"),
+			Mchid:         core.String("1615933939"),
+			Description:   core.String("样叽微信支付充值"),
+			OutTradeNo:    core.String(tradeId),
+			TimeExpire:    core.Time(time.Now().Add(10 * time.Minute)),
+			Attach:        core.String("微信支付充值"),
+			NotifyUrl:     core.String("https://www.weixin.qq.com/wxpay/pay.php"),
+			SupportFapiao: core.Bool(true),
+			Amount: &native.Amount{
+				Currency: core.String("CNY"),
+				Total:    core.Int64(amount),
+			},
+		},
+	)
+
+	if err != nil {
+		// 处理错误
+		log.Printf("call Prepay err:%s", err)
+		return "", err
+	} else {
+		// 处理返回结果
+		log.Printf("status=%d resp=%s", result.Response.StatusCode, resp)
+		log.Println("codeUrl:", *resp.CodeUrl)
+	}
+	return *resp.CodeUrl, nil
+}
+
+func (s RechargeService) QueryOrderByTradeId(tradeId string) (tradeState string, err error) {
+	var (
+		mchID                      string = "1615933939"                               // 商户号
+		mchCertificateSerialNumber string = "33DDFEC51BF5412F663B9B56510FD567B625FC68" // 商户证书序列号
+		mchAPIv3Key                string = "V10987654321younggee12345678910V"         // 商户APIv3密钥
+	)
+
+	// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
+	mchPrivateKey, err := utils.LoadPrivateKeyWithPath("./apiclient_key.pem")
+	if err != nil {
+		log.Print("load merchant private key error")
+	}
+
+	ctx := context.Background()
+	// 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
+	opts := []core.ClientOption{
+		option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
+	}
+	client, err := core.NewClient(ctx, opts...)
+	if err != nil {
+		log.Printf("new wechat pay client err: %s", err)
+	}
+
+	svc := native.NativeApiService{Client: client}
+	resp, result, err := svc.QueryOrderByOutTradeNo(ctx,
+		native.QueryOrderByOutTradeNoRequest{
+			OutTradeNo: core.String(tradeId),
+			Mchid:      core.String("1615933939"),
+		},
+	)
+	fmt.Printf("支付 %+v\n", resp)
+	if err != nil {
+		// 处理错误
+		log.Printf("call QueryOrderByOutTradeNo err: %s", err)
+		return "", err
+	} else {
+		// 处理返回结果
+		log.Printf("status=%d resp=%s", result.Response.StatusCode, resp)
+	}
+	fmt.Printf("支付状态 %+v\n", *resp.TradeState)
+	return *resp.TradeState, nil
+}

+ 40 - 0
app/util/uuid.go

@@ -6,6 +6,9 @@ import (
 	"github.com/issue9/conv"
 	"math/rand"
 	"time"
+	"youngee_b_api/app/dao"
+	"youngee_b_api/app/entity"
+	"youngee_b_api/util"
 )
 
 var snowflakeInstance *snowflake.Snowflake
@@ -49,3 +52,40 @@ func GetProjectID() string {
 	selectionId := conv.MustString(time.Now().Year())[2:] + td + conv.MustString(rand.Intn(100000-10000)+10000)
 	return selectionId
 }
+
+func MakeRechargeId(EnterpriseID string) string {
+	// 1、年月日
+	year := time.Now().Year()
+	month := time.Now().Month()
+	day := time.Now().Day()
+	yearData, _ := util.GetDayNum("year", year)
+	monthData, _ := util.GetDayNum("month", int(month))
+	dayData, _ := util.GetDayNum("day", day)
+	sum := 0
+	sum += dayData + monthData
+	leap := 0
+	if (yearData%400 == 0) || ((yearData%4 == 0) && (yearData%100 != 0)) {
+		leap = 1
+	}
+	if (leap == 1) && (monthData > 2) {
+		sum += 1
+	}
+	last := ""
+	rechargeIdPrefix := "8" + EnterpriseID[len(EnterpriseID)-2:] + conv.MustString(sum)
+	var rechargeIdLast string
+	err := dao.Db.Model(entity.RechargeRecord{}).Select("recharge_id").Where("recharge_id like ?", rechargeIdPrefix+"%").
+		Last(&rechargeIdLast).Error
+	if err != nil {
+		last = "0"
+	} else {
+		last = rechargeIdLast[len(rechargeIdLast)-2:]
+	}
+	var lastInt int
+	lastInt = conv.MustInt(last)
+	if lastInt+1 < 10 {
+		last = "0" + conv.MustString(conv.MustInt(last)+1)
+	} else {
+		last = conv.MustString(conv.MustInt(last) + 1)
+	}
+	return rechargeIdPrefix + last
+}

+ 14 - 0
app/vo/pay_wx_param.go

@@ -0,0 +1,14 @@
+package vo
+
+type GetCodeUrlParam struct {
+	Amount int64 `json:"amount"`
+}
+
+type ReCodeUrl struct {
+	CodeUrl string `json:"code_url"`
+	TradeId string `json:"trade_id"`
+}
+
+type QueryOrderByTradeIdParam struct {
+	TradeId string `json:"trade_id"`
+}

+ 8 - 0
app/vo/recharge_transfer_param.go

@@ -0,0 +1,8 @@
+package vo
+
+type RechargeTransferParam struct {
+	EnterpriseId       string  `json:"enterprise_id"`
+	SubAccountId       int64   `json:"sub_account_id"`
+	Amount             float64 `json:"amount"`
+	TransferVoucherUrl string  `json:"transfer_voucher_url"`
+}

+ 13 - 0
route/init.go

@@ -201,5 +201,18 @@ func InitRoute(r *gin.Engine) {
 		task.POST("/default/talent/cancel/list", controller.TaskController{}.CancelTalentList)           // 违约管理——达人批量解约
 
 	}
+	// 财务结算相关接口
+	finance := r.Group("/youngee/b/finance")
+	{
+		finance.Use(middleware.LoginAuthMiddleware)
+		// 余额管理——总金额、可用余额、冻结金额
+		// 余额管理——冻结记录
+		// 充值管理——累计充值金额、确认中金额
+		// 充值管理——充值记录
+		// YG官方汇款账号
+		finance.POST("/recharge/transferToPublic", controller.FinanceController{}.TransferToPublic)  // 充值管理——对公转账
+		finance.POST("/pay/getCodeUrl", controller.FinanceController{}.GetCodeUrl)                   // 获取微信支付codeURL
+		finance.POST("/pay/queryOrderByTradeId", controller.FinanceController{}.QueryOrderByTradeId) // 根据交易id查询微信是否扫码付款
+	}
 
 }