@@ -18,6 +18,8 @@ func FinUserFlowOrderActionString(kind int) string { | |||
return "green_coin_double_chain_exchange_for_balance" | |||
case md.StoreSubsidyOrdActionForFinUserFlow: | |||
return "subsidy" | |||
case md.InstallmentPaymentAutoRepaidForFinUserFlow: | |||
return "installment_payment_auto_repaid" | |||
default: | |||
return "unknown" | |||
} | |||
@@ -19,6 +19,7 @@ const ( | |||
GreenEnergyExchangeForBalanceTitleForFinUserFlow = "兑换账户余额" | |||
BalanceExchangeForGreenEnergyTitleForFinUserFlow = "账户余额兑换" | |||
GreenCoinDoubleChainExchangeForBalanceTitleForFinUserFlow = "绿色积分双链兑换账户余额" | |||
InstallmentPaymentAutoRepaidTitleForFinUserFlow = "分期付-自动扣款" | |||
) | |||
const ( | |||
@@ -30,6 +31,7 @@ const ( | |||
GreenEnergyExchangeForBalanceForFinUserFlow = 112 // 兑换账户余额 | |||
BalanceExchangeForGreenEnergyForFinUserFlow = 113 // 账户余额兑换 | |||
GreenCoinDoubleChainExchangeForBalanceForFinUserFlow = 114 // 绿色积分双链兑换账户余额 | |||
InstallmentPaymentAutoRepaidForFinUserFlow = 116 // 分期付-自动扣款 | |||
) | |||
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"` | |||
} |