1.收付款方式设置调整,银行名称和账号改为非必填;

2.收付款方式默认配置的type为0,新增的type为1;
3.商品分类调整,如果父类被隐藏或显示,其子类也做对应修改;
4.新增商品资料入参调整,串码类型改为非必填;
5.新增商品资料调整,相同名称商品不允许新建;
6.零售订单审核优化,记录审核时间;
7.零售订单付款接口优化;
8.新增零售订单付款状态查询接口;
9.日志部分init时进行设置,避免test报错;
10.QueryTimeFormat格式调整,跟前端保持一致;
11.新增河马付相关密钥文件;
This commit is contained in:
chenlin 2023-12-21 17:36:13 +08:00
parent d3f485ecda
commit c160f02a41
17 changed files with 860 additions and 101 deletions

View File

@ -11,10 +11,10 @@ import (
)
type CashierCreateRequest struct {
Name string `json:"name" binding:"required"` // 账号名称
BankName string `json:"bank_name" binding:"required"` // 银行全称
BankAccount string `json:"bank_account" binding:"required"` // 银行账号
StoreNums []uint32 `json:"store_num" binding:"required"` // 门店编号
Name string `json:"name" binding:"required"` // 账号名称
BankName string `json:"bank_name"` // 银行全称
BankAccount string `json:"bank_account"` // 银行账号
StoreNums []uint32 `json:"store_num" binding:"required"` // 门店编号
}
// CashierCreate 新增收付款账号
@ -50,6 +50,7 @@ func CashierCreate(c *gin.Context) {
BankName: req.BankName,
BankAccount: req.BankAccount,
State: 1,
Type: 1, //默认为1新增的收付款方式
Remark: "",
}

View File

