1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
func (s *wealthAdminService) NomineeOrderConfirmPreCheck(ctx *fiber.Ctx, r *mapping.NomineeOrderConfirmPreCheckReqParams) error {
l := log.FromContext(ctx.UserContext())
userContext := ctx.UserContext()
resp := mapping.NomineeOrderConfirmPreCheckResult{Orders: mapping.NomineeOrderConfirmPreCheckResultOrders{},
Config: mapping.NomineeOrderConfirmPreCheckResultConfig{
Redeem: mapping.NomineeOrderConfirmPreCheckResultConfigRedeem{
QuantityDifferenceUpperLimit: s.Config.GetString("wealthAdmin.redeemQuantityDifferenceUpperLimit"),
QuantityDifferenceLowerLimit: s.Config.GetString("wealthAdmin.redeemQuantityDifferenceLowerLimit"),
},
}}
m, err := s.dbAccess.GetWealthNomineeOrderById(userContext, r.Params.OrderId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
l.Info("wealth nominee order not found", zap.Error(err), zap.Int("nomineeOrderId", r.Params.OrderId))
return code.Response(ctx, code.NewDefaultError(code.ErrNotFound), nil)
}
l.Error("query nominee wealth order failed", zap.Error(err), zap.Int("nomineeOrderId", r.Params.OrderId))
return code.Response(ctx, code.NewDefaultError(code.ErrDatabase), nil)
}
// 确认份额上一个状态必须是上手已受理
if !mapping.CheckNomineeOrderChangeAllow(r.Params.Ops.OrderStatus, m.OrderStatus) {
return code.Response(ctx, code.NewError(code.ErrInvalidBody, "last order status must be accept", nil), nil)
}
err = copier.CopyWithOption(&resp.NomineeOrder, m, copier.Option{IgnoreEmpty: true})
if err != nil {
l.Info("copy database model to resp model failed", zap.Error(err))
return code.Response(ctx, code.NewDefaultError(code.ErrBuildResp), nil)
}
err = copier.CopyWithOption(&resp.NomineeOrder, r.Params.Ops, copier.Option{IgnoreEmpty: true})
if err != nil {
l.Info("copy req model to resp model failed", zap.Error(err))
return code.Response(ctx, code.NewDefaultError(code.ErrBuildResp), nil)
}
var precision int32 // 默认份额精度
// 查找指定产品的小数位
// 查找指定产品的小数位
channel := utils.GenerateChannel(ctx.Get(constant.HeaderXSource))
params := mmapping.ProductBasicInfoParams{
ProductCode: m.ProductCode,
Channel: channel,
}
if f, err := s.MKTWealthClient.GetProductBasicInfo(ctx.UserContext(), params); err != nil {
l.Warn("get fund product info failed", zap.Error(err),
zap.Int("orderId", m.Id), zap.String("productCode", m.ProductCode))
} else {
precision = f.GetPrecision()
}
orders, err := s.dbAccess.GetWealthOrdersByNomineeId(ctx.UserContext(), nil, m.Id, "applyAmount desc, submitTime asc") // 按照创建时间倒序
if err != nil {
l.Error("search wealth orders failed", zap.Error(err), zap.Int("nomineeOrderId", r.Params.OrderId))
return code.Response(ctx, code.NewDefaultError(code.ErrUnknow), nil)
}
if len(orders) == 0 {
return code.Response(ctx, nil, resp)
}
// 拆单计算
// 认购计算比例用的是认购金额
// 赎回计算比例用的是赎回份额
var result map[string][]decimal.Decimal
switch m.Direction {
case orderconstant.W_TRX_DIR_BUY:
orderApplyAmounts, err := wealth.TWealthOrders(orders).ApplyAmounts()
if err != nil {
l.Info("collect orders applyAmount failed", zap.Error(err))
return code.Response(ctx, code.NewDefaultError(code.ErrUnknow), nil)
}
result = quantitycalcuator.AllocateGetQuantityByAmount(orderApplyAmounts,
r.Params.Ops.FilledAmountDecimal(), r.Params.Ops.FilledQuantityDecimal(), precision, orderconstant.DefaultCashPrecision)
case orderconstant.W_TRX_DIR_SELL:
orderApplyQuantities, err := wealth.TWealthOrders(orders).ApplyQuantities()
if err != nil {
l.Info("collect orders applyQuantities failed", zap.Error(err))
return code.Response(ctx, code.NewDefaultError(code.ErrUnknow), nil)
}
result = quantitycalcuator.AllocateGetAmountByQuantity(orderApplyQuantities,
r.Params.Ops.FilledAmountDecimal(), r.Params.Ops.FilledQuantityDecimal(), precision, orderconstant.DefaultCashPrecision)
default:
l.Info("order direction unknow", zap.Int("nomineeOrderId", r.Params.OrderId), zap.Int("direction", m.Direction))
return code.Response(ctx, code.NewDefaultError(code.ErrUnknow), nil)
}
orderFilledAmounts := result["FinalAmount"]
orderFilledQuantity := result["FinalQuantity"]
for i, order := range orders {
filledAmount := orderFilledAmounts[i].StringFixed(orderconstant.DefaultCashPrecision)
filledQuantity := orderFilledQuantity[i].RoundDown(precision).StringFixed(precision)
resp.Orders = append(resp.Orders, mapping.NomineeOrderConfirmPreCheckResultOrder{
OrderId: order.Id,
OrderNumber: order.OrderNumber,
SubAccountId: order.SubAccountId,
ApplyAmount: order.ApplyAmount,
ApplyQuantity: order.ApplyQuantity,
ApplyPrice: order.ApplyPrice,
FilledAmount: filledAmount,
FilledQuantity: filledQuantity,
FilledPrice: r.Params.Ops.FilledPrice,
SourceType: order.SourceType,
})
}
if err := resp.Fix(precision); err != nil {
l.Info("fix resp data failed", zap.Error(err))
return code.Response(ctx, code.NewDefaultError(code.ErrBuildResp), nil)
}
return code.Response(ctx, nil, resp)
}