package order import ( "app.yhyue.com/moapp/jybase/common" "app.yhyue.com/moapp/jybase/date" "context" "encoding/json" "errors" "fmt" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/util/gconv" "jyOrderManager/internal/consts" "jyOrderManager/internal/jyutil" "jyOrderManager/internal/logic/product" "jyOrderManager/internal/model" "jyOrderManager/internal/service" "log" "math" "strings" "time" ) // 订单审核状态 const ( OrderUncommitted = 0 // 待提交 OrderPending = 1 // 待审核 OrderFirstPassed = 2 // 一审通过 OrderPassed = 3 // 审核通过 OrderSecondPassed = 4 // 审核通过 OrderFirstReturn = -2 // 一审退回 OrderSecondReturn = -3 // 二审退回 OrderThreeReturn = -4 // 二审退回 ReturnMoney = 1 ReturnProtocol = 2 ) // 表名 const ( TableAuditRecords = "audit_records" // 审核记录表 TableDataExportOrder = "dataexport_order" // 订单表 ) type ExamineDiscountStr struct { ExamineDiscount map[string][]Discount `json:"examineDiscount"` } type Discount struct { Min int `json:"min"` Max int `json:"max"` DiscountRate float64 `json:"discountRate"` } var ( GiftDiscount float64 ExamineDiscountConfig map[string][]Discount ) func init() { err := g.Cfg().MustGet(context.Background(), "examineDiscount").Scan(&ExamineDiscountConfig) if err != nil { log.Println("examineDiscount", err) } GiftDiscount = g.Cfg().MustGet(context.Background(), "giftDiscount", 0.6).Float64() } // OrdersExamine 订单审核 func OrdersExamine(ctx context.Context, param model.OrdersExamine) error { //查询订单 orderData, err := g.DB().Ctx(ctx).GetOne(ctx, fmt.Sprintf(`SELECT * FROM dataexport_order WHERE order_code ='%s'`, param.OrderCode)) if err != nil || orderData.IsEmpty() { return gerror.Wrap(err, "查询订单表异常") } oldAuditStatus := gconv.Int(orderData.Map()["audit_status"]) if oldAuditStatus == 3 { return errors.New("订单审核已完成,无需审批") } newAuditStatus := 1 operatorType := 2 //通过 switch param.State == 1 { case true: switch oldAuditStatus { case 1: newAuditStatus = 2 case 2: newAuditStatus = 4 operatorType = 3 case 4: newAuditStatus = 3 operatorType = 5 //三审 } case false: switch oldAuditStatus { case 1: newAuditStatus = -2 case 2: newAuditStatus = -3 operatorType = 3 case 4: newAuditStatus = -4 operatorType = 5 } } var ( contractArchiveTime string auditStatus int ) //数据库操作 return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { auditStatus = newAuditStatus var checkAuto bool productDetail, err := g.DB().Ctx(ctx).Query(ctx, fmt.Sprintf(`SELECT * FROM jy_order_detail WHERE order_code ='%s' and status =1`, param.OrderCode)) if err != nil || productDetail.IsEmpty() { return err } if oldAuditStatus == 1 && param.State == 1 { //待一审 审核通过校验是否直接通过 if CheckAutoAudit(ctx, orderData.Map(), productDetail.List()) { newAuditStatus = 3 checkAuto = true } } upData := map[string]interface{}{ "audit_status": newAuditStatus, } if newAuditStatus == 3 { //“协议归档状态”如若为“已归档”,则“订单状态”更新为“已完成”,如若为“未归档”,则订单状态仍为“未完成” contractData, err := g.DB().Ctx(ctx).GetOne(ctx, fmt.Sprintf(`SELECT contract_archive_status,contract_archive_time FROM contract where order_code = '%s'`, param.OrderCode)) if err == nil && !contractData.IsEmpty() { if common.IntAll(contractData.Map()["contract_archive_status"]) == 1 { upData["order_status"] = 1 contractArchiveTime = common.ObjToString(contractData.Map()["contract_archive_time"]) } } //0元订单审核通过后直接开通权益 if common.Float64All((orderData.Map())["pay_money"]) == 0 && consts.PhoneRegex.MatchString(common.ObjToString((orderData.Map())["user_phone"])) { upData["order_status"] = 1 uData, entId, userPositionId, err := jyutil.GetCreateUserData(gconv.String(orderData.Map()["user_phone"]), gconv.String(orderData.Map()["company_name"]), gconv.Int(orderData.Map()["buy_subject"]) == 2) if err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { // 产品服务开通 for _, m := range productDetail.List() { if !jyutil.IsServiceOpen(m) { continue } //参数注入 //参数注入 m["userMap"] = map[string]interface{}{ "userData": uData, "entId": entId, "userPositionId": userPositionId, } m["phone"] = orderData["user_phone"] m["order_code"] = param.OrderCode m["reqCompanyName"] = orderData.Map()["company_name"] m["amount"] = m["final_price"] m["reqSubject"] = orderData.Map()["buy_subject"] m["linked_orderId"] = m["linked_detail_id"] productCode := gconv.String(m["product_code"]) pFunc, err := product.JyProFunc.GetProductInitFuncByCode(productCode) if err != nil { return err } pObj, err := pFunc(m) if err != nil { return gerror.Wrap(err, fmt.Sprintf("获取%s商品异常", productCode)) } if err := pObj.OpenService(ctx, time.Now()); err != nil { return err } } if orderUserId := gconv.String(orderData.Map()["user_id"]); orderUserId == "" || orderUserId != gconv.String(uData["userId"]) { log.Printf("同步更新订单用户身份:orderUserId:%s,userId:%s,entId:%d\n", orderUserId, uData["userId"], entId) upData := g.Map{ "user_id": uData["userId"], } if entId > 0 { //企业服务 upData["ent_id"] = entId if personPhone := gconv.String(orderData.Map()["personPhone"]); personPhone != "" { jyutil.EndAddUser(ctx, entId, gconv.String(orderData.Map()["user_phone"]), personPhone, gconv.String(orderData.Map()["personName"])) } } //更新订单 _, err = g.DB().Update(ctx, consts.OrderListTableName, upData, "order_code=?", param.OrderCode) if err != nil { return err } } return nil }); err != nil { log.Println(err) return err } } } //更新订单 _, err = g.DB().Ctx(ctx).Update(ctx, "dataexport_order", upData, map[string]interface{}{"order_code": param.OrderCode}) if err != nil { return err } //插入审核记录表 tn := time.Now() insertData := map[string]interface{}{ "operator": jyutil.GetUserMsgFromCtx(ctx).EntUserName, //审核操作人 "create_time": tn.Format(date.Date_Full_Layout), "operator_type": operatorType, "audit_status": auditStatus, "back_reason": param.Reason, "order_code": param.OrderCode, "audit_type": 2, } _, err = g.DB().Ctx(ctx).Insert(ctx, "audit_records", insertData) if err != nil { return err } if checkAuto { // 给定的数据 columns := strings.Split("operator,create_time,operator_type,audit_status,back_reason,order_code,audit_type", ",") values := []interface{}{"系统自动", time.Now().Add(time.Second).Format("2006-01-02 15:04:05"), 3, OrderSecondPassed, "系统自动审核通过", param.OrderCode, 2, "系统自动", time.Now().Add(2 * time.Second).Format("2006-01-02 15:04:05"), 5, OrderPassed, "系统自动审核通过", param.OrderCode, 2} // 构建SQL插入语句 query := fmt.Sprintf("INSERT INTO audit_records (%s) VALUES (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?)", strings.Join(columns, ",")) g.DB().Ctx(ctx).Exec(ctx, query, values...) } if contractArchiveTime != "" { _ = CommonChange(ctx, param.OrderCode, contractArchiveTime, ReturnProtocol) } return nil }) } // CheckAutoAudit 校验自动审核 func CheckAutoAudit(ctx context.Context, orderData map[string]interface{}, productList []map[string]interface{}) bool { if common.Float64All(orderData["order_money"]) == 0 || common.Float64All(orderData["pay_money"]) == 0 { return false } var contractPass bool //是否支持线上自动生成电子协议 /* 判断是否符合合同 (1)订单审核状态是“已通过”; (2)签约主体为“北京剑鱼信息技术有限公司”,注:如签约主体为拓普则线下生成合同; (3)协议状态为“签协议”(“已签协议”文案修改为“签协议”,涉及:创建订单、订单审核、订单详情) (4)产品类型是“超级订阅”(且付费类型为“购买”、“续费”),或产品类型是“大会员”且会员套餐为“商机版2.0”、“专家版2.0”(且服务类型为“新购服务”、“延长服务”),注:超级订阅/大会员升级和其他产品类型,线下生成合同。 */ if orderData["signing_subject"] != "h01" { log.Println("自动审核不通过signing_subject:", orderData["signing_subject"]) return false } isEnt := gconv.Int(orderData["buy_subject"]) == 2 var ( insertData []map[string]interface{} activityProduct = make(map[string][]map[string]interface{}) isXwlp bool beforePrice, afterPrice float64 giftDiscountRate []float64 ) for _, m := range productList { productItem, err := service.Product().GetProduct(gconv.String(m["product_code"])) if err != nil { return false } productClass, err := service.Product().GetProductClass(productItem.ProductClassId) if err != nil { return false } //外采赠品 // 需“判断订单中常规商品/活动产品的折扣率是否都符合自动审核规则“的同时, //除外采赠品外的其余产品的整体折扣率不得低于XX%,才能自动审核,否则需要人工审核。 switch productClass.TopClass == "外采赠品" { case true: if productItem.ApprovalRules == nil || productItem.ApprovalRules["rate"] == nil || gconv.Int(m["final_price"]) > gconv.Int(productItem.ApprovalRules["contract_amount_max"]) || gconv.Int(m["final_price"]) < gconv.Int(productItem.ApprovalRules["contract_amount_mix"]) { return false } isXwlp = true giftDiscountRate = append(giftDiscountRate, gconv.Float64(productItem.ApprovalRules["rate"])) continue case false: beforePrice += gconv.Float64(m["original_price"]) afterPrice += gconv.Float64(m["final_price"]) } if common.Float64All(m["original_price"]) < 0 { return false } if activityCode := gconv.String(m["activity_code"]); activityCode != "" { //活动产品统一校验 activityProduct[activityCode] = append(activityProduct[activityCode], m) continue } filterMap := make(map[string]interface{}) filterData := common.ObjToString(m["filter"]) if err := json.Unmarshal([]byte(filterData), &filterMap); err != nil { log.Println("filter unmarshal err:", orderData["order_code"], err.Error()) return false } var ( cycleType, cycleCount, monthCount int examineDiscount float64 ) conditionalMap := make(map[string]interface{}) switch productType := common.InterfaceToStr(m["product_type"]); productType { case "VIP订阅": switch common.IntAll(m["service_type"]) { case 3: //升级 monthCount = GetUserTime(gconv.String(orderData["user_phone"]), gconv.String(m["linked_detail_id"]), true, isEnt) default: cycleType = common.If(common.IntAll(filterMap["buy_type"]) > 0, common.IntAll(filterMap["buy_type"]), common.IntAll(filterMap["give_type"])).(int) //1天 2月 3年 4季度 cycleCount = common.IntAll(filterMap["buy_cycle"]) + common.IntAll(filterMap["give_cycle"]) switch cycleType { //(1天 2月 3年 4季度) case 3: monthCount = cycleCount * 12 case 2: monthCount = cycleCount case 1: if cycleCount%30 > 0 { monthCount = cycleCount/30 + 1 } else { monthCount = cycleCount / 30 } case 4: monthCount = cycleCount * 3 } } //折扣率满足一定条件 for _, discount := range ExamineDiscountConfig[productType] { if monthCount >= discount.Min && monthCount < discount.Max { examineDiscount = discount.DiscountRate conditionalMap["min"] = discount.Min conditionalMap["max"] = discount.Max conditionalMap["product_type"] = productType conditionalMap["discountRate"] = discount.DiscountRate break } } case "大会员": if common.IntAll(filterMap["comboId"]) <= 0 { return false } //大会员 createType:1新建 2补充 3延长 switch common.IntAll(m["service_type"]) { case 3: //升级 monthCount = GetUserTime(gconv.String(orderData["user_phone"]), gconv.String(orderData["linked_detail_id"]), true, isEnt) default: cycleType = common.If(common.IntAll(filterMap["buy_type"]) > 0, common.IntAll(filterMap["buy_type"]), common.IntAll(filterMap["give_type"])).(int) //1天 2月 3年 4季度 cycleCount = common.IntAll(filterMap["buy_cycle"]) + common.IntAll(filterMap["give_cycle"]) switch cycleType { //(1天 2月 3年 4季度) case 3: monthCount = cycleCount * 12 case 2: monthCount = cycleCount case 1: if cycleCount%30 > 0 { monthCount = cycleCount/30 + 1 } else { monthCount = cycleCount / 30 } case 4: monthCount = cycleCount * 3 } } //折扣率满足一定条件 for _, discount := range ExamineDiscountConfig[productType] { switch discount.Max == -1 { case true: if monthCount >= discount.Min { examineDiscount = discount.DiscountRate conditionalMap["min"] = discount.Min conditionalMap["max"] = discount.Max conditionalMap["product_type"] = productType conditionalMap["discountRate"] = discount.DiscountRate break } case false: if monthCount >= discount.Min && monthCount < discount.Max { examineDiscount = discount.DiscountRate conditionalMap["min"] = discount.Min conditionalMap["max"] = discount.Max conditionalMap["product_type"] = productType conditionalMap["discountRate"] = discount.DiscountRate break } } } default: examineDiscount = productClass.Rate } if examineDiscount <= 0 { log.Println("自动审核商品折扣率获取失败") return false } log.Println("自动审核商品折扣:", m["id"], cycleType, monthCount, roundToTwoDecimalPlaces(common.Float64All(m["final_price"])/common.Float64All(m["original_price"])), examineDiscount) contractPass = roundToTwoDecimalPlaces(common.Float64All(m["final_price"])/common.Float64All(m["original_price"])) >= examineDiscount if !contractPass { return false } conditionalStr, _ := json.Marshal(conditionalMap) insertData = append(insertData, map[string]interface{}{ "create_time": time.Now().Format("2006-01-02 15:04:05"), "pay_money": gconv.Float64(m["final_price"]), "original_price": gconv.Float64(m["original_price"]), "discount_rate": common.Float64All(m["final_price"]) / common.Float64All(m["original_price"]), "order_code": gconv.Float64(m["order_code"]), "product_type": common.InterfaceToStr(m["product_type"]), "monthCount": monthCount, "conditional_remarks": string(conditionalStr), }) } if len(activityProduct) > 0 { //校验活动产品 for activityCode, activityData := range activityProduct { activity, err := g.DB().Ctx(ctx).GetOne(ctx, fmt.Sprintf(`SELECT rate FROM jy_product_activity WHERE code ='%s'`, activityCode)) if err != nil || activity.IsEmpty() { return false } var ( activityOriginalPrice, activityClosingPrice float64 activityInsertData []map[string]interface{} ) for _, datum := range activityData { //同种活动 统一计算折扣率 activityOriginalPrice += gconv.Float64(datum["original_price"]) activityClosingPrice += gconv.Float64(datum["final_price"]) activityInsertData = append(activityInsertData, map[string]interface{}{ "create_time": time.Now().Format("2006-01-02 15:04:05"), "pay_money": gconv.Float64(datum["final_price"]), "original_price": gconv.Float64(datum["original_price"]), "discount_rate": common.Float64All(datum["final_price"]) / common.Float64All(datum["original_price"]), "order_code": gconv.Float64(datum["order_code"]), "product_type": common.InterfaceToStr(datum["product_type"]), "monthCount": -1, "conditional_remarks": fmt.Sprintf("活动产品:活动id:%s 活动折扣率:%f", activityCode, gconv.Float64(activity.Map()["rate"])), }) } contractPass = roundToTwoDecimalPlaces(activityClosingPrice/activityOriginalPrice) >= gconv.Float64(activity.Map()["rate"]) if !contractPass { return false } //记录活动自动审核通过记录 insertData = append(insertData, activityInsertData...) } } if isXwlp { //存在赠送产品 每个赠送产品单独计算折扣率 if afterPrice == 0 { return false } for _, f := range giftDiscountRate { if beforePrice/afterPrice < f { return false } } } if len(insertData) > 0 { g.DB().Save(ctx, "automatic_audit_log", insertData) } return true } func roundToTwoDecimalPlaces(x float64) float64 { return math.Round(x*10000) / 10000 } func GetUserTime(phone, orderId string, isVip, isEnt bool) (monthCount int) { switch isEnt { case false: //查询用户大会员权益剩余服务周期 userData, ok := jyutil.MG.DB().FindOne("user", map[string]interface{}{ "$or": []map[string]interface{}{ {"s_phone": phone}, {"s_m_phone": phone}, }, }) if !ok || userData == nil || len(*userData) == 0 { return monthCount } var startTime, endTime time.Time if isVip { startTime = time.Unix(common.Int64All((*userData)["l_vip_starttime"]), 0).Local() endTime = time.Unix(common.Int64All((*userData)["l_vip_endtime"]), 0).Local() } else { startTime = time.Unix(common.Int64All((*userData)["i_member_starttime"]), 0).Local() endTime = time.Unix(common.Int64All((*userData)["i_member_endtime"]), 0).Local() } if startTime.Unix() < time.Now().Unix() { startTime = time.Now() } return GetMonthNum(startTime, endTime) case true: waitEmpower, err := g.DB().Ctx(context.Background()).GetOne(context.Background(), fmt.Sprintf(` SELECT e.start_time,e.end_time FROM entniche_order e INNER JOIN entniche_wait_empower w on e.wait_empower_id = w.id WHERE e.order_detail_id = %s`, orderId)) if err == nil || waitEmpower.IsEmpty() { return monthCount } var startTime, endTime time.Time startTime = jyutil.StrToTime(gconv.String(waitEmpower.Map()["start_time"]), "2006-01-02 15:04:05") endTime = time.Unix(common.Int64All(waitEmpower.Map()["end_time"]), 0).Local() if startTime.Unix() < time.Now().Unix() { startTime = time.Now() } return GetMonthNum(startTime, endTime) } return monthCount } func GetMonthNum(startTime, endTime time.Time) int { // 定义开始时间和结束时间 //start := time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC) //end := time.Date(2024, time.March, 30, 0, 0, 0, 0, time.UTC) // 获取开始时间和结束时间的年份和月份 startYear, startMonth, startDay := startTime.Date() endYear, endMonth, endDay := endTime.Date() // 计算月份差异 months := (endYear-startYear)*12 + int(endMonth-startMonth) if endDay-startDay > 0 { months += 1 } //fmt.Printf("月份差异为:%d\n", months) return months }