package payment import ( "crypto/rand" "fmt" "math/big" "strconv" "strings" "sync" "time" ) /* ---------------- 金额工具 ---------------- */ // FenToYuan 分 -> 元(两位小数字符串),供渠道下单参数使用 func FenToYuan(fen int64) string { if fen < 0 { fen = 0 } return strconv.FormatInt(fen/100, 10) + "." + fmt.Sprintf("%02d", fen%100) } // YuanToFen 元(字符串)-> 分 func YuanToFen(yuan string) (int64, error) { yuan = strings.TrimSpace(yuan) if yuan == "" { return 0, nil } f, err := strconv.ParseFloat(yuan, 64) if err != nil { return 0, fmt.Errorf("金额格式不正确: %s", yuan) } if f < 0 { return 0, fmt.Errorf("金额不能为负数: %s", yuan) } return int64(f*100 + 0.5), nil } /* ---------------- 单号生成 ---------------- 规则:前缀 + yyyyMMddHHmmss + 4 位随机数字(进程内串行生成,避免同秒碰撞; 落库仍有唯一键兜底,撞号时由调用方重试一次)。 支付单 P202609151024310001 退回单 R202609151024310001 佣金单 CM202609151024310001 对账批次 RC202609151024310001 ---------------------------------------- */ var ( noMu sync.Mutex lastNoTime int64 lastNoSeq int64 ) func nextSerial(prefix string) string { noMu.Lock() defer noMu.Unlock() now := time.Now().Unix() seq := int64(0) if now == lastNoTime { seq = lastNoSeq + 1 } lastNoTime = now lastNoSeq = seq n, _ := rand.Int(rand.Reader, big.NewInt(10000)) return fmt.Sprintf("%s%s%04d%04d", prefix, time.Now().Format("20060102150405"), seq, n.Int64()) } // NextPayNo 生成支付单号 func NextPayNo() string { return nextSerial("P") } // NextRefundNo 生成退回单号 func NextRefundNo() string { return nextSerial("R") } // NextCommissionNo 生成佣金单号 func NextCommissionNo() string { return nextSerial("CM") } // NextBatchNo 生成对账批次号 func NextBatchNo() string { return nextSerial("RC") }