@@ -18,6 +18,8 @@ func FinUserFlowOrderActionString(kind int) string { | |||||
return "green_coin_double_chain_exchange_for_balance" | return "green_coin_double_chain_exchange_for_balance" | ||||
case md.StoreSubsidyOrdActionForFinUserFlow: | case md.StoreSubsidyOrdActionForFinUserFlow: | ||||
return "subsidy" | return "subsidy" | ||||
case md.InstallmentPaymentAutoRepaidForFinUserFlow: | |||||
return "installment_payment_auto_repaid" | |||||
default: | default: | ||||
return "unknown" | return "unknown" | ||||
} | } | ||||
@@ -19,6 +19,7 @@ const ( | |||||
GreenEnergyExchangeForBalanceTitleForFinUserFlow = "兑换账户余额" | GreenEnergyExchangeForBalanceTitleForFinUserFlow = "兑换账户余额" | ||||
BalanceExchangeForGreenEnergyTitleForFinUserFlow = "账户余额兑换" | BalanceExchangeForGreenEnergyTitleForFinUserFlow = "账户余额兑换" | ||||
GreenCoinDoubleChainExchangeForBalanceTitleForFinUserFlow = "绿色积分双链兑换账户余额" | GreenCoinDoubleChainExchangeForBalanceTitleForFinUserFlow = "绿色积分双链兑换账户余额" | ||||
InstallmentPaymentAutoRepaidTitleForFinUserFlow = "分期付-自动扣款" | |||||
) | ) | ||||
const ( | const ( | ||||
@@ -30,6 +31,7 @@ const ( | |||||
GreenEnergyExchangeForBalanceForFinUserFlow = 112 // 兑换账户余额 | GreenEnergyExchangeForBalanceForFinUserFlow = 112 // 兑换账户余额 | ||||
BalanceExchangeForGreenEnergyForFinUserFlow = 113 // 账户余额兑换 | BalanceExchangeForGreenEnergyForFinUserFlow = 113 // 账户余额兑换 | ||||
GreenCoinDoubleChainExchangeForBalanceForFinUserFlow = 114 // 绿色积分双链兑换账户余额 | GreenCoinDoubleChainExchangeForBalanceForFinUserFlow = 114 // 绿色积分双链兑换账户余额 | ||||
InstallmentPaymentAutoRepaidForFinUserFlow = 116 // 分期付-自动扣款 | |||||
) | ) | ||||
const DealUserAmountRequestIdPrefix = "%s:deal_user_amount:%d" | const DealUserAmountRequestIdPrefix = "%s:deal_user_amount:%d" | ||||
@@ -0,0 +1,20 @@ | |||||
package enum | |||||
// InstallmentPaymentListIsRepaidOff 分期付列表 - 是否已偿还完 | |||||
type InstallmentPaymentListIsRepaidOff int | |||||
const ( | |||||
InstallmentPaymentListRepaidOff InstallmentPaymentListIsRepaidOff = iota | |||||
InstallmentPaymentListRepaidOn | |||||
) | |||||
func (i InstallmentPaymentListIsRepaidOff) String() string { | |||||
switch i { | |||||
case InstallmentPaymentListRepaidOff: | |||||
return "未偿还完" | |||||
case InstallmentPaymentListRepaidOn: | |||||
return "已偿还完" | |||||
default: | |||||
return "未知" | |||||
} | |||||
} |
@@ -0,0 +1,164 @@ | |||||
package installment_payment | |||||
import ( | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/enum" | |||||
md2 "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/md" | |||||
enum2 "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/rule/installment_payment/enum" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/rule/installment_payment/md" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/svc" | |||||
zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils" | |||||
zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_model.git/src/implement" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_model.git/src/models" | |||||
"errors" | |||||
"fmt" | |||||
"github.com/shopspring/decimal" | |||||
"time" | |||||
"xorm.io/xorm" | |||||
) | |||||
// AddInstallmentPaymentList 新增 "分期付-订单" 记录 | |||||
func AddInstallmentPaymentList(engine *xorm.Engine, masterId string, args md.AddInstallmentPaymentListStruct) (err error) { | |||||
now := time.Now() | |||||
//1、查找对应方案记录 | |||||
installmentPaymentSchemeDb := implement.NewInstallmentPaymentSchemeDb(engine) | |||||
scheme, err := installmentPaymentSchemeDb.GetInstallmentPaymentSchemeById(args.SchemeId) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
if scheme == nil { | |||||
return fmt.Errorf("installmentPaymentSchemeDb.InstallmentPaymentSchemeById returned nil") | |||||
} | |||||
//2、计算相关数据 | |||||
installmentNumsValue := decimal.NewFromInt32(int32(scheme.InstallmentNums)) //分期数 | |||||
totalAmount, _ := decimal.NewFromString(args.TotalAmount) //总金额 | |||||
repaymentAmountPerInstallment := totalAmount.Div(installmentNumsValue).String() //每期还款金额 | |||||
//3、新增 installment_payment_list 记录 | |||||
installmentPaymentListDb := implement.NewInstallmentPaymentListDb(engine) | |||||
_, err = installmentPaymentListDb.InsertInstallmentPaymentList(&models.InstallmentPaymentList{ | |||||
Uid: args.Uid, | |||||
GoodsId: args.GoodsId, | |||||
OrdId: args.OrdId, | |||||
TotalAmount: args.TotalAmount, | |||||
InstallmentNums: scheme.InstallmentNums, | |||||
AlreadyRepaidInstallmentNums: 0, | |||||
RepaymentAmountPerInstallment: repaymentAmountPerInstallment, | |||||
IsRepaidOff: 0, | |||||
CreateAt: now.Format("2006-01-02 15:04:05"), | |||||
UpdateAt: now.Format("2006-01-02 15:04:05"), | |||||
}) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return | |||||
} | |||||
// InstallmentPaymentManualRepaid 手动还款 | |||||
func InstallmentPaymentManualRepaid(engine *xorm.Engine, masterId string, args md.InstallmentPaymentManualRepaid) (err error) { | |||||
now := time.Now() | |||||
session := engine.NewSession() | |||||
defer func() { | |||||
session.Close() | |||||
if err := recover(); err != nil { | |||||
_ = zhios_order_relate_logx.Error(err) | |||||
} | |||||
}() | |||||
session.Begin() | |||||
//1、查找对应`installment_payment_list`记录 | |||||
installmentPaymentListDb := implement.NewInstallmentPaymentListDb(engine) | |||||
installmentPaymentList, err := installmentPaymentListDb.GetInstallmentPaymentListById(args.RecordId) | |||||
if err != nil { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
if installmentPaymentList == nil { | |||||
session.Rollback() | |||||
return fmt.Errorf("installmentPaymentListDb.InstallmentPaymentListById returned nil") | |||||
} | |||||
//2、计算相关数据 | |||||
repaidAmount, _ := decimal.NewFromString(args.RepaidAmount) //还款总金额 | |||||
repaymentAmountPerInstallmentValue, _ := decimal.NewFromString(installmentPaymentList.RepaymentAmountPerInstallment) //每期还款金额 | |||||
installmentNumsValue := decimal.NewFromInt32(int32(installmentPaymentList.InstallmentNums)) //分期数 | |||||
alreadyRepaidInstallmentNumsValue := decimal.NewFromInt32(int32(installmentPaymentList.AlreadyRepaidInstallmentNums)) //已还分期数 | |||||
residueRepaidInstallmentNumsValue := installmentNumsValue.Sub(alreadyRepaidInstallmentNumsValue) //剩余分期数 | |||||
residueRepaidTotalAmountValue := residueRepaidInstallmentNumsValue.Mul(repaymentAmountPerInstallmentValue) //剩余待还总金额 | |||||
if args.RepaidKind == 1 { | |||||
//判断还款金额是否正确 | |||||
if installmentPaymentList.RepaymentAmountPerInstallment != args.RepaidAmount { | |||||
session.Rollback() | |||||
return errors.New("还款金额与按期还款金额不一致") | |||||
} | |||||
//按期还款 | |||||
installmentPaymentList.AlreadyRepaidInstallmentNums++ | |||||
if installmentPaymentList.AlreadyRepaidInstallmentNums == installmentPaymentList.InstallmentNums { | |||||
installmentPaymentList.IsRepaidOff = int(enum2.InstallmentPaymentListRepaidOn) //TODO::已还完 | |||||
} | |||||
} else { | |||||
//判断还款金额是否正确 | |||||
if repaidAmount.GreaterThan(residueRepaidTotalAmountValue) { | |||||
session.Rollback() | |||||
return errors.New("还款金额大于剩余待还总金额") | |||||
} | |||||
if repaidAmount.LessThan(residueRepaidTotalAmountValue) { | |||||
session.Rollback() | |||||
return errors.New("还款金额小于剩余待还总金额") | |||||
} | |||||
//全额还款 | |||||
installmentPaymentList.AlreadyRepaidInstallmentNums++ | |||||
installmentPaymentList.IsRepaidOff = int(enum2.InstallmentPaymentListRepaidOn) //TODO::已还完 | |||||
} | |||||
//3、处理用户余额 | |||||
orderType := enum.FinUserFlowOrderActionString(md2.InstallmentPaymentAutoRepaidForFinUserFlow) | |||||
var dealUserAmount = md2.DealUserAmount{ | |||||
Kind: "sub", | |||||
Mid: masterId, | |||||
Title: md2.InstallmentPaymentAutoRepaidTitleForFinUserFlow, | |||||
OrderType: orderType, | |||||
OrdAction: md2.InstallmentPaymentAutoRepaidForFinUserFlow, | |||||
OrdId: zhios_order_relate_utils.Int64ToStr(installmentPaymentList.OrdId), | |||||
Uid: args.Uid, | |||||
Amount: zhios_order_relate_utils.StrToFloat64(args.RepaidAmount), | |||||
} | |||||
err = svc.DealUserAmount(session, dealUserAmount) | |||||
if err != nil { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
//4、新增 installment_payment_repaid_flow 记录 | |||||
installmentPaymentRepaidFlowDb := implement.NewInstallmentPaymentRepaidFlowDb(engine) | |||||
_, err = installmentPaymentRepaidFlowDb.InsertInstallmentPaymentRepaidFlowBySession(session, &models.InstallmentPaymentRepaidFlow{ | |||||
RecordId: installmentPaymentList.Id, | |||||
Uid: installmentPaymentList.Uid, | |||||
Amount: installmentPaymentList.RepaymentAmountPerInstallment, | |||||
WhichRepaymentPeriod: installmentPaymentList.AlreadyRepaidInstallmentNums, | |||||
CreateAt: now.Format("2006-01-02 15:04:05"), | |||||
UpdateAt: now.Format("2006-01-02 15:04:05"), | |||||
}) | |||||
if err != nil { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
//5、修改`installment_payment_list` 记录 | |||||
updateAffected, err := installmentPaymentListDb.UpdateInstallmentPaymentListBySess(session, installmentPaymentList, "already_repaid_installment_nums", "is_repaid_off") | |||||
if err != nil { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
if updateAffected <= 0 { | |||||
session.Rollback() | |||||
return errors.New("更新 `installment_payment_list` 记录失败") | |||||
} | |||||
err = session.Commit() | |||||
if err != nil { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
return | |||||
} |
@@ -0,0 +1,142 @@ | |||||
package installment_payment | |||||
import ( | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/enum" | |||||
md2 "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/md" | |||||
enum2 "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/rule/installment_payment/enum" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/rule/installment_payment/md" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/svc" | |||||
zhios_order_relate_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/cache" | |||||
zhios_order_relate_logx "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/logx" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_model.git/src/implement" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_model.git/src/models" | |||||
"errors" | |||||
"fmt" | |||||
"time" | |||||
"xorm.io/xorm" | |||||
) | |||||
const LockKey = "installment_payment_auto_repaid_lock_key" | |||||
// InstallmentPaymentAutoRepaid 分期付自动还款 | |||||
func InstallmentPaymentAutoRepaid(engine *xorm.Engine, masterId string, ch *rabbit.Channel) (err error) { | |||||
//TODO::增加“悲观锁”防止串行 | |||||
getString, _ := cache.GetString(LockKey) | |||||
if getString != "" { | |||||
fmt.Println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", "上一次结算未执行完") | |||||
return errors.New("上一次结算未执行完") | |||||
} | |||||
cache.SetEx(LockKey, "running", 3600*1) //1小时 | |||||
//1、查找出"可用余额"大于"每期还款金额"的用户数据 | |||||
var sqlStr = "SELECT ipl.*, up.fin_valid, up.uid FROM `installment_payment_list` ipl " + | |||||
"JOIN `user_profile` up ON up.uid = ipl.uid " + | |||||
"WHERE ipl.is_repaid_off =0 AND up.fin_valid > ipl.repayment_amount_per_installment;" | |||||
nativeStringResultMap, _ := db.QueryNativeString(engine, sqlStr) | |||||
//2、整理用户余额数据 | |||||
var userFinValid = map[string]float64{} | |||||
for _, resultMap := range nativeStringResultMap { | |||||
userFinValid[resultMap["uid"]] = zhios_order_relate_utils.StrToFloat64(resultMap["fin_valid"]) | |||||
} | |||||
//2、将符合条件的数据推入mq队列 | |||||
for _, resultMap := range nativeStringResultMap { | |||||
if userFinValid[resultMap["uid"]] >= zhios_order_relate_utils.StrToFloat64(resultMap["repayment_amount_per_installment"]) { | |||||
//TODO::推入rabbitmq 异步处理 | |||||
ch.Publish(md.InstallmentPaymentExchange, md.InstallmentPaymentStructForAutoRepaid{ | |||||
MasterId: masterId, | |||||
Id: zhios_order_relate_utils.StrToInt(resultMap["id"]), | |||||
Uid: zhios_order_relate_utils.StrToInt(resultMap["uid"]), | |||||
GoodsId: zhios_order_relate_utils.StrToInt(resultMap["goods_id"]), | |||||
OrdId: zhios_order_relate_utils.StrToInt64(resultMap["ord_id"]), | |||||
TotalAmount: resultMap["total_amount"], | |||||
InstallmentNums: zhios_order_relate_utils.StrToInt(resultMap["installment_nums"]), | |||||
AlreadyRepaidInstallmentNums: zhios_order_relate_utils.StrToInt(resultMap["already_repaid_installment_nums"]), | |||||
RepaymentAmountPerInstallment: resultMap["repayment_amount_per_installment"], | |||||
IsRepaidOff: zhios_order_relate_utils.StrToInt(resultMap["is_repaid_off"]), | |||||
}, md.InstallmentPaymentRoutKeyForAutoRepaid) | |||||
userFinValid[resultMap["uid"]] -= zhios_order_relate_utils.StrToFloat64(resultMap["repayment_amount_per_installment"]) | |||||
} | |||||
} | |||||
cache.Del(LockKey) //删除悲观锁 | |||||
return | |||||
} | |||||
// DealInstallmentPaymentAutoRepaid 处理分期付自动还款 | |||||
func DealInstallmentPaymentAutoRepaid(engine *xorm.Engine, args md.InstallmentPaymentStructForAutoRepaid, masterId string) (err error) { | |||||
now := time.Now() | |||||
session := engine.NewSession() | |||||
defer func() { | |||||
session.Close() | |||||
if err := recover(); err != nil { | |||||
_ = zhios_order_relate_logx.Error(err) | |||||
} | |||||
}() | |||||
session.Begin() | |||||
//1、查询出对应`installment_payment_list` 数据 | |||||
installmentPaymentListDb := implement.NewInstallmentPaymentListDb(engine) | |||||
installmentPaymentList, err := installmentPaymentListDb.GetInstallmentPaymentListById(args.Id) | |||||
if err != nil { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
installmentPaymentList.AlreadyRepaidInstallmentNums++ | |||||
if installmentPaymentList.AlreadyRepaidInstallmentNums == installmentPaymentList.InstallmentNums { | |||||
installmentPaymentList.IsRepaidOff = int(enum2.InstallmentPaymentListRepaidOn) //TODO::已还完 | |||||
} | |||||
//2、处理用户余额 | |||||
orderType := enum.FinUserFlowOrderActionString(md2.InstallmentPaymentAutoRepaidForFinUserFlow) | |||||
var dealUserAmount = md2.DealUserAmount{ | |||||
Kind: "sub", | |||||
Mid: masterId, | |||||
Title: md2.InstallmentPaymentAutoRepaidTitleForFinUserFlow, | |||||
OrderType: orderType, | |||||
OrdAction: md2.InstallmentPaymentAutoRepaidForFinUserFlow, | |||||
OrdId: zhios_order_relate_utils.Int64ToStr(args.OrdId), | |||||
Uid: args.Uid, | |||||
Amount: zhios_order_relate_utils.StrToFloat64(installmentPaymentList.RepaymentAmountPerInstallment), | |||||
} | |||||
err = svc.DealUserAmount(session, dealUserAmount) | |||||
if err != nil { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
//3、新增`installment_payment_repaid_flow`数据 | |||||
installmentPaymentRepaidFlowDb := implement.NewInstallmentPaymentRepaidFlowDb(engine) | |||||
_, err = installmentPaymentRepaidFlowDb.InsertInstallmentPaymentRepaidFlowBySession(session, &models.InstallmentPaymentRepaidFlow{ | |||||
RecordId: installmentPaymentList.Id, | |||||
Uid: installmentPaymentList.Uid, | |||||
Amount: installmentPaymentList.RepaymentAmountPerInstallment, | |||||
WhichRepaymentPeriod: installmentPaymentList.AlreadyRepaidInstallmentNums, | |||||
CreateAt: now.Format("2006-01-02 15:04:05"), | |||||
UpdateAt: now.Format("2006-01-02 15:04:05"), | |||||
}) | |||||
if err != nil { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
//4、修改`installment_payment_list` 记录 | |||||
updateAffected, err := installmentPaymentListDb.UpdateInstallmentPaymentListBySess(session, installmentPaymentList, "already_repaid_installment_nums", "is_repaid_off") | |||||
if err != nil { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
if updateAffected <= 0 { | |||||
session.Rollback() | |||||
return errors.New("更新 `installment_payment_list` 记录失败") | |||||
} | |||||
err = session.Commit() | |||||
if err != nil { | |||||
session.Rollback() | |||||
return err | |||||
} | |||||
return | |||||
} |
@@ -0,0 +1,14 @@ | |||||
package installment_payment | |||||
import ( | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/md" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/utils/cache" | |||||
) | |||||
func Init(redisAddr string) (err error) { | |||||
if redisAddr != "" { | |||||
cache.NewRedis(redisAddr) | |||||
} | |||||
_, err = cache.SelectDb(md.RedisDataBase) | |||||
return | |||||
} |
@@ -0,0 +1,18 @@ | |||||
package md | |||||
type AddInstallmentPaymentListStruct struct { | |||||
MasterId string `json:"master_id"` | |||||
Uid int `json:"uid"` | |||||
GoodsId int `json:"goods_id"` | |||||
OrdId int64 `json:"ord_id"` | |||||
TotalAmount string `json:"total_amount"` //总金额 | |||||
SchemeId int `json:"scheme_id"` //分期方案Id | |||||
} | |||||
type InstallmentPaymentManualRepaid struct { | |||||
MasterId string `json:"master_id"` | |||||
Uid int `json:"uid"` | |||||
RecordId int `json:"record_id"` //对应 installment_payment_list(分期付-订单列表)id | |||||
RepaidAmount string `json:"repaid_amount"` //还款金额 | |||||
RepaidKind int32 `json:"repaid_kind"` //还款方式(1:按期还款 2:全额还款) | |||||
} |
@@ -0,0 +1,20 @@ | |||||
package md | |||||
const InstallmentPaymentExchange = "installment.payment" | |||||
const ( | |||||
InstallmentPaymentRoutKeyForAutoRepaid = "auto_repaid" // 自动还款 | |||||
) | |||||
type InstallmentPaymentStructForAutoRepaid struct { | |||||
MasterId string `json:"master_id"` | |||||
Id int `json:"id"` | |||||
Uid int `json:"uid"` | |||||
GoodsId int `json:"goods_id"` | |||||
OrdId int64 `json:"ord_id"` | |||||
TotalAmount string `json:"total_amount"` | |||||
InstallmentNums int `json:"installment_nums"` | |||||
AlreadyRepaidInstallmentNums int `json:"already_repaid_installment_nums"` | |||||
RepaymentAmountPerInstallment string `json:"repayment_amount_per_installment"` | |||||
IsRepaidOff int `json:"is_repaid_off"` | |||||
} |