daShangDao_psiServer/service/wangdian.go
2026-06-23 14:26:46 +08:00

188 lines
6.7 KiB
Go

package service
import (
"encoding/json"
"fmt"
"log"
"psi/config"
"psi/database"
"psi/models"
"psi/utils"
"strconv"
"time"
"gorm.io/gorm"
)
type WangdianPurchaseInfo struct {
ProviderNo string `json:"provider_no"`
WarehouseNo string `json:"warehouse_no"`
OuterNo string `json:"outer_no"`
IsUseOuterNo int8 `json:"is_use_outer_no,omitempty"`
IsCheck int8 `json:"is_check,omitempty"`
Contact string `json:"contact,omitempty"`
PurchaseName string `json:"purchase_name,omitempty"`
Telno string `json:"telno,omitempty"`
ReceiveAddress string `json:"receive_address,omitempty"`
LogisticsType int8 `json:"logistics_type,omitempty"` // 暂未使用 运货方式(物流公司)
ExpectArriveTime string `json:"expect_arrive_time,omitempty"`
Remark string `json:"remark,omitempty"`
OtherFee float64 `json:"other_fee,omitempty"` // 暂未使用 其他费用
PostFee float64 `json:"post_fee,omitempty"` // 暂未使用 邮资
Prop1 string `json:"prop1,omitempty"` // 暂未使用 自定义属性1
Prop2 string `json:"prop2,omitempty"` // 暂未使用 自定义属性2
DetailsList []WangdianPurchaseDetailItem `json:"details_list"`
}
type WangdianPurchaseDetailItem struct {
SpecNo string `json:"spec_no"`
Num float64 `json:"num"`
Price float64 `json:"price"`
Discount float64 `json:"discount,omitempty"`
Tax float64 `json:"tax,omitempty"` //暂未使用 税率
TaxPrice float64 `json:"tax_price,omitempty"` //暂未使用 税后单价
TaxAmount float64 `json:"tax_amount,omitempty"` //暂未使用 税后金额
Remark string `json:"remark,omitempty"`
Prop1 string `json:"prop1,omitempty"` // 暂未使用 自定义属性1
Prop2 string `json:"prop2,omitempty"` // 暂未使用 自定义属性2
}
type WangdianPurchasePushResponse struct {
Code int `json:"code"`
Message string `json:"message"`
}
func PushPurchaseOrder(purchaseOrderID int64, db ...*gorm.DB) (*WangdianPurchasePushResponse, error) {
databaseConn := database.OptionalDB(db...)
dbName := databaseConn.Migrator().CurrentDatabase()
log.Printf("[旺店通推送] 使用数据库: %s", dbName)
// 1. 查询采购单
var po models.PurchaseOrder
if err := databaseConn.Where("id = ? AND is_del = 0", purchaseOrderID).First(&po).Error; err != nil {
return nil, fmt.Errorf("采购单不存在: %v", err)
}
// 2. 查询供应商编号(必须)
var supplier models.Supplier
if err := databaseConn.Select("code, contact_person, contact_phone, address").
Where("id = ? AND is_del = 0", po.SupplierID).First(&supplier).Error; err != nil {
return nil, fmt.Errorf("获取供应商信息失败: %v", err)
}
if supplier.Code == "" {
return nil, fmt.Errorf("供应商编码为空,请先维护供应商档案")
}
// 3. 查询仓库编号(必须)
var warehouse models.Warehouse
if err := databaseConn.Select("code, contact_person, contact_phone, address").
Where("id = ? AND is_del = 0", po.WarehouseID).First(&warehouse).Error; err != nil {
return nil, fmt.Errorf("获取仓库信息失败: %v", err)
}
if warehouse.Code == "" {
return nil, fmt.Errorf("仓库编码为空,请先维护仓库档案")
}
// 4. 查询采购明细,关联商品条码
type itemRow struct {
models.PurchaseOrderItem
Barcode string `gorm:"column:barcode"`
}
var items []itemRow
if err := databaseConn.Table("purchase_order_item").
Select("purchase_order_item.*, product.barcode").
Joins("LEFT JOIN product ON purchase_order_item.product_id = product.id AND product.is_del = 0").
Where("purchase_order_item.purchase_order_id = ? AND purchase_order_item.is_del = 0", purchaseOrderID).
Debug().Scan(&items).Error; err != nil {
return nil, fmt.Errorf("获取采购明细失败: %v", err)
}
if len(items) == 0 {
return nil, fmt.Errorf("采购明细为空,无法推送")
}
// 5. 组装 details_list
detailList := make([]WangdianPurchaseDetailItem, 0, len(items))
for _, it := range items {
specNo := it.Barcode
if specNo == "" {
specNo = strconv.FormatInt(it.ProductID, 10)
}
detailList = append(detailList, WangdianPurchaseDetailItem{
SpecNo: specNo,
Num: float64(it.Quantity),
Price: float64(it.UnitPrice) / 100.0,
Discount: 1.0,
})
}
// 6. 组装 purchase_info
info := WangdianPurchaseInfo{
ProviderNo: supplier.Code,
WarehouseNo: warehouse.Code,
OuterNo: po.PoNo,
IsUseOuterNo: 0,
IsCheck: 0,
Contact: warehouse.ContactPerson,
PurchaseName: warehouse.ContactPerson,
Telno: warehouse.ContactPhone,
ReceiveAddress: warehouse.Address,
Remark: po.Remark,
DetailsList: detailList,
}
if po.ExpectedArrivalDate > 0 {
info.ExpectArriveTime = time.Unix(po.ExpectedArrivalDate, 0).Format("2006-01-02 15:04:05")
}
infoBytes, err := json.Marshal(info)
if err != nil {
return nil, fmt.Errorf("序列化采购信息失败: %v", err)
}
// 7. 校验配置
cfg := config.AppConfig.Wangdian
if cfg.Sid == "" || cfg.AppKey == "" || cfg.AppSecret == "" {
return nil, fmt.Errorf("旺店通接口配置不完整(sid/appkey/appsecret)")
}
// 8. 组装公共请求参数 + 计算签名
timestamp := time.Now().Unix()
params := map[string]string{
"sid": cfg.Sid,
"appkey": cfg.AppKey,
"timestamp": strconv.FormatInt(timestamp, 10),
"purchase_info": string(infoBytes),
}
params["sign"] = utils.WangdianSign(params, cfg.AppSecret)
// 9. 提交请求
url := cfg.URL
if cfg.Sandbox && cfg.SandboxURL != "" {
url = cfg.SandboxURL
}
if url == "" {
url = "https://api.wangdian.cn/openapi2/purchase_order_push.php"
}
timeout := cfg.Timeout
if timeout <= 0 {
timeout = 30
}
log.Printf("[旺店通推送] 请求URL: %s (sandbox=%v, form-urlencoded), 参数: sid=%s appkey=%s timestamp=%s sign=%s", url, cfg.Sandbox, cfg.Sid, cfg.AppKey, params["timestamp"], params["sign"])
log.Printf("[旺店通推送] purchase_info: %s", string(infoBytes))
respBody, err := utils.SubmitFormDataWithTimeout(url, params, timeout)
if err != nil {
return nil, fmt.Errorf("推送请求失败: %v", err)
}
log.Printf("[旺店通推送] 返回信息: %s", string(respBody))
var resp WangdianPurchasePushResponse
if err := json.Unmarshal([]byte(respBody), &resp); err != nil {
return nil, fmt.Errorf("解析响应失败(raw=%s): %v", respBody, err)
}
return &resp, nil
}