123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543 |
- // Copyright 2019 getensh.com. All rights reserved.
- // Use of this source code is governed by getensh.com.
- package fee
- import (
- "context"
- "encoding/json"
- "fmt"
- "git.getensh.com/common/gopkgs/database"
- "git.getensh.com/common/gopkgs/logger"
- "go.uber.org/zap"
- "google.golang.org/grpc/status"
- "gorm.io/gorm"
- "property-garden/errors"
- "property-garden/impl/v1/charge_utils"
- dbmodel "property-garden/model"
- pb_v1 "property-garden/pb/v1"
- "property-garden/utils"
- "time"
- )
- // 计算相差月份
- func calcMonthDiff(start, end int64) int {
- if start > end {
- return 0
- }
- startTime := time.Unix(start, 0)
- endTime := time.Unix(end, 0)
- // 开始时间-结束时间 +1个月 eg:5月到9月 = (9-5) = 4个月
- return (endTime.Year()-startTime.Year())*12 + int(endTime.Month()-startTime.Month())
- }
- // 检查账单结束时间是否比计费开始时间更早
- func checkEndTime(endTime string, start int64) int64 {
- t, err := time.ParseInLocation("2006-01", endTime, time.Local)
- if err != nil {
- fmt.Println("err:", err)
- return 0
- }
- if t.Unix() == start {
- return 0
- }
- // 如果账最后一次单生成时间在计费时间前不生成
- if t.Before(time.Unix(start, 0)) {
- return 0
- }
- return t.Unix()
- }
- // 计算上期账单截止时间按费用生效时间
- func calcBillEndByChargeEffectiveTime(billPeriod int32, chargeEffectiveTime int64) int64 {
- now := time.Now()
- nowTimestamp := now.Unix()
- // 计费开始时间大于当前时间,不生成
- if nowTimestamp < chargeEffectiveTime {
- return 0
- }
- endTime := ""
- countMount := 0
- switch billPeriod {
- case charge_utils.BillPeriodMonth:
- // 月度账单
- // 返回本月
- endTime = now.Format("2006-01")
- case charge_utils.BillPeriodThreeMonth:
- // 季度账单
- countMount = 3
- case charge_utils.BillPeriodSixMonth:
- // 半年账单
- countMount = 6
- case charge_utils.BillPeriodYear:
- // 一年账单
- countMount = 12
- default:
- return 0
- }
- if endTime == "" {
- diffYear := now.Year() - time.Unix(chargeEffectiveTime, 0).Year()
- diffMonth := now.Month() - time.Unix(chargeEffectiveTime, 0).Month()
- totolMount := diffYear*12 + int(diffMonth)
- if totolMount < countMount {
- return 0
- }
- diff := totolMount % countMount
- endTime = now.AddDate(0, -diff, 0).Format("2006-01")
- }
- return checkEndTime(endTime, chargeEffectiveTime)
- }
- // 计算上期账单截止时间按自然周期
- func calcBillEndByNaturePeriod(billPeriod int32, chargeEffectiveTime int64) int64 {
- now := time.Now()
- month := now.Month()
- year := now.Year()
- nowTimestamp := now.Unix()
- // 计费开始时间大于当前时间,不生成
- if nowTimestamp < chargeEffectiveTime {
- return 0
- }
- endTime := ""
- switch billPeriod {
- case charge_utils.BillPeriodMonth:
- // 月度账单
- // 返回本月
- endTime = now.Format("2006-01")
- case charge_utils.BillPeriodThreeMonth:
- // 季度账单
- // 1-3月生成上年10-12月账单
- if month >= time.January && month <= time.March {
- endTime = fmt.Sprintf("%d-01", year)
- } else if month >= time.April && month <= time.June {
- endTime = fmt.Sprintf("%d-04", year)
- } else if month >= time.July && month <= time.September {
- endTime = fmt.Sprintf("%d-07", year)
- } else {
- endTime = fmt.Sprintf("%d-10", year)
- }
- case charge_utils.BillPeriodSixMonth:
- // 半年账单7,1
- // 1-6月生成上年7-12月账单
- if month >= time.January && month <= time.June {
- endTime = fmt.Sprintf("%d-01", year)
- } else {
- endTime = fmt.Sprintf("%d-07", year)
- }
- case charge_utils.BillPeriodYear:
- // 一年账单
- // 返回本年一月
- endTime = fmt.Sprintf("%d-01", year)
- }
- return checkEndTime(endTime, chargeEffectiveTime)
- }
- // 计算上期账单截止时间
- func calcBillEnd(billPeriod, billPeriodType int32, chargeEffectiveTime int64) int64 {
- if billPeriodType == 1 {
- return calcBillEndByNaturePeriod(billPeriod, chargeEffectiveTime)
- } else {
- return calcBillEndByChargeEffectiveTime(billPeriod, chargeEffectiveTime)
- }
- }
- func calcFee(conf *dbmodel.TChargeConf, bind *dbmodel.TChargeBind, totalMonth int) (int64, string) {
- billDesc := pb_v1.BillDesc{ChargeBasis: conf.ChargeBasis}
- if conf.ChargeBasis == charge_utils.ChargeBasisArea || conf.ChargeBasis == charge_utils.ChargeBasisUsedArea || conf.ChargeBasis == charge_utils.ChargeBasisSpaceArea {
- // 1 按房屋面积 2 按使用面积 3 按车位面积 计算公式为面积*单价+附加费
- billDesc.ObjArea = bind.ObjArea
- billDesc.FixAmount = conf.FixAmount
- billDesc.FixAmountName = conf.FixAmountName
- billDesc.UnitPrice = conf.UnitPrice
- desc, _ := json.Marshal(billDesc)
- //desc := fmt.Sprintf("面积:%f\n单价:%s\n附加费:%s\n", bind.ObjArea, float64(conf.UnitPrice)/100.00, float64(conf.FixAmount)/100.00)
- return (int64(float64(conf.UnitPrice)*bind.ObjArea) + conf.FixAmount) * int64(totalMonth), string(desc)
- } else if conf.ChargeBasis == charge_utils.ChargeBasisFix {
- // 5 固定费用 ,返回费用配置中固定费用
- billDesc.FixAmount = conf.FixAmount
- desc, _ := json.Marshal(billDesc)
- //desc := fmt.Sprintf("固定费用:%f\n", float64(conf.FixAmount)/100.00)
- return conf.FixAmount * int64(totalMonth), string(desc)
- } else if conf.ChargeBasis == charge_utils.ChargeBasisSelf {
- // 6 自定义 返回绑定关系中自定义费用
- billDesc.CustomFee = bind.CustomFee
- desc, _ := json.Marshal(billDesc)
- //desc := fmt.Sprintf("自定义费用:%f\n", float64(bind.CustomFee)/100.00)
- return bind.CustomFee * int64(totalMonth), string(desc)
- }
- return 0, ""
- }
- // 生成账单
- func generateBill(chargeConf *dbmodel.TChargeConf, chargeBind *dbmodel.TChargeBind, endTime int64, needCheckStart bool) *dbmodel.TChargeBill {
- // 是否需要检查开始时间,手动生成不需要
- if needCheckStart {
- // 检查是否到计费开始时间
- if chargeBind.Start >= endTime {
- return nil
- }
- }
- // 检查最后生成账单时间是否比账单截止日期大
- if chargeBind.BillLastTime >= endTime {
- return nil
- }
- // 账单开始时间
- billStart := chargeBind.Start
- if chargeBind.BillLastTime != 0 {
- billStart = chargeBind.BillLastTime
- }
- totalMonth := calcMonthDiff(billStart, endTime)
- if totalMonth == 0 {
- return nil
- }
- // 计算费用
- fee, desc := calcFee(chargeConf, chargeBind, totalMonth)
- if fee == 0 {
- return nil
- }
- // TODO 宏定义
- bill := &dbmodel.TChargeBill{
- ObjType: chargeBind.ObjType,
- ObjId: chargeBind.ObjId,
- ObjName: chargeBind.ObjName,
- ChargeId: chargeBind.ChargeId,
- ChargeBindId: chargeBind.ID,
- ChargeTimeType: chargeConf.ChargeTimeType,
- ChargeDesc: desc,
- Amount: fee,
- ChargeType: chargeConf.ChargeType,
- ChargeName: chargeConf.ChargeName,
- PayMode: 1,
- ChargeStart: billStart,
- ChargeEnd: endTime,
- HouseId: chargeBind.HouseId,
- Status: 1,
- }
- return bill
- }
- // 更新费用项目账单最后时间
- func updateChargeLastTime(db *gorm.DB, chargeConf *dbmodel.TChargeConf, chargeBind *dbmodel.TChargeBind, endtime int64, dbname string) error {
- if chargeConf != nil {
- // 更新费用项目所有绑定
- where := map[string]interface{}{"id": chargeConf.ID, "bill_last_time <": endtime}
- value := map[string]interface{}{"bill_last_time": endtime}
- chargeConf.SetTable(dbname)
- err := chargeConf.Update(db, where, value)
- if err != nil {
- return errors.DataBaseError
- }
- chargeBind = dbmodel.NewChargeBind(dbname)
- where = map[string]interface{}{"charge_id": chargeConf.ID, "bill_last_time <": endtime}
- value = map[string]interface{}{"bill_last_time": endtime}
- err = chargeBind.Update(db, where, value)
- if err != nil {
- return errors.DataBaseError
- }
- } else if chargeBind != nil {
- // 更新绑定费项
- where := map[string]interface{}{"id": chargeBind.ID}
- value := map[string]interface{}{"bill_last_time": endtime}
- err := chargeBind.Update(db, where, value)
- if err != nil {
- return errors.DataBaseError
- }
- }
- return nil
- }
- func generateAllBill(req *pb_v1.GenerateBillRequest, db *gorm.DB) (err error) {
- dbname := utils.GetGardenDbName(req.GardenId)
- // 获取周期收费费项
- chargeConf := dbmodel.NewChargeConf(dbname)
- where := map[string]interface{}{
- "charge_time_type": charge_utils.ChargeTimeTypePeriod,
- }
- chargeConfList, err := chargeConf.List(db, where, nil, -1, 0)
- if err != nil {
- return errors.DataBaseError
- }
- p := dbmodel.NewChargeBill(dbname)
- for _, chargeConf := range chargeConfList {
- endTime := calcBillEnd(chargeConf.BillPeriod, chargeConf.BillPeriodType, chargeConf.ChargeEffectiveTime)
- if endTime == 0 {
- continue
- }
- // 查询费用绑定结束时间(bill_last_time)不为endtime的
- changrBind := dbmodel.NewChargeBind(dbname)
- where := map[string]interface{}{"bill_last_time <": endTime, "charge_id": chargeConf.ID}
- chargeBindList, err := changrBind.List(db, where, nil, -1, 0)
- if err != nil {
- return errors.DataBaseError
- }
- billList := []dbmodel.TChargeBill{}
- // 遍历账单绑定,生成账单
- billLen := 0
- hasBill := false
- for _, v := range chargeBindList {
- bill := generateBill(&chargeConf, &v, endTime, true)
- if bill == nil {
- continue
- }
- billList = append(billList, *bill)
- billLen++
- hasBill = true
- if billLen == 1000 {
- // 1000 条插入一次
- // 批量插入账单
- err = p.InsertMulti(db, &billList)
- if err != nil {
- return errors.DataBaseError
- }
- billLen = 0
- billList = []dbmodel.TChargeBill{}
- }
- }
- if !hasBill {
- // 没有账单
- continue
- }
- if billLen > 0 {
- // 插入剩余
- err = p.InsertMulti(db, &billList)
- if err != nil {
- return errors.DataBaseError
- }
- }
- // 更新绑定关系总中账单截止时间
- err = updateChargeLastTime(db, &chargeConf, nil, endTime, dbname)
- if err != nil {
- return errors.DataBaseError
- }
- }
- return nil
- }
- // 生成指定费用项目账单
- func generateBillByChargeId(req *pb_v1.GenerateBillRequest, db *gorm.DB) (err error) {
- dbname := utils.GetGardenDbName(req.GardenId)
- // 获取周期收费费项
- chargeConf := dbmodel.NewChargeConf(dbname)
- where := map[string]interface{}{
- "id": req.ChargeId,
- }
- err = chargeConf.Find(db, where)
- if err != nil {
- return errors.DataBaseError
- }
- if req.EndTime <= chargeConf.BillLastTime {
- return status.Error(10003, "结束时间不能小于上次账单结束时间")
- }
- p := dbmodel.NewChargeBill(dbname)
- endTime := req.EndTime
- // 查询费用绑定结束时间(bill_last_time)不为endtime的
- changrBind := dbmodel.NewChargeBind(dbname)
- where = map[string]interface{}{"bill_last_time <": endTime}
- chargeBindList, err := changrBind.List(db, where, nil, -1, 0)
- if err != nil {
- return errors.DataBaseError
- }
- billList := []dbmodel.TChargeBill{}
- // 遍历账单绑定,生成账单
- billLen := 0
- hasBill := false
- for _, v := range chargeBindList {
- bill := generateBill(chargeConf, &v, endTime, false)
- if bill == nil {
- continue
- }
- billList = append(billList, *bill)
- billLen++
- hasBill = true
- if billLen == 1000 {
- // 1000 条插入一次
- // 批量插入账单
- err = p.InsertMulti(db, &billList)
- if err != nil {
- return errors.DataBaseError
- }
- billLen = 0
- billList = []dbmodel.TChargeBill{}
- }
- }
- if !hasBill {
- // 没有账单
- return nil
- }
- if billLen > 0 {
- // 插入剩余
- err = p.InsertMulti(db, &billList)
- if err != nil {
- return errors.DataBaseError
- }
- }
- // 更新绑定关系总中账单截止时间
- err = updateChargeLastTime(db, chargeConf, nil, endTime, dbname)
- if err != nil {
- return errors.DataBaseError
- }
- return nil
- }
- // 生成一条费用项目绑定账单
- func generateBillByChargeBindId(req *pb_v1.GenerateBillRequest, db *gorm.DB) (err error) {
- dbname := utils.GetGardenDbName(req.GardenId)
- // 获取绑定费用项目
- changrBind := dbmodel.NewChargeBind(dbname)
- where := map[string]interface{}{"id": req.ChargeBindId}
- err = changrBind.Find(db, where)
- if err != nil {
- return errors.DataBaseError
- }
- if req.EndTime <= changrBind.BillLastTime {
- return status.Error(10003, "结束时间不能小于上次账单结束时间")
- }
- // 获取周期收费费项
- chargeConf := dbmodel.NewChargeConf(dbname)
- where = map[string]interface{}{
- "id": changrBind.ChargeId,
- }
- err = chargeConf.Find(db, where)
- if err != nil {
- return errors.DataBaseError
- }
- // 生成账单
- bill := generateBill(chargeConf, changrBind, req.EndTime, false)
- if bill == nil {
- return nil
- }
- bill.SetTable(dbname)
- // 插入账单
- err = bill.Insert(db)
- if err != nil {
- return errors.DataBaseError
- }
- // 更新绑定关系总中账单截止时间
- err = updateChargeLastTime(db, nil, changrBind, req.EndTime, dbname)
- if err != nil {
- return errors.DataBaseError
- }
- return nil
- }
- func runBatchBill(req *pb_v1.GenerateBillRequest) error {
- dbname := utils.GetGardenDbName(req.GardenId)
- changrBind := dbmodel.NewChargeBind(dbname)
- changrBindList := []dbmodel.TChargeBind{}
- for i := 0; i < 10000; i++ {
- changrBindTemp := dbmodel.TChargeBind{
- ObjId: int64(i + 10),
- ObjName: "1-1-906",
- HouseId: 1,
- ChargeId: 7,
- Start: 1619798400,
- End: 0,
- ChargeType: 1,
- ObjType: 1,
- CustomFee: 10,
- BillLastTime: 0,
- ObjArea: 20.20,
- }
- changrBindTemp.UniqFlag.Int32 = 1
- changrBindTemp.UniqFlag.Valid = true
- changrBindList = append(changrBindList, changrBindTemp)
- if i%2000 == 0 {
- changrBind.InsertMulti(database.DB(), &changrBindList)
- changrBindList = []dbmodel.TChargeBind{}
- }
- }
- return nil
- }
- // 生成全部账单
- func GenerateBill(ctx context.Context, req *pb_v1.GenerateBillRequest) (reply *pb_v1.GenerateBillReply, err error) {
- reply = &pb_v1.GenerateBillReply{}
- if req.GardenId <= 0 {
- return reply, errors.ParamsError
- }
- if req.ChargeId > 0 || req.ChargeBindId > 0 {
- if req.EndTime <= 0 {
- return reply, errors.ParamsError
- } else {
- // 结束时间往后推一个月,包含入参的月份
- //req.EndTime = time.Unix(req.EndTime, 0).AddDate(0, 1, 0).Unix()
- }
- }
- db := database.DB().Begin()
- // 捕获各个task中的异常并返回给调用者
- defer func() {
- if r := recover(); r != nil {
- db.Rollback()
- err = fmt.Errorf("%+v", r)
- e := &status.Status{}
- if er := json.Unmarshal([]byte(err.Error()), e); er != nil {
- logger.Error("err",
- zap.String("system_err", err.Error()),
- zap.Stack("stacktrace"))
- }
- }
- }()
- if req.ChargeId > 0 {
- err = generateBillByChargeId(req, db)
- } else if req.ChargeBindId > 0 {
- err = generateBillByChargeBindId(req, db)
- } else {
- err = generateAllBill(req, db)
- }
- if err != nil {
- db.Rollback()
- } else {
- db.Commit()
- }
- return reply, err
- }
|