This commit is contained in:
2026-01-16 17:01:36 +08:00
parent 23ad0477b2
commit 663ad1af0d
8 changed files with 600 additions and 0 deletions

View File

@@ -683,3 +683,172 @@ func (y *YunYinSignPayService) QueryPayeeBill(ctx context.Context, sourceOrderCo
RefundAmount: billItem.RefundAmount,
}, nil
}
// RefundPayeeBillRequest 退款请求
type RefundPayeeBillRequest struct {
SourceOrderCode string `json:"sourceOrderCode,omitempty"` // 来源订单号(与 participateId 二选一)
ParticipateId int64 `json:"participateId,omitempty"` // 参与方ID与 sourceOrderCode 二选一)
RefundAmount float64 `json:"refundAmount"` // 退款金额(必填)
RefundReason string `json:"refundReason,omitempty"` // 退款原因(可选)
}
// RefundPayeeBillResponse 退款响应
type RefundPayeeBillResponse struct {
Code interface{} `json:"code"` // 返回码,可能是字符串"200"或数字200
Msg string `json:"msg"` // 返回码的描述信息
Data interface{} `json:"data,omitempty"`
}
// GetCodeInt 获取 code 的 int 值
func (r *RefundPayeeBillResponse) GetCodeInt() int {
switch v := r.Code.(type) {
case int:
return v
case float64:
return int(v)
case string:
if v == "200" {
return 200
}
return 0
default:
return 0
}
}
// RefundPayeeBill 发起退款
func (y *YunYinSignPayService) RefundPayeeBill(ctx context.Context, sourceOrderCode string, participateId int64, refundAmount float64, refundReason string) error {
// 1. 获取token和操作ID带缓存
accessToken, err := y.GetAccessToken(ctx)
if err != nil {
return fmt.Errorf("获取云印签token失败: %v", err)
}
operationUserId, err := y.GetUserId(ctx, accessToken)
if err != nil {
return fmt.Errorf("获取云印签操作ID失败: %v", err)
}
// 2. 如果只提供了 sourceOrderCode需要先查询收款单获取 participateId
if participateId == 0 && sourceOrderCode != "" {
// 查询收款单列表获取 participateId
reqBody := QueryPayeeBillRequest{
SourceOrderCode: sourceOrderCode,
ListPageNo: 1,
ListPageSize: 10,
}
jsonData, err := json.Marshal(reqBody)
if err != nil {
return fmt.Errorf("序列化查询请求失败: %v", err)
}
url := fmt.Sprintf("%s/signFlowBill/payeeBillList", y.config.ApiURL)
httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(jsonData))
if err != nil {
return fmt.Errorf("创建查询请求失败: %v", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("appId", y.config.AppID)
httpReq.Header.Set("accessToken", accessToken)
httpReq.Header.Set("userId", operationUserId)
httpReq.Header.Set("source", "pc")
resp, err := y.client.Do(httpReq)
if err != nil {
return fmt.Errorf("查询请求失败: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("读取查询响应失败: %v", err)
}
var queryResp QueryPayeeBillResponse
if err := json.Unmarshal(body, &queryResp); err != nil {
return fmt.Errorf("解析查询响应失败: %v, 响应内容: %s", err, string(body))
}
codeInt := queryResp.GetCodeInt()
if codeInt != 200 {
return fmt.Errorf("查询收款单失败: %s", queryResp.Msg)
}
if len(queryResp.Data) == 0 {
return fmt.Errorf("未找到匹配的收款单记录,订单号: %s", sourceOrderCode)
}
// 获取第一条记录的 participateId
billItem := queryResp.Data[0]
participateId = billItem.ParticipateID
logx.Infof("通过订单号查询到参与方ID: %d", participateId)
}
// 3. 验证参数
if participateId == 0 {
return fmt.Errorf("参与方ID不能为空请提供 participateId 或 sourceOrderCode")
}
if refundAmount <= 0 {
return fmt.Errorf("退款金额必须大于0")
}
// 4. 构建退款请求
refundReq := RefundPayeeBillRequest{
RefundAmount: refundAmount,
RefundReason: refundReason,
}
// 优先使用 participateId
if participateId > 0 {
refundReq.ParticipateId = participateId
} else if sourceOrderCode != "" {
// 如果 participateId 仍然为0使用 sourceOrderCode
refundReq.SourceOrderCode = sourceOrderCode
}
jsonData, err := json.Marshal(refundReq)
if err != nil {
return fmt.Errorf("序列化退款请求失败: %v", err)
}
// 5. 调用退款API
url := fmt.Sprintf("%s/signFlowBill/refund", y.config.ApiURL)
httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(jsonData))
if err != nil {
return fmt.Errorf("创建退款请求失败: %v", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("appId", y.config.AppID)
httpReq.Header.Set("accessToken", accessToken)
httpReq.Header.Set("userId", operationUserId)
httpReq.Header.Set("source", "pc")
resp, err := y.client.Do(httpReq)
if err != nil {
return fmt.Errorf("退款请求失败: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("读取退款响应失败: %v", err)
}
var refundResp RefundPayeeBillResponse
if err := json.Unmarshal(body, &refundResp); err != nil {
return fmt.Errorf("解析退款响应失败: %v, 响应内容: %s", err, string(body))
}
// 6. 检查响应码
codeInt := refundResp.GetCodeInt()
if codeInt != 200 {
return fmt.Errorf("退款失败: %s", refundResp.Msg)
}
logx.Infof("云印签退款成功参与方ID: %d, 退款金额: %.2f, 退款原因: %s", participateId, refundAmount, refundReason)
return nil
}