This commit is contained in:
邹宗楠
2025-04-27 10:21:07 +08:00
parent a900238a6b
commit 103197adf0
8 changed files with 533 additions and 9 deletions

View File

@@ -1161,7 +1161,7 @@ func GetStoresSkusNew(ctx *jxcontext.Context, storeIDs, skuIDs []int, upcs []str
SELECT SQL_CALC_FOUND_ROWS
t3.id store_id, t3.name store_name, t3.pay_percentage,
t1.*,
t2.name_id, t2.id sku_id, t2.spec_quality sku_spec_quality, t2.spec_unit sku_spec_unit, t2.weight, t2m.vendor_thing_id sku_jd_id,
t2.name_id, t2.id sku_id, t2.spec_quality sku_spec_quality, t2.spec_unit sku_spec_unit, t2.weight, t2m.vendor_thing_id sku_jd_id,t2m.remark jd_sku_remark,
t2.comment, t2.category_id sku_category_id, t2.status sku_status, t2.eclp_id,
t4.created_at bind_created_at, t4.updated_at bind_updated_at, t4.last_operator bind_last_operator, t4.deleted_at bind_deleted_at,
t4.sub_store_id, t4.price bind_price, IF(t4.unit_price IS NOT NULL, t4.unit_price, t1.price) unit_price, t4.status store_sku_status, t4.auto_sale_at,

View File

@@ -278,6 +278,9 @@ const (
OrderStatusAccepted = 10 // 已经接单,也即待出库,待拣货
OrderStatusFinishedPickup = 15 // 拣货完成
OrderStatusWaitPay = 23 // 京西订单,等待支付
OrderStatusPayFail = 24 // 京西订单,支付错误
OrderStatusApplyFailedGetGoods = 17 // 取货失败待审核
OrderStatusAgreeFailedGetGoods = 18 // 取货失败

View File

@@ -301,13 +301,14 @@ type StoreSkuExt struct {
// VendorInfoMap用于替换其之下的所有信息
VendorInfoMap map[int]*StoreSkuVendorInfo `json:"vendorInfoMap,omitempty"`
JdID string `orm:"column(sku_jd_id);null;index" json:"jdID"`
EbaiID string `orm:"column(ebai_id);index" json:"ebaiID"`
MtwmID string `orm:"column(mtwm_id)" json:"mtwmID"` // 这个也不是必须的只是为了DAO取数据语句一致
YbID string `orm:"column(yb_id);index" json:"ybID"`
DdID string `orm:"column(dd_id);index" json:"ddID"`
JdsID string `orm:"column(jds_id);index" json:"jdsID"`
TaoID string `orm:"column(tao_id)" json:"taoID"` // 这个也不是必须的只是为了DAO取数据语句一致
JdRemark string `orm:"column(jd_sku_remark)" json:"jdRemark"`
JdID string `orm:"column(sku_jd_id);null;index" json:"jdID"`
EbaiID string `orm:"column(ebai_id);index" json:"ebaiID"`
MtwmID string `orm:"column(mtwm_id)" json:"mtwmID"` // 这个也不是必须的只是为了DAO取数据语句一致
YbID string `orm:"column(yb_id);index" json:"ybID"`
DdID string `orm:"column(dd_id);index" json:"ddID"`
JdsID string `orm:"column(jds_id);index" json:"jdsID"`
TaoID string `orm:"column(tao_id)" json:"taoID"` // 这个也不是必须的只是为了DAO取数据语句一致
JdSyncStatus int8 `orm:"default(2)" json:"jdSyncStatus"`
EbaiSyncStatus int8 `orm:"default(2)" json:"ebaiSyncStatus"`

View File

@@ -197,12 +197,13 @@ func (c *DeliveryHandler) callbackMsg2Waybill(msg *dadaapi.CallbackMsg) (retVal
updateTime = updateTime / 1000
}
retVal.StatusTime = utils.Timestamp2Time(updateTime)
retVal.VendorOrderID, retVal.OrderVendorID = jxutils.SplitUniversalOrderID(msg.OrderID)
//retVal.VendorOrderID, retVal.OrderVendorID = jxutils.SplitUniversalOrderID(msg.OrderID)
sql := `SELECT * FROM goods_order WHERE vendor_order_id = ? ORDER BY order_created_at DESC LIMIT 1 OFFSET 0`
sqlParams := []interface{}{msg.OrderID}
dao.GetRow(dao.GetDB(), &goods, sql, sqlParams)
retVal.OrderVendorID = goods.VendorID
retVal.VendorOrderID = goods.VendorOrderID
return retVal, goods
}

View File

@@ -0,0 +1,422 @@
package localjx
import (
"encoding/json"
"fmt"
"git.rosy.net.cn/baseapi/platformapi/tonglianpayapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"time"
)
var (
TransactionStatus = map[string]string{
"0000": "交易成功",
"1001": "交易不存在",
"2008": "交易处理中,请查询交易,如果是实时交易(例如刷卡支付,交易撤销,退货),建议每隔一段时间(10秒)查询交易",
"2000": "交易处理中,请查询交易,如果是实时交易(例如刷卡支付,交易撤销,退货),建议每隔一段时间(10秒)查询交易",
"3888": "流水号重复",
"3099": "渠道商户错误",
"3014": "交易金额小于应收手续费",
"3031": "校验实名信息失败",
"3088": "交易未支付",
"3089": "撤销异常,如已影响资金24小时内会做差错退款处理",
"3050": "交易已被撤销",
"3999": "1",
"3889": "1",
"3045": "1",
}
)
// BarCodeScannerPay 面对面扫码支付(扫码枪)
func BarCodeScannerPay(vendorOrderId string, userPaymentLabel string) (payCode string, err error) {
db := dao.GetDB()
goodsOrder, err := dao.GetSimpleOrder(db, vendorOrderId)
if err != nil {
return "", err
}
if goodsOrder.VendorID != model.VendorIDJX {
return "", fmt.Errorf("此订单不是京西订单,无法使用该功能")
}
storeDetail, err := dao.GetStoreDetail(db, goodsOrder.JxStoreID, 0, "")
if err != nil {
return "", err
}
param := &tonglianpayapi.OnlinePayParam{
Trxamt: utils.Int64ToStr(goodsOrder.ActualPayPrice),
Reqsn: goodsOrder.VendorOrderID,
Body: storeDetail.Name,
Authcode: userPaymentLabel, // 支付授权码
Chnlstoreid: utils.Int2Str(goodsOrder.JxStoreID),
NotifyUrl: tonglianpayapi.OnlinePayCallbackUrl,
Signtype: tonglianpayapi.EncryptionRsa,
}
terminfo := &tonglianpayapi.TerminfoBase{
Termno: fmt.Sprintf("%d_1", goodsOrder.JxStoreID),
Devicetype: "10",
Termsn: fmt.Sprintf("%d_1", goodsOrder.JxStoreID),
Longitude: fmt.Sprintf("+%s", utils.Float64ToStr(jxutils.IntCoordinate2Standard(storeDetail.Lng))),
Latitude: fmt.Sprintf("+%s", utils.Float64ToStr(jxutils.IntCoordinate2Standard(storeDetail.Lat))),
}
terminfoBByte, _ := json.Marshal(terminfo)
param.Terminfo = string(terminfoBByte)
vendorCode, trxId, remake, err := api.TLpayAPI.CreateOnlineBankOrder(param)
if err != nil {
return "", err
}
if vendorCode == tonglianpayapi.TrxStatusSuccess {
goodsOrder.Status = model.OrderStatusFinished
} else if vendorCode == tonglianpayapi.TrxStatusTransaction || vendorCode == tonglianpayapi.TrxStatusTransaction2 {
goodsOrder.Status = model.OrderStatusWaitPay
} else {
goodsOrder.Status = model.OrderStatusPayFail
}
goodsOrder.VendorStatus = vendorCode
goodsOrder.VendorOrderID2 = trxId
dao.UpdateEntity(db, goodsOrder, "Status", "VendorStatus", "VendorOrderID2")
partner.CurOrderManager.OnOrderMsg(goodsOrder, vendorCode, fmt.Sprintf(vendorCode+":"+TransactionStatus[vendorCode]+":%s", remake))
if vendorCode == "3045" || vendorCode == "3999" || vendorCode == "3889" {
return remake, nil
}
return TransactionStatus[vendorCode], nil
}
// OnTLOnlinePayCallback 通联扫码枪扫码支付回调
func OnTLOnlinePayCallback(call *tonglianpayapi.CallBackResultOnlinePay) (err error) {
switch call.TrxCode {
case tonglianpayapi.MsgTypePay, tonglianpayapi.MsgTypePayZFB:
err = onTLOnlinePayFinished(call)
case tonglianpayapi.MsgTypeRefund, tonglianpayapi.MsgTypeRefundZFB:
err = onTLOnlinePayRefund(call)
default:
}
return err
}
func onTLOnlinePayFinished(call *tonglianpayapi.CallBackResultOnlinePay) (err error) {
db := dao.GetDB()
if order, err := partner.CurOrderManager.LoadOrder(call.CusorderID, model.VendorIDJX); err == nil {
if call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
loc, _ := time.LoadLocation("Local")
t1, _ := time.ParseInLocation("20060102150405", call.PayTime, loc)
order.OrderFinishedAt = t1
order.Status = model.OrderStatusFinished
} else if call.TrxStatus == tonglianpayapi.TrxStatusTransaction || call.TrxStatus == tonglianpayapi.TrxStatusTransaction2 {
order.Status = model.OrderStatusWaitPay
} else {
order.Status = model.OrderStatusPayFail
}
order.VendorStatus = call.TrxStatus
dao.UpdateEntity(db, order, "Status", "OrderFinishedAt", "VendorStatus")
partner.CurOrderManager.OnOrderMsg(order, call.TrxStatus, fmt.Sprintf(call.TrxStatus+":"+TransactionStatus[call.TrxStatus]+":支付结果回调成功"))
//if call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
// err = OnPayFinished(orderPay)
//}
} else {
globals.SugarLogger.Debugf("onTLpayFinished msg:%s, err:%v", utils.Format4Output(call, true), err)
}
return err
}
func onTLOnlinePayRefund(call *tonglianpayapi.CallBackResultOnlinePay) (err error) {
db := dao.GetDB()
afsOrder, err := partner.CurOrderManager.LoadAfsOrder(call.CusorderID, model.VendorIDJX)
if err != nil {
return err
}
if afsOrder == nil {
return fmt.Errorf("CusorderID 错误")
}
goodsOrder, err := partner.CurOrderManager.LoadOrder(afsOrder.VendorOrderID, model.VendorIDJX)
if err != nil {
return err
}
//partner.CurOrderManager.OnOrderMsg(goodsOrder, call.TrxStatus, fmt.Sprintf(call.TrxStatus+":"+TransactionStatus[call.TrxStatus]+":支付结果回调成功"))
// 退款成功
if call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
afsOrder.VendorStatus = call.TrxStatus
afsOrder.Status = model.AfsOrderStatusFinished
afsOrder.AfsFinishedAt = time.Now()
dao.UpdateEntity(db, afsOrder, "VendorStatus", "Status", "AfsFinishedAt")
}
// 是否全退取消订单
orderSkuCount := 0
for _, v := range goodsOrder.Skus {
orderSkuCount += v.Count
}
afsSkus, err := dao.GetStoreAfsOrderSkuList2(db, []string{goodsOrder.VendorOrderID})
if err != nil {
return err
}
refundSkuCount := 0
for _, v := range afsSkus {
refundSkuCount += v.Count
}
if orderSkuCount == refundSkuCount {
goodsOrder.Status = model.OrderStatusCanceled
goodsOrder.VendorStatus = call.TrxStatus
dao.UpdateEntity(db, goodsOrder, "Status", "VendorStatus")
}
return err
}
// QueryBarCodeScannerStatus 刷新当前订单的支付状态
func QueryBarCodeScannerStatus(vendorOrderId string) (vendorStatus string, err error) {
db := dao.GetDB()
goodsOrder, err := dao.GetSimpleOrder(db, vendorOrderId)
if err != nil {
return "", err
}
if goodsOrder.VendorID != model.VendorIDJX {
return "", fmt.Errorf("此订单不是京西订单,无法使用该功能")
}
param := &tonglianpayapi.PayTransactionStatusQuery{
Reqsn: goodsOrder.VendorOrderID,
Trxid: "",
Randomstr: utils.GetUUID(),
Signtype: tonglianpayapi.EncryptionRsa,
Sign: "",
}
vendorCode, remake, err := api.TLpayAPI.QueryPayTransaction(param)
if err != nil {
return "", err
}
if vendorCode == tonglianpayapi.TrxStatusSuccess {
goodsOrder.OrderFinishedAt = time.Now()
goodsOrder.Status = model.OrderStatusFinished
} else if vendorCode == tonglianpayapi.TrxStatusTransaction || vendorCode == tonglianpayapi.TrxStatusTransaction2 {
goodsOrder.Status = model.OrderStatusWaitPay
} else {
goodsOrder.Status = model.OrderStatusPayFail
}
goodsOrder.VendorStatus = vendorCode
dao.UpdateEntity(db, goodsOrder, "Status", "VendorStatus", "OrderFinishedAt")
partner.CurOrderManager.OnOrderMsg(goodsOrder, vendorCode, fmt.Sprintf("扫码枪扫码刷新结果:[code:%s,说明:%s,异常原因:%s],本地状态[%d]", vendorCode, TransactionStatus[vendorCode], remake, goodsOrder.Status))
if vendorCode == "3045" || vendorCode == "3999" || vendorCode == "3889" {
return remake, nil
}
return TransactionStatus[vendorCode], nil
}
// RefundBarCodeScannerOrder 扫码支付订单退单
func RefundBarCodeScannerOrder(ctx *jxcontext.Context, vendorOrderId string, skuIds map[int]int, reason string) (err error) {
db := dao.GetDB()
txDB, _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db, txDB)
if r != nil {
panic(r)
}
}
}()
goodsOrder, err := partner.CurOrderManager.LoadOrder(vendorOrderId, model.VendorIDJX)
if err != nil {
dao.Rollback(db, txDB)
return err
}
// 检查订单售后
refundType, refundMoney, err := checkJxAfsOrder(db, goodsOrder, vendorOrderId, skuIds)
if err != nil {
dao.Rollback(db, txDB)
return err
}
// 本地添加售后数据
afsOrder, orderSkuFinancial, afsOrderSkuFinancial, err := addRefundSku(ctx, goodsOrder, skuIds, reason, refundType)
if err != nil {
dao.Rollback(db, txDB)
return err
}
// 通知退款,等待回传
status, errMsg, err := api.TLpayAPI.OrderRefund(&tonglianpayapi.OnLineOrderRefundParam{
Trxamt: utils.Int2Str(refundMoney),
Reqsn: afsOrder.AfsOrderID,
Oldreqsn: goodsOrder.VendorOrderID,
Oldtrxid: "",
Remark: reason,
Benefitdetail: "",
Randomstr: utils.GetUUID(),
Signtype: tonglianpayapi.EncryptionRsa,
Sign: "",
})
if err != nil {
return err
}
switch status {
case "0000", "1001", "2008", "2000":
if err = dao.CreateEntity(db, afsOrder); err != nil {
dao.Rollback(db, txDB)
return err
}
if err = dao.CreateMultiEntities(db, orderSkuFinancial); err != nil {
dao.Rollback(db, txDB)
return err
}
if err = dao.CreateMultiEntities(db, afsOrderSkuFinancial); err != nil {
dao.Rollback(db, txDB)
return err
}
dao.Commit(db, txDB)
default:
return fmt.Errorf(errMsg)
}
return nil
}
func checkJxAfsOrder(db *dao.DaoDB, goodsOrder *model.GoodsOrder, vendorOrderId string, skuIds map[int]int) (bool, int, error) {
if goodsOrder.Status != model.OrderStatusFinished {
return false, 0, fmt.Errorf("订单未完成支付,无法退款(刷新订单状态/联系管理员)")
}
// 未结束的售后单,不让继续添加售后单了
completeAfsOrder, _ := dao.GetAfsOrders(db, model.VendorIDJX, vendorOrderId, "")
if len(completeAfsOrder) != model.NO {
for _, v := range completeAfsOrder {
if v.Status != model.AfsOrderStatusFinished {
return false, 0, fmt.Errorf("此订单还有未完成的售后单,请稍后再处理")
}
}
}
totalCount := 0 // 总的商品个数
skuList := make(map[int]*model.OrderSku, 0)
for _, v := range goodsOrder.Skus {
skuList[v.SkuID] = v
totalCount += v.Count
}
// 已经完成商品个数+当次录入商品个数>订单商品个数 不让继续退了
afsSkuList, _ := dao.GetStoreAfsOrderSkuList2(db, []string{vendorOrderId}) // 已经退款商品
afsCount := 0 // 总的售后个数
for _, v := range afsSkuList {
if v.Count+skuIds[v.SkuID] > skuList[v.SkuID].Count {
return false, 0, fmt.Errorf("商品[%s],记录购买个数为:%d个,退款个数为:%d个,请检查输入", skuList[v.SkuID].SkuName, skuList[v.SkuID].Count, v.Count+skuIds[v.SkuID])
}
afsCount += v.Count + skuIds[v.SkuID]
}
if afsCount > totalCount {
return false, 0, fmt.Errorf("退货商品数计算结果大于订单商品数,请联系管理员")
}
totalRefundMoney := 0 // 总退款金额
for skuId, skuCount := range skuIds {
if skuList[skuId] != nil && skuList[skuId].Count >= skuCount {
totalRefundMoney += int(skuList[skuId].SalePrice) * skuCount
}
}
return totalCount == afsCount, totalRefundMoney, nil
}
func addRefundSku(ctx *jxcontext.Context, goodsOrder *model.GoodsOrder, skuIds map[int]int, reason string, refundType bool) (afsOrder *model.AfsOrder, skuFinancial, afsSkuFinancial []*model.OrderSkuFinancial, err error) {
refundMoney := 0 // 退款金额
afsOrderID := utils.Int64ToStr(jxutils.GenAfsOrderNo()) // 退款Id
orderSkuFinancial := make([]*model.OrderSkuFinancial, 0, len(goodsOrder.Skus)) // 所有商品
afsOrderSkuFinancial := make([]*model.OrderSkuFinancial, 0, len(skuIds)) // 申请退款商品
for _, v := range goodsOrder.Skus {
orderSku := &model.OrderSkuFinancial{
ModelIDCUL: model.ModelIDCUL{
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
LastOperator: ctx.GetUserName(),
},
VendorID: model.VendorIDJX,
VendorOrderID: goodsOrder.VendorOrderID,
VendorSubOrderID: "",
AfsOrderID: "",
IsAfsOrder: 0,
VendorStoreID: utils.Int2Str(goodsOrder.JxStoreID),
StoreID: goodsOrder.JxStoreID,
JxStoreID: goodsOrder.JxStoreID,
VendorSkuID: v.VendorSkuID,
SkuID: v.SkuID,
JxSkuID: v.JxSkuID,
PromotionType: 0,
Name: v.SkuName,
ShopPrice: v.ShopPrice,
SalePrice: v.SalePrice,
Count: v.Count,
SkuBoxMoney: 0,
UserMoney: v.SalePrice,
PmSubsidyMoney: 0,
PmSkuSubsidyMoney: 0,
PmDeductionsMoney: 0,
ShopMoney: v.SalePrice,
ShopMoneyByCal: 0,
JxSubsidyMoney: 0,
JxSkuSubsidyMoney: 0,
JxDeductionsMoney: 0,
JxShopMoney: v.SalePrice,
RefundMoney: 0,
RefundMoneyByCal: 0,
StoreSubID: 0,
StoreSubName: "",
}
orderSkuFinancial = append(orderSkuFinancial, orderSku)
if skuIds[v.SkuID] != 0 && v.Count >= skuIds[v.SkuID] {
refundMoney += int(v.SalePrice) * v.Count
orderSku.AfsOrderID = afsOrderID
orderSku.IsAfsOrder = 1
orderSku.Count = skuIds[v.SkuID]
orderSku.RefundMoney = v.SalePrice * int64(v.Count)
afsOrderSkuFinancial = append(afsOrderSkuFinancial, orderSku)
}
}
afsOrder = &model.AfsOrder{
ModelIDCUL: model.ModelIDCUL{
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
LastOperator: ctx.GetUserName(),
},
VendorID: model.VendorIDJX,
AfsOrderID: afsOrderID,
VendorOrderID: goodsOrder.VendorOrderID,
VendorStoreID: utils.Int2Str(goodsOrder.StoreID),
StoreID: goodsOrder.StoreID,
AfsCreatedAt: time.Now(),
VendorAppealType: utils.Int2Str(model.VendorIDJX),
AppealType: model.AfsAppealTypeReturnAndRefund,
VendorReasonType: "",
ReasonType: model.AfsReasonNotOthers,
ReasonDesc: utils.LimitUTF8StringLen(reason, 1024),
ReasonImgList: "",
RefundType: model.AfsTypePartRefund,
VendorOrgCode: "0",
RefundMoney: int64(refundMoney),
Status: model.AfsOrderStatusNew,
Flag: 0,
AfsTotalShopMoney: goodsOrder.ActualPayPrice - int64(refundMoney),
}
if refundType {
afsOrder.RefundType = model.AfsTypeFullRefund
}
return afsOrder, orderSkuFinancial, afsOrderSkuFinancial, err
}