@ -200,8 +200,17 @@ func CategoryDisplay(c *gin.Context) {
req := new(CategoryDisplayRequest)
_ = c.ShouldBindJSON(req)
var category models.Category
err := orm.Eloquent.Model(models.Category{}).
Where("id", req.Id).
Where("id", req.Id).Find(&category).Error
if err != nil {
app.Error(c, 500, err, "修改失败")
return
}
// 批量更新,如果父类被修改,其子类也要对应修改
err = orm.Eloquent.Debug().Model(models.Category{}).
Where("number like ?", category.Number+"%").
UpdateColumn("display", req.Display).Error
if err != nil {

View File

@ -16,7 +16,7 @@ type CommodityCreateRequest struct {
Name string `json:"name" binding:"required"` // 商品名称
ErpCategoryId uint32 `json:"erp_category_id" binding:"required"` // 商品分类id
IsIMEI uint32 `json:"is_imei" binding:"required"` // 是否串码1-串码类 2-非串码
IMEIType uint32 `json:"imei_type" binding:"required"` // 系统生成串码2-是(系统生成) 3-否(手动添加)
IMEIType uint32 `json:"imei_type"` // 系统生成串码2-是(系统生成) 3-否(手动添加)
ErpSupplierId uint32 `json:"erp_supplier_id" binding:"required"` // 主供应商
RetailPrice uint32 `json:"retail_price" binding:"required"` // 指导零售价
MinRetailPrice uint32 `json:"min_retail_price" binding:"required"` // 最低零售价
@ -69,6 +69,11 @@ func CommodityCreate(c *gin.Context) {
return
}
if models.IsExistingProduct(req.Name) {
app.Error(c, http.StatusBadRequest, errors.New("param err"), "商品名称已存在,不能重复")
return
}
if req.IsIMEI == 2 { // 是否串码1-串码类 2-非串码
req.IMEIType = 1 // 系统生成串码2-是(系统生成) 3-否(手动添加) 1表示非串码
}

View File

@ -475,6 +475,10 @@ func ErpOrderAudit(c *gin.Context) {
orderState = model.ErpOrderStateAudited
case 2: // 取消审核
orderState = model.ErpOrderStateUnAudit
default:
logger.Error("req.State no in (1,2)", logger.Field("req.State", req.State))
app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误")
return
}
// 检查支付方式,如果没有线上支付则默认为支付已完成
@ -492,6 +496,7 @@ func ErpOrderAudit(c *gin.Context) {
err = begin.Table("erp_order").Where("id = ?", erpOrder.ID).Updates(map[string]interface{}{
"state": orderState,
"pay_status": nPayStatus,
"audit_time": time.Now(),
}).Error
if err != nil {
begin.Rollback()
@ -592,10 +597,43 @@ func ErpOrderAddInvoice(c *gin.Context) {
// @Tags 零售订单
// @Produce json
// @Accept json
// @Param request body models.ErpOrderDeleteReq true "收款模型"
// @Success 200 {object} app.Response
// @Param request body models.ErpOrderPayReq true "收款模型"
// @Success 200 {object} models.ErpOrderPayResp
// @Router /api/v1/erp_order/pay [post]
func ErpOrderPay(c *gin.Context) {
var req = new(model.ErpOrderPayReq)
if err := c.ShouldBindJSON(&req); err != nil {
logger.Error("ShouldBindJSON err:", logger.Field("err", err))
app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误")
return
}
err := tools.Validate(req) //必填参数校验
if err != nil {
app.Error(c, http.StatusBadRequest, err, err.Error())
return
}
resp, err := model.ErpOrderPay(req)
if err != nil {
logger.Error("ErpOrderPay err:", logger.Field("err", err))
app.Error(c, http.StatusInternalServerError, err, err.Error())
return
}
app.OK(c, resp, "")
return
}
// ErpOrderQueryPayStatus 查询付款状态
// @Summary 查询付款状态
// @Tags 零售订单
// @Produce json
// @Accept json
// @Param request body models.ErpOrderDeleteReq true "查询付款状态模型"
// @Success 200 {object} models.ErpOrderPayResp
// @Router /api/v1/erp_order/query_pay_status [post]
func ErpOrderQueryPayStatus(c *gin.Context) {
var req = new(model.ErpOrderDeleteReq)
if err := c.ShouldBindJSON(&req); err != nil {
logger.Error("ShouldBindJSON err:", logger.Field("err", err))
@ -609,38 +647,13 @@ func ErpOrderPay(c *gin.Context) {
return
}
//通过单据号查询收款金额
var orderInfo model.ErpOrder
err = orm.Eloquent.Table("erp_order").Where("bill_sn = ?", req.BillSn).Find(&orderInfo).Error
resp, err := model.QueryErpOrderPayStatus(req.BillSn)
if err != nil {
logger.Error("order delete err:", logger.Field("err", err))
app.Error(c, http.StatusInternalServerError, err, "操作失败,未查询到订单")
logger.Error("QueryErpOrderPayStatus err:", logger.Field("err", err))
app.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
if orderInfo.PayStatus != model.WaitForPaying {
app.Error(c, http.StatusInternalServerError, err, "操作失败,订单不是待支付状态")
return
}
var cashiers []model.ErpOrderCashier
err = json.Unmarshal([]byte(orderInfo.CashierList), &cashiers)
if err != nil {
logger.Error("unmarshal CashierList err:", logger.Field("err", err))
}
amount := 0
for i, _ := range cashiers {
if cashiers[i].CashierId == model.OnlinePay { //线上支付
amount = int(cashiers[i].Amount)
break
}
}
if amount == 0 {
app.Error(c, http.StatusInternalServerError, err, "操作失败订单金额为0")
return
}
app.OK(c, nil, "")
app.OK(c, resp, "")
return
}

View File

@ -21,6 +21,8 @@ import (
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
"github.com/wechatpay-apiv3/wechatpay-go/services/transferbatch"
"io"
"os"
//"github.com/wechatpay-apiv3/wechatpay-go/utils"
wechatpayutils "github.com/wechatpay-apiv3/wechatpay-go/utils"
"go-admin/logger"
@ -131,7 +133,7 @@ func GenTradeNo() string {
return tradeNO
}
// 给用户打款
// Transfer 给用户打款
func Transfer(amount uint32, openId string, desc string) (*WxTransferResp, error) {
tradeNO := GenTradeNo()
nonce := GenRandStr(NonceStringLength)
@ -598,6 +600,71 @@ type HmPayBToCBizContent struct {
NotifyUrl string `json:"notify_url"`
}
// HmPayBToCOrderDetail 刷卡支付B扫Ctrade.pay 响应业务参数
type HmPayBToCOrderDetail struct {
SubCode string `json:"sub_code"` // 业务返回码
SubMsg string `json:"sub_msg"` // 业务返回信息
PayWay string `json:"pay_way"` // 支付方式 AUTO情况下条码的类型
BankWay string `json:"bank_way"` // 银行通道
OutOrderNo string `json:"out_order_no"` // 商户订单号
PlatTrxNo string `json:"plat_trx_no"` // 平台交易流水号
BankOrderNo string `json:"bank_order_no"` // 银行订单号,平台送给渠道的商户订单号
BankTrxNo string `json:"bank_trx_no"` // 银行流水号,渠道返回给平台的流水号,订单成功情况下存在
BuyerId string `json:"buyer_id"` // 买家ID付款方唯一标识
SuccessTime string `json:"success_time"` // 支付成功时间格式yyyyMMddHHmmss
TotalAmount float64 `json:"total_amount"` // 订单总金额
SettleAmount float64 `json:"settle_amount"` // 商户可结算金额,订单金额-商户手续费
BuyerPayAmount float64 `json:"buyer_pay_amount"` // 买家付款金额,买家实际付款的金额,订单金额-优惠金额
ReqReserved string `json:"req_reserved"` // 商户自定义字段
}
// HmPayTradeQueryContent 订单查询 trade.query 业务请求参数
type HmPayTradeQueryContent struct {
OrderCreateTime string `json:"order_create_time,omitempty"` // 订单创建时间yyyyMMddHHmmss当仅上送out_order_no需上送若不上送默认当天
OutOrderNo string `json:"out_order_no"` // 商户订单号,订单号三选一必填
PlatTrxNo string `json:"plat_trx_no,omitempty"` // 平台交易流水号
BankOrderNo string `json:"bank_order_no,omitempty"` // 银行订单号
}
// HmPayTradeQueryResp 订单查询 trade.query 业务响应参数
type HmPayTradeQueryResp struct {
SubCode string `json:"sub_code"` // 业务返回码
SubMsg string `json:"sub_msg"` // 业务返回信息
IsCancel string `json:"is_cancel"` // 是否已撤销 true/false
IsFundAuthPay string `json:"is_fund_auth_pay"` // 预授权冻结是否已完成 true/false
IsRefund string `json:"is_refund"` // 是否有退款 true/false
RefundSuccessAmount float64 `json:"refund_success_amount"` // 成功退款金额,单位元
TotalAmount float64 `json:"total_amount"` // 订单总金额
OutOrderNo string `json:"out_order_no"` // 商户订单号
PlatTrxNo string `json:"plat_trx_no"` // 平台交易流水号
BankOrderNo string `json:"bank_order_no"` // 银行订单号
BankTrxNo string `json:"bank_trx_no"` // 银行流水号
BuyerID string `json:"buyer_id"` // 买家ID
SuccessTime string `json:"success_time"` // 支付成功时间格式yyyyMMddHHmmss
SettleAmount float64 `json:"settle_amount"` // 商户可结算金额,订单金额-商户手续费
PayWayCode string `json:"pay_way_code"` // 用户使用的交易方式如WECHATALIPAYUNIONPAY
PayModeCode string `json:"pay_mode_code"` // 用户使用的支付模式如扫码支付PAY_SCAN
BankWayCode string `json:"bank_way_code"` // 银行通道
BuyerPayAmount float64 `json:"buyer_pay_amount"` // 买家付款金额,买家实际付款的金额,订单金额-优惠金额
ReqReserved string `json:"req_reserved"` // 商户自定义字段
}
// HmPayTradeCancelContent 订单撤销 trade.cancel 业务请求参数
type HmPayTradeCancelContent struct {
OutOrderNo string `json:"out_order_no"` // 商户订单号,订单号三选一必填
PlatTrxNo string `json:"plat_trx_no,omitempty"` // 平台交易流水号
BankOrderNo string `json:"bank_order_no,omitempty"` // 银行订单号
}
// HmPayTradeCancelResp 订单撤销 trade.cancel 业务响应参数
type HmPayTradeCancelResp struct {
SubCode string `json:"sub_code"` // 业务返回码
SubMsg string `json:"sub_msg"` // 业务返回信息
Action string `json:"action"` // 本次撤销触发的动作 CLOSE REFUND UNKNOW
CancelPlatTrxNo string `json:"cancel_plat_trx_no"` // 撤销平台流水号
ReqReserved string `json:"req_reserved"` // 商户自定义字段
}
type HmPayUnifiedOrderPayData struct {
TimeStamp string `json:"timeStamp"`
Package string `json:"package"`
@ -626,29 +693,10 @@ type HmPayUnifiedOrderRsp struct {
Sign string `json:"sign"`
}
type HmPayBToCOrderDetail struct {
SubCode string `json:"sub_code"`
SubMsg string `json:"sub_msg"`
PayWay string `json:"pay_way"`
BankWay string `json:"bank_way"`
OutOrderNo string `json:"out_order_no"`
PlatTrxNo string `json:"plat_trx_no"`
BankOrderNo string `json:"bank_order_no"`
BankTrxNo string `json:"bank_trx_no"`
BuyerId string `json:"buyer_id"`
SuccessTime string `json:"success_time"`
TotalAmount string `json:"total_amount"`
SettleAmount string `json:"settle_amount"`
BuyerPayAmount string `json:"buyer_pay_amount"`
ReqReserved string `json:"req_reserved"`
DiscountDetail string `json:"discount_detail"`
BuyerTradeFundBillDetail string `json:"buyer_trade_fund_bill_detail"`
}
func ParsePrivateKey() (*rsa.PrivateKey, error) {
//fp := "/Users/li/mh/mh_server/pack/configs/hm_pay/private_key.pem"
fp := "./configs/hm_pay/private_key.pem"
privateKey, err := ioutil.ReadFile(fp)
fp := "/Users/max/Documents/code/deovo/mh_goadmin_server/config/hm_pay/private_key.pem"
//fp := "./configs/hm_pay/private_key.pem"
privateKey, err := os.ReadFile(fp)
if err != nil {
logger.Errorf("read file err:", err)
return nil, err
@ -709,7 +757,8 @@ func GenHmPaySign(m map[string]string) (string, error) {
signStr := strings.Join(signData, "&")
//signStr = signStr + "&key=" + payKey
logger.Error("签字符串1:", logger.Field("signStr", signStr))
//logger.Info("签字符串1:", logger.Field("signStr", signStr))
fmt.Println("签字符串1:", signStr)
signature, err := Sha1withRsa(signStr)
if err != nil {
@ -848,8 +897,8 @@ func ToSignContent(s interface{}) (string, error) {
return signStr, nil
}
// HmJsPayUnifiedOrder 河马刷卡支付B扫C
func HmJsPayUnifiedOrder(orderId string, totalFee uint32, authCode, notifyUrl string) (*HmPayBToCOrderDetail, error) {
// HmJsPayBToCOrder 河马刷卡支付B扫C
func HmJsPayBToCOrder(orderId string, totalFee float64, authCode, notifyUrl string) (*HmPayBToCOrderDetail, error) {
now := time.Now().Local()
strTime := fmt.Sprintf("%04d%02d%02d%02d%02d%02d", now.Year(), now.Month(), now.Day(), now.Hour(),
now.Minute(), now.Second())
@ -880,7 +929,7 @@ func HmJsPayUnifiedOrder(orderId string, totalFee uint32, authCode, notifyUrl st
CreateIp: clientIp,
CreateTime: strTime,
PayWay: "AUTO",
TotalAmount: float64(totalFee) / 100,
TotalAmount: totalFee,
OutOrderNo: orderId,
Body: "门店消费",
StoreId: "100001",
@ -934,3 +983,139 @@ func HmJsPayUnifiedOrder(orderId string, totalFee uint32, authCode, notifyUrl st
return &hmPayDetail, nil
}
// HmQueryOrder 订单查询
func HmQueryOrder(orderId string) (*HmPayTradeQueryResp, error) {
now := time.Now().Local()
nonce := GenRandStr(NonceStringLength)
unifiedOrderReq := HmJsPayUnifiedOrderReq{}
publicPara := HmPayPublicPara{
AppId: HmPayMerchantId,
Method: "trade.query",
SignType: "RSA",
Sign: "",
Timestamp: now.Format(TimeFormat),
Nonce: nonce,
}
biz := HmPayTradeQueryContent{
OutOrderNo: orderId,
}
unifiedOrderReq.HmPayPublicPara = publicPara
bizString, err := json.Marshal(&biz)
if err != nil {
logger.Error("marshal biz err:", logger.Field("err", err))
return nil, err
}
unifiedOrderReq.HmPayPublicPara.BizContent = string(bizString)
m, err := struct2Map(unifiedOrderReq)
if err != nil {
logger.Error("HmJsPayUnifiedOrder struct2Map err:", logger.Field("err", err))
return nil, err
}
sign, err := GenHmPaySign(m)
if err != nil {
logger.Error("HmJsPayUnifiedOrder GenHmPaySign err:", logger.Field("err", err))
return nil, err
}
unifiedOrderReq.Sign = sign
unifiedOrderResp, err := HmPayUnifiedOrder(unifiedOrderReq)
if err != nil {
logger.Errorf("WxUnifiedOrder unified order error %#v", err)
return nil, err
}
signContent, err := ToSignContent(unifiedOrderResp)
if err != nil {
logger.Errorf("ToSignContent err:", err)
return nil, err
}
err = HmVerifySha1Rsa(signContent, unifiedOrderResp.Sign)
if err != nil {
logger.Errorf("HmVerifySha1Rsa err:", err)
return nil, err
}
fmt.Println("Resp Data:", unifiedOrderResp.Data)
var hmPayDetail HmPayTradeQueryResp
err = json.Unmarshal([]byte(unifiedOrderResp.Data), &hmPayDetail)
if err != nil {
logger.Errorf("hm pay unified order pay data unmarshal error %#v", err)
return nil, err
}
fmt.Println("hmPayDetail:", hmPayDetail)
return &hmPayDetail, nil
}
// HmCancelOrder 订单撤销
func HmCancelOrder(orderId string) (*HmPayTradeCancelResp, error) {
now := time.Now().Local()
nonce := GenRandStr(NonceStringLength)
unifiedOrderReq := HmJsPayUnifiedOrderReq{}
publicPara := HmPayPublicPara{
AppId: HmPayMerchantId,
Method: "trade.cancel",
SignType: "RSA",
Sign: "",
Timestamp: now.Format(TimeFormat),
Nonce: nonce,
}
biz := HmPayTradeCancelContent{
OutOrderNo: orderId,
}
unifiedOrderReq.HmPayPublicPara = publicPara
bizString, err := json.Marshal(&biz)
if err != nil {
logger.Error("marshal biz err:", logger.Field("err", err))
return nil, err
}
unifiedOrderReq.HmPayPublicPara.BizContent = string(bizString)
m, err := struct2Map(unifiedOrderReq)
if err != nil {
logger.Error("HmJsPayUnifiedOrder struct2Map err:", logger.Field("err", err))
return nil, err
}
sign, err := GenHmPaySign(m)
if err != nil {
logger.Error("HmJsPayUnifiedOrder GenHmPaySign err:", logger.Field("err", err))
return nil, err
}
unifiedOrderReq.Sign = sign
unifiedOrderResp, err := HmPayUnifiedOrder(unifiedOrderReq)
if err != nil {
logger.Errorf("WxUnifiedOrder unified order error %#v", err)
return nil, err
}
signContent, err := ToSignContent(unifiedOrderResp)
if err != nil {
logger.Errorf("ToSignContent err:", err)
return nil, err
}
err = HmVerifySha1Rsa(signContent, unifiedOrderResp.Sign)
if err != nil {
logger.Errorf("HmVerifySha1Rsa err:", err)
return nil, err
}
fmt.Println("Resp Data:", unifiedOrderResp.Data)
var hmPayDetail HmPayTradeCancelResp
err = json.Unmarshal([]byte(unifiedOrderResp.Data), &hmPayDetail)
if err != nil {
logger.Errorf("hm pay unified order pay data unmarshal error %#v", err)
return nil, err
}
fmt.Println("hmPayDetail:", hmPayDetail)
return &hmPayDetail, nil
}

View File

@ -1,6 +1,10 @@
package pay
import "testing"
import (
"fmt"
"testing"
"time"
)
func TestWxSDKPay(t *testing.T) {
WxSDKPay(55191926, "元气森林")
@ -10,3 +14,51 @@ func TestWxSDKPay(t *testing.T) {
func TestTransfer(t *testing.T) {
//Transfer(400,"ohuHh4riVVPxwKHrYHsWwZRpxVMk")
}
// 生成随机订单号
func generateRandomOrderNumber() string {
currentDate := time.Now().Format("20060102")
tradeNO := "sale" + currentDate + RandomNumString(10000000, 99999999)
return tradeNO
}
// 支付
func TestHmJsPayBToCOrder(t *testing.T) {
orderId := generateRandomOrderNumber()
fmt.Println("orderId:", orderId)
totalFee := 0.01
authCode := "281830160266615739"
notifyUrl := "https://dev.switch.deovo.com:8004/api/v1/wxpay/notice"
order, err := HmJsPayBToCOrder(orderId, totalFee, authCode, notifyUrl)
if err != nil {
fmt.Println("err:", err)
}
fmt.Println("order:", order)
}
// 查询
func TestHmQueryOrder(t *testing.T) {
orderId := "sale2023122119561908"
order, err := HmQueryOrder(orderId)
if err != nil {
fmt.Println("err:", err)
}
fmt.Println("order:", order)
}
// 撤销
func TestHmCancelOrder(t *testing.T) {
orderId := "test20231220439"
order, err := HmCancelOrder(orderId)
if err != nil {
fmt.Println("err:", err)
}
fmt.Println("order:", order)
}

View File

@ -31,6 +31,7 @@ type ErpStoreCashier struct {
StoreName string `json:"store_name"` // 门店名称
State uint32 `json:"state"` // 状态:1-使用 2-未用
Remark string `json:"remark" gorm:"type:varchar(512)"` // 备注
Type uint32 `json:"type"` //
}
// CashierStore 账号关联的门店信息
@ -68,6 +69,7 @@ func CreateAccount(req *ErpCashier, storeNums []uint32) error {
Name: req.Name,
BankName: req.BankName,
BankAccount: req.BankAccount,
Type: 1,
State: 1,
Remark: "",
}
@ -97,6 +99,7 @@ func CreateAccount(req *ErpCashier, storeNums []uint32) error {
StoreId: storeNums[i],
State: 1,
Remark: "",
Type: 1,
}
if ok {
storeCategory.StoreName = v
@ -134,6 +137,11 @@ func UpdateAccount(req *ErpCashier, storeNums []uint32) error {
BankAccount: req.BankAccount,
State: 1,
}
var cashierInfo ErpCashier
if err := orm.Eloquent.Table("erp_cashier").Where("id=?", req.ID).Find(&cashierInfo).Error; err != nil {
return fmt.Errorf("更新失败[update cashier err]%v", err)
}
if err := tx.Model(&ErpCashier{}).Where("id=?", req.ID).Updates(cashier).Error; err != nil {
tx.Rollback()
return fmt.Errorf("更新失败[update cashier err]%v", err)
@ -152,6 +160,7 @@ func UpdateAccount(req *ErpCashier, storeNums []uint32) error {
store.Name = req.Name
store.BankName = req.BankName
store.BankAccount = req.BankAccount
store.Type = cashierInfo.Type
if containsStore(storeNums, store.StoreId) { // 更新门店状态
store.State = 1 // 使用
@ -183,6 +192,7 @@ func UpdateAccount(req *ErpCashier, storeNums []uint32) error {
BankName: req.BankName,
BankAccount: req.BankAccount,
State: 1, // 使用
Type: cashierInfo.Type,
}
if err := tx.Create(&newStore).Error; err != nil {
tx.Rollback()
@ -285,6 +295,7 @@ func GetAccountList(storeId, pageSize, pageIndex int) (*ErpCashierListResp, erro
BankName: v.BankName,
BankAccount: v.BankAccount,
State: v.State,
Type: v.Type,
}
temp.Model.ID = v.ErpCashierId
categories = append(categories, temp)

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"go-admin/app/admin/apis/pay"
orm "go-admin/common/global"
"go-admin/logger"
"gorm.io/gorm"
@ -30,6 +31,12 @@ const (
HavePrinted = 2 // 已打印
OnlinePay = 1 // 线上支付(微信/支付宝/云闪付扫码)
PayInit = "pay_init"
Paying = "paying"
PayOk = "pay_ok"
PayFailed = "pay_failed"
PayUnknown = "pay_unknown"
)
type ErpOrder struct {
@ -97,9 +104,22 @@ type ErpOrderCommodity struct {
}
type ErpOrderCashier struct {
CashierId uint32 `json:"cashier_id"` // 收付款方式id
Name string `json:"name"` // 收付款方式名称
Amount uint32 `json:"amount"` // 金额
CashierId uint32 `json:"cashier_id"` // 收付款方式id
Name string `json:"name"` // 收付款方式名称
Amount float64 `json:"amount"` // 金额
}
type ErpOrderRecord struct {
Model
ErpOrderId uint32 `json:"erp_order_id" gorm:"index"` // 零售订单id
BillSn string `json:"bill_sn" gorm:"index"` // 单据编号
OutOrderNo string `json:"out_order_no"` // 商户订单号
PlatTrxNo string `json:"plat_trx_no"` // 平台交易流水号
BankOrderNo string `json:"bank_order_no"` // 银行订单号
TotalAmount float64 `json:"total_amount"` // 订单总金额
PayWay string `json:"pay_way"` // 支付方式
Status string `json:"status"` // 支付状态
}
type ErpOrderCreateReq struct {
@ -149,6 +169,15 @@ type ErpOrderDeleteReq struct {
BillSn string `json:"bill_sn" binding:"required"` // 单据编号
}
type ErpOrderPayReq struct {
BillSn string `json:"bill_sn" binding:"required"` // 单据编号
AuthCode string `json:"auth_code" binding:"required"` // 用户付款码
}
type ErpOrderPayResp struct {
Status string `json:"status"` // 支付成功pay_ok支付失败pay_failed 等待支付paying; 未知状态pay_unknown
}
type ErpOrderAuditReq struct {
BillSn string `json:"bill_sn" binding:"required"` // 单据编号
State int `json:"state" binding:"required"` // 审核操作: 1-审核 2-取消审核
@ -371,9 +400,11 @@ func UpdateStock(gdb *gorm.DB, erpOrder ErpOrder) error {
}
}
}
} else {
return errors.New("订单类型错误")
}
return errors.New("订单类型错误")
return nil
}
// CheckIsOnlinePay 检查是否包含线上支付
@ -556,3 +587,288 @@ func DeleteOrder(req *ErpOrderDeleteReq) error {
return nil
}
// 生成序列号
func GenErpOrderSerialNo() string {
currentDate := time.Now().Format("20060102")
tradeNO := "sale" + currentDate + pay.RandomNumString(10000000, 99999999)
return tradeNO
}
// GetErpOrderSn 生成零售订单号
func GetErpOrderSn() string {
var erpOrderSn string
for {
erpOrderSn = GenErpOrderSerialNo()
var count int64
err := orm.Eloquent.Table("erp_order_record").Where("out_order_no=?", erpOrderSn).Count(&count)
if err != nil {
logger.Error("err:", logger.Field("err", err))
}
if count == 0 {
break
}
}
return erpOrderSn
}
// 查询是否有已支付的订单
func checkIsPayOk(billSn string) bool {
var count int64
err := orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status = ?", billSn, PayOk).
Count(&count).Error
if err != nil {
logger.Error("checkIsPayOk err:", logger.Field("err", err))
}
return count > 0
}
// 查询是否有待支付的订单
func checkIsPaying(billSn string) bool {
var count int64
err := orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status = ?", billSn, Paying).
Count(&count).Error
if err != nil {
logger.Error("checkIsPayOk err:", logger.Field("err", err))
}
return count > 0
}
// 查询是否有刚初始化的订单
func checkIsPayInit(billSn string) bool {
var count int64
err := orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status = ?", billSn, PayInit).
Count(&count).Error
if err != nil {
logger.Error("checkIsPayOk err:", logger.Field("err", err))
}
return count > 0
}
// ErpOrderPay 零售订单支付
func ErpOrderPay(req *ErpOrderPayReq) (*ErpOrderPayResp, error) {
resp := &ErpOrderPayResp{
Status: PayFailed,
}
//通过单据号查询收款金额
var orderInfo ErpOrder
err := orm.Eloquent.Table("erp_order").Where("bill_sn = ?", req.BillSn).Find(&orderInfo).Error
if err != nil {
logger.Error("未查询到订单:", logger.Field("err", err))
return resp, err
}
if orderInfo.PayStatus == HavePaid {
logger.Error("ErpOrderPay err, 订单已支付")
return resp, errors.New("订单已支付")
}
if orderInfo.PayStatus != WaitForPaying {
logger.Error("ErpOrderPay err, 订单不是待支付状态")
return resp, errors.New("订单不是待支付状态")
}
var cashiers []ErpOrderCashier
err = json.Unmarshal([]byte(orderInfo.CashierList), &cashiers)
if err != nil {
logger.Error("unmarshal CashierList err:", logger.Field("err", err))
}
var amount float64
for i, _ := range cashiers {
if cashiers[i].CashierId == OnlinePay { //线上支付
amount = cashiers[i].Amount
break
}
}
if amount == 0 {
logger.Error("ErpOrderPay err, 订单金额为0")
return resp, errors.New("订单金额为0")
}
// 查询是否有已支付的订单
if checkIsPayOk(req.BillSn) {
// 更新零售订单表
err = orm.Eloquent.Model(&ErpOrder{}).Where("id = ?", orderInfo.ID).
Update("pay_status", HavePaid).Error
if err != nil {
logger.Error("ErpOrderPay update erp_order_record err:", logger.Field("err", err))
}
resp.Status = PayOk
logger.Error("ErpOrderPay err2, 订单已支付")
return resp, errors.New("订单已支付")
}
// 查询是否有待支付订单
if checkIsPaying(req.BillSn) {
queryResp, err := QueryErpOrderPayStatus(req.BillSn)
if err != nil {
logger.Error("查询订单失败", logger.Field("err", err))
return resp, err
}
// 等待支付或成功支付则返回,否则新建订单
if queryResp.Status == Paying || queryResp.Status == PayOk {
resp.Status = queryResp.Status
return resp, nil
}
}
// 查询是否有刚初始化的订单
var orderSn string
if checkIsPayInit(req.BillSn) {
var orderRecordInfo ErpOrderRecord
err := orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status = ?", req.BillSn, PayInit).
Find(&orderRecordInfo).Error
if err != nil {
logger.Error("未查询到订单:", logger.Field("err", err))
return resp, errors.New("未查询到待支付订单")
}
orderSn = orderRecordInfo.OutOrderNo
} else {
// 创建支付订单号,并记录到支付记录表,关联零售订单号
orderSn = GetErpOrderSn()
var erpOrderRecord ErpOrderRecord
erpOrderRecord.ErpOrderId = orderInfo.ID
erpOrderRecord.BillSn = orderInfo.BillSn
erpOrderRecord.OutOrderNo = orderSn
erpOrderRecord.Status = PayInit
err = orm.Eloquent.Table("erp_order_record").Create(&erpOrderRecord).Error
if err != nil {
logger.Error("Create erp_order_record err:", logger.Field("err", err))
return resp, err
}
}
resp.Status = Paying
// 发起支付扣款
hmPayResp, err := pay.HmJsPayBToCOrder(orderSn, amount, req.AuthCode, "")
if err != nil {
logger.Error("HmJsPayBToCOrder err")
return resp, err
}
// 更新支付状态
var payStatus string
switch hmPayResp.SubCode {
case "SUCCESS":
payStatus = PayOk
case "FAILED":
payStatus = PayFailed
case "WAITING_PAYMENT":
payStatus = Paying
default:
payStatus = PayUnknown
}
updateInfo := map[string]interface{}{
"bank_order_no": hmPayResp.BankOrderNo,
"plat_trx_no": hmPayResp.PlatTrxNo,
"total_amount": hmPayResp.TotalAmount,
"pay_way": hmPayResp.PayWay,
"status": payStatus,
}
begin := orm.Eloquent.Begin()
err = begin.Model(&ErpOrderRecord{}).Where("out_order_no = ?", orderSn).
Updates(updateInfo).Error
if err != nil {
begin.Rollback()
logger.Error("update erp_order_record err:", logger.Field("err", err))
return resp, err
}
if payStatus == PayOk { // 支付成功
// 更新零售订单表
err = begin.Model(&ErpOrder{}).Where("id = ?", orderInfo.ID).
Update("pay_status", HavePaid).Error
if err != nil {
begin.Rollback()
logger.Error("update erp_order_record err:", logger.Field("err", err))
return resp, err
}
}
err = begin.Commit().Error
if err != nil {
begin.Rollback()
return resp, fmt.Errorf("ErpOrderPay[commit err]%v", err)
}
resp.Status = payStatus
return resp, nil
}
// QueryErpOrderPayStatus 查询零售订单支付状态
func QueryErpOrderPayStatus(billSn string) (*ErpOrderPayResp, error) {
resp := &ErpOrderPayResp{
Status: Paying,
}
// 查询待支付订单
var orderRecordInfo ErpOrderRecord
err := orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status = ?", billSn, Paying).
Find(&orderRecordInfo).Error
if err != nil {
logger.Error("未查询到订单:", logger.Field("err", err))
resp.Status = PayFailed
return resp, errors.New("未查询到待支付订单")
}
// 查询支付状态
hmQueryResp, err := pay.HmQueryOrder(orderRecordInfo.OutOrderNo)
if err != nil {
logger.Error("查询失败:", logger.Field("err", err))
return resp, err
}
// 更新订单状态
var payStatus string
switch hmQueryResp.SubCode {
case "SUCCESS":
payStatus = PayOk
case "FAILED":
payStatus = PayFailed
case "WAITING_PAYMENT":
payStatus = Paying
default:
payStatus = PayUnknown
}
updateInfo := map[string]interface{}{
"bank_order_no": hmQueryResp.BankOrderNo,
"plat_trx_no": hmQueryResp.PlatTrxNo,
"total_amount": hmQueryResp.TotalAmount,
"pay_way": hmQueryResp.PayWayCode,
"status": payStatus,
}
begin := orm.Eloquent.Begin()
err = begin.Model(&ErpOrderRecord{}).Where("out_order_no = ?", orderRecordInfo.OutOrderNo).
Updates(updateInfo).Error
if err != nil {
begin.Rollback()
logger.Error("query update erp_order_record err:", logger.Field("err", err))
return resp, err
}
if payStatus == PayOk { // 支付成功
// 更新零售订单表
err = begin.Model(&ErpOrder{}).Where("id = ?", orderRecordInfo.ID).
Update("pay_status", HavePaid).Error
if err != nil {
begin.Rollback()
logger.Error("query update erp_order_record err:", logger.Field("err", err))
return resp, err
}
}
err = begin.Commit().Error
if err != nil {
begin.Rollback()
return resp, fmt.Errorf("QueryErpOrderPayStatus[commit err]%v", err)
}
resp.Status = payStatus
return resp, nil
}

View File

@ -375,7 +375,7 @@ func checkStockExcel(sheetCols [][]string) error {
for i := 1; i < nMax; i++ { // todo
if i < len(sheetCols[0]) {
if !isExistingProduct(sheetCols[0][i]) {
if !IsExistingProduct(sheetCols[0][i]) {
return errors.New("第" + strconv.Itoa(i+1) + "行商品不存在,请新建商品")
}
}
@ -535,7 +535,8 @@ func determineLowAndMax(sheetCols [][]string) (int, int) {
return nLow, nMax
}
func isExistingProduct(productName string) bool {
// IsExistingProduct 查询商品资料中某个名称的商品是否存在
func IsExistingProduct(productName string) bool {
if productName == "" {
return true
}

View File

@ -88,7 +88,7 @@ const (
const DateTimeFormat = "2006-01-02"
const TimeFormat = "2006-01-02 15-04-05"
const QueryTimeFormat = "2006-01-02T15:04:05-07:00"
const QueryTimeFormat = "2006-01-02T15:04:05+08:00"
const (
ExportUrl = "https://dev.admin.deovo.com/load/export/"

View File

@ -10,11 +10,12 @@ import (
func registerErpOrderManageRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
r := v1.Group("/erp_order").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
r.POST("create", erpordermanage.ErpOrderCreate) // 创建订单
r.POST("edit", erpordermanage.ErpOrderEdit) // 编辑订单
r.POST("list", erpordermanage.ErpOrderList) // 查询订单列表
r.POST("audit", erpordermanage.ErpOrderAudit) // 审核订单
r.POST("delete", erpordermanage.ErpOrderDelete) // 删除订单
r.POST("add_invoice", erpordermanage.ErpOrderAddInvoice) // 开发票
r.POST("pay", erpordermanage.ErpOrderPay) // 收款
r.POST("create", erpordermanage.ErpOrderCreate) // 创建订单
r.POST("edit", erpordermanage.ErpOrderEdit) // 编辑订单
r.POST("list", erpordermanage.ErpOrderList) // 查询订单列表
r.POST("audit", erpordermanage.ErpOrderAudit) // 审核订单
r.POST("delete", erpordermanage.ErpOrderDelete) // 删除订单
r.POST("add_invoice", erpordermanage.ErpOrderAddInvoice) // 开发票
r.POST("pay", erpordermanage.ErpOrderPay) // 收款
r.POST("query_pay_status", erpordermanage.ErpOrderQueryPayStatus) // 收款
}

View File

@ -0,0 +1,16 @@
-----BEGIN RSA PUBLIC KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAL0dNT9QonTb5MML
IZCgIwRvbBWKKdjna/YJurrB4PM12NXjcvNckqTqnAE8p25sHkOPumzeIW7qFzYw
njyPNQlg+uXFEGI74m1zTi265GwI2VqV683YCur+iHE9BORHRACKicsA6r6NMj0w
3tW5AfFG8ZFanvoiBgklyzVZQXI9AgMBAAECgYAwfKOhVyg/8/0kMN9rOFOiGHtK
zoDKNxOMIpR4u6rUULMGq+lWEh8bH3feLVH0Us4znk87CHzj9ir7Xt3DQyhijUit
fZtbwcM8Y504PP6o4L8iWbzD4WzxiGavszVggFy8A8//uY+EsZRfv1ypOR7UxCA0
CcEhFposifjfFO/ZgQJBAOVmuXYn7gFl6iyv2iw0KdwnbT4/Zok8YxA//bGN90d/
nE4A6zMlr0AFNl6UuxIOhtEkMFJTa3//mwnPE9+gWuECQQDTCqWT1czlgOnboFrd
+ZaaVB8Wgo7LhrZJau1t7Ad/VFB7q3sW5Vbzso9J6VHx1PZKhN8TRv0rbdCx+Iy0
YL7dAkBND0/XqBwkbhbXAg+Y78DsCnaBBY0tJpLkf4p6HYVmiDB6Z4aavNr894NH
DFE3BJg3Ix/0V79x1OxAxEYZxeUhAkAznCfv5losSgFmx2lRXYc6CJZtr9Hx7ejI
PZEAz7bpqZnhFIeOitlf0Wv/PELg7B6rDKFtGnAxXbKqw0jNl9c1AkEAkHxmgMbk
++VSr+I9BWH//HC6mRr6NRVnvKcCr6nPsQajQuZxgkPo2/bXePD/DbKopxlkjmLZ
n2ox4tCsiiVapQ==
-----END RSA PUBLIC KEY-----

View File

@ -0,0 +1,6 @@
-----BEGIN RSA PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9HTU/UKJ02+TDCyGQoCMEb2wV
iinY52v2Cbq6weDzNdjV43LzXJKk6pwBPKdubB5Dj7ps3iFu6hc2MJ48jzUJYPrl
xRBiO+Jtc04tuuRsCNlalevN2Arq/ohxPQTkR0QAionLAOq+jTI9MN7VuQHxRvGR
Wp76IgYJJcs1WUFyPQIDAQAB
-----END RSA PUBLIC KEY-----

View File

@ -1678,6 +1678,39 @@ const docTemplate = `{
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.ErpOrderPayReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/models.ErpOrderPayResp"
}
}
}
}
},
"/api/v1/erp_order/query_pay_status": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"零售订单"
],
"summary": "查询付款状态",
"parameters": [
{
"description": "查询付款状态模型",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.ErpOrderDeleteReq"
}
@ -1687,7 +1720,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/app.Response"
"$ref": "#/definitions/models.ErpOrderPayResp"
}
}
}
@ -3840,8 +3873,6 @@ const docTemplate = `{
"basic.CashierCreateRequest": {
"type": "object",
"required": [
"bank_account",
"bank_name",
"name",
"store_num"
],
@ -3882,8 +3913,6 @@ const docTemplate = `{
"basic.CashierEditRequest": {
"type": "object",
"required": [
"bank_account",
"bank_name",
"cashier_id",
"name",
"store_num"
@ -3958,7 +3987,6 @@ const docTemplate = `{
"required": [
"erp_category_id",
"erp_supplier_id",
"imei_type",
"is_imei",
"min_retail_price",
"name",
@ -5180,7 +5208,7 @@ const docTemplate = `{
"properties": {
"amount": {
"description": "金额",
"type": "integer"
"type": "number"
},
"cashier_id": {
"description": "收付款方式id",
@ -5467,6 +5495,32 @@ const docTemplate = `{
}
}
},
"models.ErpOrderPayReq": {
"type": "object",
"required": [
"auth_code",
"bill_sn"
],
"properties": {
"auth_code": {
"description": "用户付款码",
"type": "string"
},
"bill_sn": {
"description": "单据编号",
"type": "string"
}
}
},
"models.ErpOrderPayResp": {
"type": "object",
"properties": {
"status": {
"description": "支付成功pay_ok支付失败pay_failed 等待支付paying; 未知状态pay_unknown",
"type": "string"
}
}
},
"models.ErpStock": {
"type": "object",
"properties": {

View File

@ -1667,6 +1667,39 @@
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.ErpOrderPayReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/models.ErpOrderPayResp"
}
}
}
}
},
"/api/v1/erp_order/query_pay_status": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"零售订单"
],
"summary": "查询付款状态",
"parameters": [
{
"description": "查询付款状态模型",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.ErpOrderDeleteReq"
}
@ -1676,7 +1709,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/app.Response"
"$ref": "#/definitions/models.ErpOrderPayResp"
}
}
}
@ -3829,8 +3862,6 @@
"basic.CashierCreateRequest": {
"type": "object",
"required": [
"bank_account",
"bank_name",
"name",
"store_num"
],
@ -3871,8 +3902,6 @@
"basic.CashierEditRequest": {
"type": "object",
"required": [
"bank_account",
"bank_name",
"cashier_id",
"name",
"store_num"
@ -3947,7 +3976,6 @@
"required": [
"erp_category_id",
"erp_supplier_id",
"imei_type",
"is_imei",
"min_retail_price",
"name",
@ -5169,7 +5197,7 @@
"properties": {
"amount": {
"description": "金额",
"type": "integer"
"type": "number"
},
"cashier_id": {
"description": "收付款方式id",
@ -5456,6 +5484,32 @@
}
}
},
"models.ErpOrderPayReq": {
"type": "object",
"required": [
"auth_code",
"bill_sn"
],
"properties": {
"auth_code": {
"description": "用户付款码",
"type": "string"
},
"bill_sn": {
"description": "单据编号",
"type": "string"
}
}
},
"models.ErpOrderPayResp": {
"type": "object",
"properties": {
"status": {
"description": "支付成功pay_ok支付失败pay_failed 等待支付paying; 未知状态pay_unknown",
"type": "string"
}
}
},
"models.ErpStock": {
"type": "object",
"properties": {

View File

@ -41,8 +41,6 @@ definitions:
type: integer
type: array
required:
- bank_account
- bank_name
- name
- store_num
type: object
@ -74,8 +72,6 @@ definitions:
type: integer
type: array
required:
- bank_account
- bank_name
- cashier_id
- name
- store_num
@ -154,7 +150,6 @@ definitions:
required:
- erp_category_id
- erp_supplier_id
- imei_type
- is_imei
- min_retail_price
- name
@ -1007,7 +1002,7 @@ definitions:
properties:
amount:
description: 金额
type: integer
type: number
cashier_id:
description: 收付款方式id
type: integer
@ -1217,6 +1212,24 @@ definitions:
description: 总条数
type: integer
type: object
models.ErpOrderPayReq:
properties:
auth_code:
description: 用户付款码
type: string
bill_sn:
description: 单据编号
type: string
required:
- auth_code
- bill_sn
type: object
models.ErpOrderPayResp:
properties:
status:
description: 支付成功pay_ok支付失败pay_failed 等待支付paying; 未知状态pay_unknown
type: string
type: object
models.ErpStock:
properties:
commodities:
@ -3556,6 +3569,27 @@ paths:
- application/json
parameters:
- description: 收款模型
in: body
name: request
required: true
schema:
$ref: '#/definitions/models.ErpOrderPayReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/models.ErpOrderPayResp'
summary: 收款
tags:
- 零售订单
/api/v1/erp_order/query_pay_status:
post:
consumes:
- application/json
parameters:
- description: 查询付款状态模型
in: body
name: request
required: true
@ -3567,8 +3601,8 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/app.Response'
summary: 收款
$ref: '#/definitions/models.ErpOrderPayResp'
summary: 查询付款状态
tags:
- 零售订单
/api/v1/inventory/add_remark:

View File

@ -25,6 +25,7 @@ type LogField struct {
}
func init() {
Setup()
}
func Setup() {