fix: 采购价格上限按数量计算总价 (#181)
This commit is contained in:
@@ -156,11 +156,12 @@ func mappingIsValid(context repository.SybOrderContext) bool {
|
||||
}
|
||||
|
||||
// PurchaseTaskRequest 是一条采购任务的人工确认输入。价格单位固定为人民币分。
|
||||
// UnitPriceLimitCent 是采购员确认的单价上限;订单总价上限必须在事务内按最新数量计算。
|
||||
type PurchaseTaskRequest struct {
|
||||
SybID string
|
||||
MaxPriceCent int64
|
||||
MappingOptionKey string
|
||||
ContextVersion string
|
||||
SybID string
|
||||
UnitPriceLimitCent int64
|
||||
MappingOptionKey string
|
||||
ContextVersion string
|
||||
}
|
||||
|
||||
// PurchaseTaskOptions 是一次批量创建共用的执行门禁。
|
||||
@@ -240,6 +241,11 @@ func CreatePurchaseTasksWithOptions(db *sql.DB, actor *model.User, requests []Pu
|
||||
result.Failures = append(result.Failures, TaskCreateError{SybID: request.SybID, Reason: reason})
|
||||
continue
|
||||
}
|
||||
totalPriceLimitCent, err := CalculateOrderPriceLimitCent(request.UnitPriceLimitCent, context.Order.Quantity)
|
||||
if err != nil {
|
||||
result.Failures = append(result.Failures, TaskCreateError{SybID: request.SybID, Reason: err.Error()})
|
||||
continue
|
||||
}
|
||||
taskID, err := repository.NextTaskID(tx, model.TaskPurchase)
|
||||
if err != nil {
|
||||
return PurchaseTaskResult{}, err
|
||||
@@ -252,7 +258,7 @@ func CreatePurchaseTasksWithOptions(db *sql.DB, actor *model.User, requests []Pu
|
||||
GoodsID: context.Order.ShopeeGoodsID,
|
||||
PddGoodsURL: context.PddGoodsURL, PddGoodsID: context.PddGoodsID,
|
||||
PddOptions: optionsJSON, Quantity: context.Order.Quantity,
|
||||
MaxPriceCent: request.MaxPriceCent,
|
||||
MaxPriceCent: totalPriceLimitCent,
|
||||
}); err != nil {
|
||||
return PurchaseTaskResult{}, err
|
||||
}
|
||||
@@ -306,8 +312,8 @@ func validatePurchaseRequest(q repository.Execer, request PurchaseTaskRequest) (
|
||||
if context.Order.Quantity <= 0 {
|
||||
return "采购数量必须大于 0", context, "", nil
|
||||
}
|
||||
if request.MaxPriceCent <= 0 {
|
||||
return "人民币价格上限必须大于 0", context, "", nil
|
||||
if request.UnitPriceLimitCent <= 0 {
|
||||
return "人民币单价上限必须大于 0", context, "", nil
|
||||
}
|
||||
active, err := repository.HasActivePurchaseTask(q, request.SybID)
|
||||
if err != nil {
|
||||
@@ -319,28 +325,42 @@ func validatePurchaseRequest(q repository.Execer, request PurchaseTaskRequest) (
|
||||
return "", context, choice.OptionsJSON, nil
|
||||
}
|
||||
|
||||
// parsePriceYuanToCent 把网页输入的人民币元精确转成分,不使用浮点数。
|
||||
// CalculateOrderPriceLimitCent 用整数分计算订单总价上限,避免浮点误差和乘法溢出。
|
||||
func CalculateOrderPriceLimitCent(unitPriceLimitCent int64, quantity int) (int64, error) {
|
||||
if unitPriceLimitCent <= 0 {
|
||||
return 0, fmt.Errorf("人民币单价上限必须大于 0")
|
||||
}
|
||||
if quantity <= 0 {
|
||||
return 0, fmt.Errorf("采购数量必须大于 0")
|
||||
}
|
||||
if unitPriceLimitCent > math.MaxInt64/int64(quantity) {
|
||||
return 0, fmt.Errorf("订单总价上限过大")
|
||||
}
|
||||
return unitPriceLimitCent * int64(quantity), nil
|
||||
}
|
||||
|
||||
// ParsePriceYuanToCent 把网页输入的人民币单价上限精确转成分,不使用浮点数。
|
||||
func ParsePriceYuanToCent(raw string) (int64, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return 0, fmt.Errorf("价格上限不能为空")
|
||||
return 0, fmt.Errorf("人民币单价上限不能为空")
|
||||
}
|
||||
var whole, fraction string
|
||||
parts := strings.Split(raw, ".")
|
||||
if len(parts) > 2 {
|
||||
return 0, fmt.Errorf("价格格式不正确")
|
||||
return 0, fmt.Errorf("单价格式不正确")
|
||||
}
|
||||
whole = parts[0]
|
||||
if len(parts) == 2 {
|
||||
fraction = parts[1]
|
||||
}
|
||||
if whole == "" || len(fraction) > 2 {
|
||||
return 0, fmt.Errorf("价格最多保留两位小数")
|
||||
return 0, fmt.Errorf("单价最多保留两位小数")
|
||||
}
|
||||
for _, value := range []string{whole, fraction} {
|
||||
for _, r := range value {
|
||||
if r < '0' || r > '9' {
|
||||
return 0, fmt.Errorf("价格只能填写数字")
|
||||
return 0, fmt.Errorf("单价只能填写数字")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -351,17 +371,17 @@ func ParsePriceYuanToCent(raw string) (int64, error) {
|
||||
}
|
||||
var yuan, cent int64
|
||||
if _, err := fmt.Sscan(whole, &yuan); err != nil {
|
||||
return 0, fmt.Errorf("价格格式不正确")
|
||||
return 0, fmt.Errorf("单价格式不正确")
|
||||
}
|
||||
if _, err := fmt.Sscan(fraction, ¢); err != nil {
|
||||
return 0, fmt.Errorf("价格格式不正确")
|
||||
return 0, fmt.Errorf("单价格式不正确")
|
||||
}
|
||||
if yuan > (math.MaxInt64-cent)/100 {
|
||||
return 0, fmt.Errorf("价格上限过大")
|
||||
return 0, fmt.Errorf("人民币单价上限过大")
|
||||
}
|
||||
total := yuan*100 + cent
|
||||
if total <= 0 {
|
||||
return 0, fmt.Errorf("价格上限必须大于 0")
|
||||
return 0, fmt.Errorf("人民币单价上限必须大于 0")
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user