package service import ( "database/sql" "encoding/json" "errors" "fmt" mercator "git.xiaoniaokuaiyan.com/qiaozhi/xn-mercator" "github.com/smartwalle/alipay/v3" "gopkg.in/guregu/null.v3" "log" "math/rand" "strconv" "strings" "sync" "time" "xiaoniaokuaiyan.com/xiaoniao/pay/ali" "xiaoniaokuaiyan.com/xiaoniao/config" "xiaoniaokuaiyan.com/xiaoniao/constants" "xiaoniaokuaiyan.com/xiaoniao/dal" "xiaoniaokuaiyan.com/xiaoniao/entity" "xiaoniaokuaiyan.com/xiaoniao/pay/wx" "xiaoniaokuaiyan.com/xiaoniao/util" ) type OrderService struct { dal.IOrder *WeixinService } /* type tubeInfo struct { Yellow uint `json:"yellow"` Green uint `json:"green"` Purple uint `json:"purple"` } */ type OrderHookSymbol int const ( BEFORE_ORDER_CREATE OrderHookSymbol = iota + 1 AFTER_ORDER_CANCEL ) const BUYER_OPENID_KEY string = "buyer_openid_key" const BUYER_TPL string = "支付成功,我们会尽快为被邀请人发送酒精基因检测包" const OPENID_TPL string = "您的朋友已经为您支付成功,我们会尽快为您邮寄酒精基因检测包" type OrderHookHandle func(oitem *entity.Order) error var orderHooks = map[OrderHookSymbol][]OrderHookHandle{} func RegistOrderHook(typ OrderHookSymbol, handle OrderHookHandle) { if _, ok := orderHooks[typ]; !ok { orderHooks[typ] = []OrderHookHandle{} } orderHooks[typ] = append(orderHooks[typ], handle) } func (srv *OrderService) AddOrder(orderItem *entity.Order, city_id int, city string) (interface{}, error) { if orderItem == nil || len(orderItem.ProductIds) == 0 { return nil, errors.New("1::wrong request") } var btime time.Time btime, err := time.Parse("2006-01", orderItem.Birthday) if err != nil { btime, err = time.Parse("2006-01-02", orderItem.Birthday) if err != nil { return nil, errors.New("9::wrong birthday") } } if orderItem.Age <= 0 { orderItem.Age = time.Now().Year() - btime.Year() } db := util.GetWriteSqlDB() var ( productIds = []int{} productQuantityMap = map[int]int{} ) for _, v := range orderItem.ProductIds { productIds = append(productIds, v.ProductId) //20210119 添加 产品1149 两人核酸,下单后拆单 //splitProductId, err := config.IniConf.Section("server").Key("split_product").Int() //if err == nil && v.ProductId == splitProductId { // v.Quantity *= 2 //} productQuantityMap[v.ProductId] = v.Quantity } /* var plist = []*entity.ProductDB{} //db.Select(&plist, "select * from t_product where id in ("+util.IntJoin(productIds, ",")+") order by is_no_booktime desc") s_source_zfb := "" if orderItem.IsZFB { s_source_zfb = " and t2.source='sp_zfb' " } sqlTemp := "select t1.*, sum(t4.quantity) as bought_num from (select * from t_product where id in(" + util.IntJoin(productIds, ",") + ")) t1 left join (select t3.quantity, t3.product_id from t_order t2 left join t_order_product t3 on t2.id = t3.order_id where custom_mobile = ? and t2.status not in (7,9,14) and t2.retype in ('100','110') " + s_source_zfb + " ) t4 on t1.id = t4.product_id GROUP BY t1.id order by t1.is_no_booktime desc" db.Select(&plist, sqlTemp, orderItem.CustomMobile) if len(plist) != len(productIds) { return nil, errors.New("6::wrong param productids") } // 20230629 增加支付宝特殊处理 if orderItem.IsZFB { entity.ZFBWash(plist) } //检查库存以及个人限量 for _, pitem := range plist { if pitem.StockSwitch == "ON" && pitem.Stock < productQuantityMap[pitem.Id] { return nil, errors.New("10::out of stock") } if pitem.PstockSwitch == "ON" && int(pitem.BoughtNum.Int64)+productQuantityMap[pitem.Id] > pitem.Pstock { return nil, errors.New(fmt.Sprintf("11::upper limit of product %d", pitem.Id)) } //20210922 处理坐标 if pitem.SaleMode == 100 && strings.Contains(orderItem.Coordinates, ",") { str := "select maps from v_product_city where city_id = ? and product_id = ?" var maps string err = db.Get(&maps, str, city_id, pitem.Id) is := mercator.IsPointInRingByStr(orderItem.Coordinates, maps) if !is { return nil, fmt.Errorf("12::订单坐标超过范围 %s", orderItem.Coordinates) } } }*/ plist, err := Stocklimit(orderItem.CustomMobile, productIds, productQuantityMap, orderItem.IsZFB) if err != nil { return nil, err } for _, pitem := range plist { //20210922 处理坐标 if pitem.SaleMode == 100 && strings.Contains(orderItem.Coordinates, ",") { str := "select maps from v_product_city where city_id = ? and product_id = ?" var maps string err = db.Get(&maps, str, city_id, pitem.Id) is := mercator.IsPointInRingByStr(orderItem.Coordinates, maps) if !is { return nil, fmt.Errorf("12::订单坐标超过范围 %s", orderItem.Coordinates) } } } //如果产品中含有不需要预约时间的,则省略产能检查 //检查产能是否足够 strSql := "select t1.gnum, t2.remain_num, t2.pdate, t1.id as gid, t2.id as infoid, t1.shortname from (select tpc.id, num as gnum, shortname from t_producer_config tpc,t_city where city_id = t_city.id and city_id = ? and tpc.id = ?) t1 left join t_producer_info t2 on t1.id = t2.global_id and t2.pdate = ? and t2.time_range = ?" var producerInfo = struct { GNum int `db:"gnum"` RemainNum sql.NullInt64 `db:"remain_num"` PDate sql.NullString `db:"pdate"` GId int `db:"gid"` InfoId sql.NullInt64 `db:"infoid"` Shortname string `db:"shortname"` }{} var pinfoSqlResult sql.Result err = db.Get(&producerInfo, strSql, city_id, orderItem.PTId, orderItem.VisitDate, orderItem.VisitTimeRange) if err != nil { return nil, errors.New("4::该城市暂未开通服务") } tx := db.MustBegin() if len(plist) > 0 && plist[0].IsNoBooktime != 1 { visitDate, err := time.Parse("2006-01-02", orderItem.VisitDate) if err != nil { return nil, errors.New("2::wrong visit date") } //当天下单最早只能约第二天上门,240220 houyf改为20点 if visitDate.Before(time.Now()) || (time.Now().Hour() >= 20 && time.Now().Truncate(time.Hour*24).Add(time.Hour*48).After(visitDate)) { return nil, errors.New("3::invalid visit date") } if producerInfo.PDate.String == orderItem.VisitDate && producerInfo.RemainNum.Int64 <= 0 { return nil, errors.New("5::producer not enough") } if !producerInfo.PDate.Valid { pinfoSqlResult = tx.MustExec("insert into t_producer_info(global_id, pdate, time_range, num, remain_num) values(?, ?, ?, ?, ?)", producerInfo.GId, visitDate, orderItem.VisitTimeRange, producerInfo.GNum, producerInfo.GNum-1) lid, err := pinfoSqlResult.LastInsertId() if err != nil { tx.Tx.Rollback() return nil, err } orderItem.MaterialId = int(lid) } else { pinfoSqlResult = tx.MustExec("update t_producer_info set remain_num = ? where id = ? and remain_num = ?", producerInfo.RemainNum.Int64-1, producerInfo.InfoId, producerInfo.RemainNum.Int64) orderItem.MaterialId = int(producerInfo.InfoId.Int64) } if ra, _ := pinfoSqlResult.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return nil, errors.New("7::failed to update producer info") } } //orderItem.VisitDate = visitDate if city == "" { if producerInfo.Shortname != "" { city = producerInfo.Shortname } else { city = "QG" } } orderId := GenerateOrderNo(city) orderItem.Id = orderId //region 20221012 护士加项 使用折扣金额 result := map[string]interface{}{} isNurse := false json.Unmarshal([]byte(orderItem.ShareSource), &result) if nurse, ok := result["source"].(string); ok && nurse == "nurse" { if deliver_user_id, ok := result["id"].(float64); ok { orderItem.Payment, orderItem.RepeatItemFee, err = ComputeProductPriceByDeliverID(productIds, productQuantityMap, deliver_user_id) isNurse = true } } if !isNurse { //20221012如果没经过护士折扣,就正常计算价格 orderItem.Payment, orderItem.RepeatItemFee, err = ComputeProductPrice(productIds, productQuantityMap, false) } // 20230629 增加支付宝特殊处理 if orderItem.IsZFB { orderItem.Payment, orderItem.RepeatItemFee, err = ComputeProductPrice_ZFB(productIds, productQuantityMap) } //endregion //orderItem.Payment, orderItem.RepeatItemFee, err = ComputeProductPrice(productIds, productQuantityMap, false) //20211127 联仁根据门店id 修改定价 if shopId, ok := util.StringToInt(orderItem.JdOrderId); ok && orderItem.Source == "lianren" { lianRenPrice(plist, shopId) orderItem.Payment = plist[0].Price } //取消减免重复项目费用 orderItem.RepeatItemFee = 0 if err != nil { tx.Tx.Rollback() return nil, err } if len(orderItem.ItemIds) > 0 { iprice, _, err := ComputeProductPrice(orderItem.ItemIds, productQuantityMap, true) if err != nil { tx.Tx.Rollback() return nil, err } orderItem.Payment += iprice tempResult := tx.MustExec("insert into t_product_detect_item(detect_item_ids) values(?)", util.IntJoin(orderItem.ItemIds, ",")) if tpid, err := tempResult.LastInsertId(); err != nil { tx.Tx.Rollback() return nil, err } else { tx.MustExec("insert into t_order_product(order_id, product_id, is_personal) values(?,?,?)", orderId, tpid, 1) } } WORK_FEE_REQUIRED, _ := config.IniConf.Section("server").Key("work_fee_required_bound").Int() WORK_FEE, _ := config.IniConf.Section("server").Key("work_fee").Int() //判断是否需要上门费 orderItem.OnlinePayment = orderItem.Payment for _, pitem := range plist { if pitem.SaleMode == 200 || pitem.IsWorkFee == 2 { orderItem.OnlinePayment -= pitem.Price } } if orderItem.Source != "web-jiaxiang" && (orderItem.OnlinePayment > 0 && orderItem.Payment < WORK_FEE_REQUIRED) { orderItem.WorkFee = WORK_FEE } //todo 20210331 判断是否活动来的订单,如果是某些特殊的产品id,按照数量扣减99块钱(2,132,568,645,958) //存到RepeatItemFee 里面,这样,在支付的时候,和前端都可以扣减相应的钱 if len(orderItem.CustomManagerId) > 0 { cmId := &struct { Source string `json:"s"` IsCheck string `json:"t"` }{} err := json.Unmarshal([]byte(orderItem.CustomManagerId), &cmId) quantity := 0 if err == nil && cmId.IsCheck == "200" { if count, ok := productQuantityMap[2]; ok { quantity += count } if count, ok := productQuantityMap[132]; ok { quantity += count } if count, ok := productQuantityMap[568]; ok { quantity += count } if count, ok := productQuantityMap[645]; ok { quantity += count } if count, ok := productQuantityMap[958]; ok { quantity += count } orderItem.RepeatItemFee += 99 * quantity * 100 } } if orderItem.OnlinePayment <= 0 { orderItem.OnlinePayment = -1 } orderItem.CreatedAt = time.Now().Format("2006-01-02 15:04:05") //判断并标记是否来自销售系统订单 if srv.IsAgentOrder(orderItem.OpenId) { orderItem.IsAgent = 1 } relationship := orderItem.Relationship orderItem.Relationship = "" strSql, mkv := util.GenerateInsertSqlFromStruct("t_order", orderItem) osqlResult, err := tx.NamedExec(strSql, mkv) if err != nil { tx.Tx.Rollback() return nil, err } if ra, _ := osqlResult.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return nil, errors.New("8::failed to upadte order info") } var ( tube = map[string]interface{}{} maxTube = map[string]interface{}{} ) sqlStock := "update t_product set stock = stock -? where id = ?;" if orderItem.IsZFB { sqlStock = "update t_product set zfb_stock = zfb_stock -? where id = ?;" } for _, pitem := range plist { if pitem.Tube.Valid && pitem.Tube.String != "" { json.Unmarshal([]byte(pitem.Tube.String), &tube) for k, v := range tube { if iv, iok := v.(float64); iok { if v1, ok := maxTube[k]; ok { if uint(iv) > v1.(uint) { maxTube[k] = uint(iv) } } else { maxTube[k] = uint(iv) } } else { if _, ok := maxTube[k]; ok { maxTube[k] = fmt.Sprintf("%v[%v]", maxTube[k], v) } else { maxTube[k] = v } } } } tx.MustExec("insert into t_order_product(order_id, product_id, product_name, price, picture, quantity) values(?,?,?,?, ?, ?)", orderId, pitem.Id, pitem.Name, pitem.Price, pitem.Picture, productQuantityMap[pitem.Id]) if pitem.StockSwitch == "ON" && pitem.Stock >= 0 { tx.MustExec(sqlStock, productQuantityMap[pitem.Id], pitem.Id) } } tx.Commit() go func() { strTube, _ := json.Marshal(maxTube) _, err = db.Exec("insert into t_order_extra(order_id, pressure_pipe, relationship, is_yunxue, is_dfguomin) values (?,?,?,?,?)", orderItem.Id, strTube, relationship, orderItem.IsYunxue, orderItem.IsDfguomin) if err != nil { fmt.Println(err) } if orderItem.IsNeedPay != "N" { client := util.GetRedis() client.Select(12) client.HSet("order_unpay", orderItem.Id, fmt.Sprintf("%s;%d", orderItem.CreatedAt, orderItem.MaterialId)) } //update sale num of product db.Exec("update t_product set sale_num = sale_num + 1 where id in (" + util.IntJoin(productIds, ",") + ");") }() go func() { if util.SourceCheck(orderItem.Source) && orderItem.Source != "lianren" { client := util.GetRedis() client.Select(12) msg := map[string]interface{}{ "user": "system", "orderid": orderItem.Id, "source": orderItem.Source, "status": constants.ORDERSTATUS_UNPAY, } buf, _ := json.Marshal(msg) client.Publish("order-status-change", string(buf)) } }() return orderItem, nil } func (srv *OrderService) AddOrderInner(orderItem *entity.Order, city_id int, city string) (interface{}, error) { if orderItem == nil || len(orderItem.ProductIds) == 0 { return nil, errors.New("1::wrong request") } var btime time.Time btime, err := time.Parse("2006-01", orderItem.Birthday) if err != nil { btime, err = time.Parse("2006-01-02", orderItem.Birthday) if err != nil { return nil, errors.New("9::wrong birthday") } } if orderItem.Age <= 0 { orderItem.Age = time.Now().Year() - btime.Year() } db := util.GetWriteSqlDB() var ( productIds = []int{} productQuantityMap = map[int]int{} ) for _, v := range orderItem.ProductIds { productIds = append(productIds, v.ProductId) productQuantityMap[v.ProductId] = v.Quantity } var plist []entity.ProductDB = []entity.ProductDB{} db.Select(&plist, "select * from t_product where id in ("+util.IntJoin(productIds, ",")+") order by is_no_booktime desc") if len(plist) != len(productIds) { return nil, errors.New("6::wrong param productids") } tx := db.MustBegin() //orderItem.VisitDate = visitDate orderItem.Payment, orderItem.RepeatItemFee, err = ComputeProductPrice(productIds, productQuantityMap, false) //取消减免重复项目费用 orderItem.RepeatItemFee = 0 if err != nil { tx.Tx.Rollback() return nil, err } /*WORK_FEE_REQUIRED, _ := config.IniConf.Section("server").Key("work_fee_required_bound").Int() WORK_FEE, _ := config.IniConf.Section("server").Key("work_fee").Int() //判断是否需要上门费 */ orderItem.OnlinePayment = orderItem.Payment for _, pitem := range plist { if pitem.SaleMode == 200 || pitem.IsWorkFee == 2 { orderItem.OnlinePayment -= pitem.Price } } discount, _ := config.IniConf.Section("server").Key("inner_add_discount").Int() orderItem.Payment = int(float64(orderItem.Payment) * float64(discount) / 100) /*if orderItem.OnlinePayment > 0 && orderItem.Payment < WORK_FEE_REQUIRED { orderItem.WorkFee = WORK_FEE } */ if orderItem.OnlinePayment <= 0 { orderItem.OnlinePayment = -1 } orderItem.CreatedAt = time.Now().Format("2006-01-02 15:04:05") relationship := orderItem.Relationship orderItem.Relationship = "" if city == "" { db.Get(&city, "select shortname from t_city where id = ?", city_id) } orderId := GenerateOrderNo(city) orderItem.Id = orderId if hooks, ok := orderHooks[BEFORE_ORDER_CREATE]; ok { for _, handle := range hooks { if err = handle(orderItem); err != nil { tx.Tx.Rollback() return nil, err } } } strSql, mkv := util.GenerateInsertSqlFromStruct("t_order", orderItem) osqlResult, err := tx.NamedExec(strSql, mkv) if err != nil { tx.Tx.Rollback() return nil, err } if ra, _ := osqlResult.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return nil, errors.New("8::failed to upadte order info") } var ( tube = map[string]uint{} maxTube = map[string]uint{} ) for _, pitem := range plist { if pitem.Tube.Valid && pitem.Tube.String != "" { json.Unmarshal([]byte(pitem.Tube.String), &tube) for k, v := range tube { if v1, ok := maxTube[k]; ok { if v > v1 { maxTube[k] = v } } else { maxTube[k] = v } } } tx.MustExec("insert into t_order_product(order_id, product_id, product_name, price, picture, quantity) values(?,?,?,?, ?, ?)", orderId, pitem.Id, pitem.Name, pitem.Price, pitem.Picture, productQuantityMap[pitem.Id]) } tx.Commit() go func() { strTube, _ := json.Marshal(maxTube) _, err = db.Exec("insert into t_order_extra(order_id, pressure_pipe, relationship, is_yunxue, is_dfguomin, bloodtest_id) values (?,?,?,?,?,?)", orderItem.Id, strTube, relationship, orderItem.IsYunxue, orderItem.IsDfguomin, orderItem.BloodTestId) if err != nil { fmt.Println(err) } client := util.GetRedis() client.Select(12) client.HSet("order_unpay", orderItem.Id, fmt.Sprintf("%s;%d", orderItem.CreatedAt, orderItem.MaterialId)) //update sale num of product db.Exec("update t_product set sale_num = sale_num + 1 where id in (" + util.IntJoin(productIds, ",") + ");") }() //20210129 护士下单绑定护士信息 if len(orderItem.NurseId) > 0 { nurseId, _ := strconv.Atoi(orderItem.NurseId) strSql = "insert into t_order_deliver_user(order_id,deliver_user_id,career,visit_date,visit_time_range,create_at,get_type) values(?,?,?,?,?,?,?);" sqlResult, _ := db.Exec(strSql, orderId, nurseId, "2", orderItem.VisitDate, orderItem.VisitTimeRange, time.Now().Format("2006-01-02 15:04:05"), "assign") if la, _ := sqlResult.RowsAffected(); la <= 0 { tx.Tx.Rollback() return false, nil } } return orderItem, nil } // 20210527 vip 健康好礼活动 兑换码下单 func (srv *OrderService) AddOrderFCode(orderItem *entity.Order, city_id int, city, fcode string) (interface{}, error) { //直接待接单状态,不用支付 //校验兑换码是否使用? //t_fcode 改成已使用 //jd_order_id 保存 DHM:兑换码 if len(fcode) == 0 { return nil, fmt.Errorf("12:fcode %s error ", fcode) } if orderItem == nil || len(orderItem.ProductIds) == 0 { return nil, errors.New("1::wrong request") } var btime time.Time btime, err := time.Parse("2006-01", orderItem.Birthday) if err != nil { btime, err = time.Parse("2006-01-02", orderItem.Birthday) if err != nil { return nil, errors.New("9::wrong birthday") } } if orderItem.Age <= 0 { orderItem.Age = time.Now().Year() - btime.Year() } db := util.GetWriteSqlDB() var ( productIds = []int{} productQuantityMap = map[int]int{} ) for _, v := range orderItem.ProductIds { productIds = append(productIds, v.ProductId) productQuantityMap[v.ProductId] = v.Quantity } /*var plist []entity.ProductDB = []entity.ProductDB{} //db.Select(&plist, "select * from t_product where id in ("+util.IntJoin(productIds, ",")+") order by is_no_booktime desc") sqlTemp := "select t1.*, sum(t4.quantity) as bought_num from (select * from t_product where id in(" + util.IntJoin(productIds, ",") + ")) t1 left join (select t3.quantity, t3.product_id from t_order t2 left join t_order_product t3 on t2.id = t3.order_id where custom_mobile = ? and t2.status not in (7,9,14) and t2.retype in ('100','110')) t4 on t1.id = t4.product_id GROUP BY t1.id order by t1.is_no_booktime desc" db.Select(&plist, sqlTemp, orderItem.CustomMobile) if len(plist) != len(productIds) { return nil, errors.New("6::wrong param productids") } //检查库存以及个人限量 for _, pitem := range plist { if pitem.StockSwitch == "ON" && pitem.Stock < productQuantityMap[pitem.Id] { return nil, errors.New("10::out of stock") } if pitem.PstockSwitch == "ON" && int(pitem.BoughtNum.Int64)+productQuantityMap[pitem.Id] > pitem.Pstock { return nil, errors.New(fmt.Sprintf("11::upper limit of product %d", pitem.Id)) } }*/ plist, err := Stocklimit(orderItem.CustomMobile, productIds, productQuantityMap, false) if err != nil { return nil, err } //如果产品中含有不需要预约时间的,则省略产能检查 //检查产能是否足够 strSql := "select t1.gnum, t2.remain_num, t2.pdate, t1.id as gid, t2.id as infoid, t1.shortname from (select tpc.id, num as gnum, shortname from t_producer_config tpc,t_city where city_id = t_city.id and city_id = ? and tpc.id = ?) t1 left join t_producer_info t2 on t1.id = t2.global_id and t2.pdate = ? and t2.time_range = ?" var producerInfo = struct { GNum int `db:"gnum"` RemainNum sql.NullInt64 `db:"remain_num"` PDate sql.NullString `db:"pdate"` GId int `db:"gid"` InfoId sql.NullInt64 `db:"infoid"` Shortname string `db:"shortname"` }{} var pinfoSqlResult sql.Result err = db.Get(&producerInfo, strSql, city_id, orderItem.PTId, orderItem.VisitDate, orderItem.VisitTimeRange) if err != nil { return nil, errors.New("4::该城市暂未开通服务") } tx := db.MustBegin() if len(plist) > 0 && plist[0].IsNoBooktime != 1 { visitDate, err := time.Parse("2006-01-02", orderItem.VisitDate) if err != nil { return nil, errors.New("2::wrong visit date") } //当天下单最早只能约第二天上门,240220 houyf改为20点 if visitDate.Before(time.Now()) || (time.Now().Hour() >= 20 && time.Now().Truncate(time.Hour*24).Add(time.Hour*48).After(visitDate)) { return nil, errors.New("3::invalid visit date") } if producerInfo.PDate.String == orderItem.VisitDate && producerInfo.RemainNum.Int64 <= 0 { return nil, errors.New("5::producer not enough") } if !producerInfo.PDate.Valid { pinfoSqlResult = tx.MustExec("insert into t_producer_info(global_id, pdate, time_range, num, remain_num) values(?, ?, ?, ?, ?)", producerInfo.GId, visitDate, orderItem.VisitTimeRange, producerInfo.GNum, producerInfo.GNum-1) lid, err := pinfoSqlResult.LastInsertId() if err != nil { tx.Tx.Rollback() return nil, err } orderItem.MaterialId = int(lid) } else { pinfoSqlResult = tx.MustExec("update t_producer_info set remain_num = ? where id = ? and remain_num = ?", producerInfo.RemainNum.Int64-1, producerInfo.InfoId, producerInfo.RemainNum.Int64) orderItem.MaterialId = int(producerInfo.InfoId.Int64) } if ra, _ := pinfoSqlResult.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return nil, errors.New("7::failed to update producer info") } } //fcode 验证 pinfoSqlResult = tx.MustExec("update t_fcode set already_use_num = already_use_num +1 where code_str = ? and use_num>= already_use_num+1 ", fcode) if ra, _ := pinfoSqlResult.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return nil, errors.New("13::failed to update t_fcode ") } //orderItem.VisitDate = visitDate if city == "" { if producerInfo.Shortname != "" { city = producerInfo.Shortname } else { city = "QG" } } orderId := GenerateOrderNo(city) orderItem.Id = orderId orderItem.Payment, orderItem.RepeatItemFee, err = ComputeProductPrice(productIds, productQuantityMap, false) //取消减免重复项目费用 orderItem.RepeatItemFee = 0 if err != nil { tx.Tx.Rollback() return nil, err } if len(orderItem.ItemIds) > 0 { iprice, _, err := ComputeProductPrice(orderItem.ItemIds, productQuantityMap, true) if err != nil { tx.Tx.Rollback() return nil, err } orderItem.Payment += iprice tempResult := tx.MustExec("insert into t_product_detect_item(detect_item_ids) values(?)", util.IntJoin(orderItem.ItemIds, ",")) if tpid, err := tempResult.LastInsertId(); err != nil { tx.Tx.Rollback() return nil, err } else { tx.MustExec("insert into t_order_product(order_id, product_id, is_personal) values(?,?,?)", orderId, tpid, 1) } } WORK_FEE_REQUIRED, _ := config.IniConf.Section("server").Key("work_fee_required_bound").Int() WORK_FEE, _ := config.IniConf.Section("server").Key("work_fee").Int() //判断是否需要上门费 orderItem.OnlinePayment = orderItem.Payment for _, pitem := range plist { if pitem.SaleMode == 200 || pitem.IsWorkFee == 2 { orderItem.OnlinePayment -= pitem.Price } } if orderItem.Source != "web-jiaxiang" && (orderItem.OnlinePayment > 0 && orderItem.Payment < WORK_FEE_REQUIRED) { orderItem.WorkFee = WORK_FEE } if orderItem.OnlinePayment <= 0 { orderItem.OnlinePayment = -1 } orderItem.CreatedAt = time.Now().Format("2006-01-02 15:04:05") //判断并标记是否来自销售系统订单 if srv.IsAgentOrder(orderItem.OpenId) { orderItem.IsAgent = 1 } relationship := orderItem.Relationship orderItem.Relationship = "" orderItem.JdOrderId = fmt.Sprintf("兑换码:%s", fcode) orderItem.Status = 10 strSql, mkv := util.GenerateInsertSqlFromStruct("t_order", orderItem) osqlResult, err := tx.NamedExec(strSql, mkv) if err != nil { tx.Tx.Rollback() return nil, err } if ra, _ := osqlResult.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return nil, errors.New("8::failed to upadte order info") } var ( tube = map[string]interface{}{} maxTube = map[string]interface{}{} ) var pnames string for _, pitem := range plist { if pitem.Tube.Valid && pitem.Tube.String != "" { json.Unmarshal([]byte(pitem.Tube.String), &tube) for k, v := range tube { if iv, iok := v.(float64); iok { if v1, ok := maxTube[k]; ok { if uint(iv) > v1.(uint) { maxTube[k] = uint(iv) } } else { maxTube[k] = uint(iv) } } else { if _, ok := maxTube[k]; ok { maxTube[k] = fmt.Sprintf("%v[%v]", maxTube[k], v) } else { maxTube[k] = v } } } } tx.MustExec("insert into t_order_product(order_id, product_id, product_name, price, picture, quantity) values(?,?,?,?, ?, ?)", orderId, pitem.Id, pitem.Name, pitem.Price, pitem.Picture, productQuantityMap[pitem.Id]) if pitem.StockSwitch == "ON" && pitem.Stock >= 0 { tx.MustExec("update t_product set stock = stock -? where id = ?;", productQuantityMap[pitem.Id], pitem.Id) } pnames += pitem.Name + "," } tx.Commit() go func() { strTube, _ := json.Marshal(maxTube) _, err = db.Exec("insert into t_order_extra(order_id, pressure_pipe, relationship, is_yunxue, is_dfguomin) values (?,?,?,?,?)", orderItem.Id, strTube, relationship, orderItem.IsYunxue, orderItem.IsDfguomin) if err != nil { fmt.Println(err) } //update sale num of product db.Exec("update t_product set sale_num = sale_num + 1 where id in (" + util.IntJoin(productIds, ",") + ");") }() go func() { sms := &SMSService{dal.DefaultSMSCodeDal} smsType := int(constants.SMS_ORDER_PAID_INFORM) var params = map[string]string{} params["products"] = pnames[0 : len(pnames)-1] sms.SendSMS(orderItem.Mobile, smsType, params) }() return orderItem, nil } // 20240122 创建普通订单以后创建普通非支付订单可以调用次方法 func AddOrderNormal(orderItem *entity.Order) (string, error) { db := util.GetWriteSqlDB() shortName := orderItem.Id us := UserService{} //注册用户 user, err := us._registUser(orderItem.Mobile, false) if err != nil || user == nil { return "", errors.New(fmt.Sprintf("2::RegistUser user error :%s ,[%s]", orderItem.Mobile, err)) } toUpFields := map[string]interface{}{ "custom_mobile": orderItem.Mobile, "name": orderItem.Name, "custom_id": user.Id, "status": 10, "gender": orderItem.Gender, "birthday": orderItem.Birthday, "mobile": orderItem.Mobile, "source": orderItem.Source, "created_at": time.Now().Format("2006-01-02 15:04:05"), } tx := db.MustBegin() var strSql string //todo 插入 t_order_product var pinfo = struct { Name string `db:"name"` ProductId int `db:"product_id"` Price int `db:"price"` Picture string `db:"picture"` Tube null.String `db:"tube"` }{} strSql = "select id, name, price, picture, tube from t_product where id = ?" err = db.Get(&pinfo, strSql, orderItem.ProductIds[0].ProductId) if err != nil { tx.Tx.Rollback() return "", errors.New(fmt.Sprintf("6:: no record t_product :%d[%s]", orderItem.ProductIds[0].ProductId, err)) } id := GenerateOrderNo(shortName) toUpFields["id"] = id toUpFields["payment"] = pinfo.Price //todo 插入 t_order strSql = util.GenerateInsertSql("t_order", toUpFields) _, err = tx.NamedExec(strSql, toUpFields) if err != nil { tx.Tx.Rollback() return "", errors.New(fmt.Sprintf("5:: Insert t_order error :[%s]", err)) } strSql = "insert into t_order_product(order_id, product_id, product_name,price, picture, quantity) values(?,?,?,?,?,1);" _, err = tx.Exec(strSql, id, pinfo.ProductId, pinfo.Name, pinfo.Price, pinfo.Picture) if err != nil { tx.Tx.Rollback() return "", errors.New(fmt.Sprintf("7:: insert t_order_product error :[%s]", err)) } //todo 插入t_order_extra 表 strSql = "insert into t_order_extra(order_id, pressure_pipe) values(?,?);" _, err = tx.Exec(strSql, id, pinfo.Tube) if err != nil { tx.Tx.Rollback() return "", errors.New(fmt.Sprintf("8:: insert t_order_extra error :[%s]", err)) } tx.Commit() return id, nil } // 20211220 酒精基因,推送公众号消息 func (srv *OrderService) sendWXtpl(openid, tpl, url string) { msg := map[string]interface{}{ "touser": openid, "template_id": "4E6pVJlubXT-uItbymrn-ghkvTCBQxBu2-Z0ljL0V3c", "url": url, "data": map[string]interface{}{ "first": map[string]interface{}{ "value": "", "color": "#173177", }, "keyword1": map[string]interface{}{ "value": "PK赢茅台 大奖等你来", "color": "#173177", }, "keyword2": map[string]interface{}{ "value": time.Now().Format("2006-01-02"), "color": "#173177", }, "remark": map[string]interface{}{ "value": tpl, "color": "#173177", }, }, } buf, _ := json.Marshal(msg) _, err := srv.WeixinService.SendTpl(string(buf), "") if err != nil { log.Println(err.Error()) } } // 家政分享下单 相关操作 20200814 qz // openId:下单人, // toopenId:分享人, // custommobild 下单人电话 func (srv *OrderService) InsertAgent(orderId, openId, toopenId, customMobile string) (interface{}, error) { //检索 分享人id 查不到就终止操作 parentagent, err := getAgentByOpenId(toopenId) if err != nil { return nil, errors.New("no toopenid found") } fmt.Println(parentagent) db := util.GetWriteSqlDB() agent, _ := getAgentByOpenId(openId) if agent != nil { userparentid := "" agentType := 2 if agent.UserParentId.String == "0" { //如果 userparentid = agent.UserParentId.String if agent.AgentType.Int64 == 0 { agentType = 2 } else if agent.AgentType.Int64 == 1 { agentType = 3 } else { agentType = int(agent.AgentType.Int64) } //更新 _, err = db.Exec("update t_agent set userparentid = ? ,type = ?where id = ?", userparentid, agentType, agent.Id) } } else { _, err = db.Exec("insert into t_agent(openid,userparentid,mobile,type) values(?,?,?,2)", openId, parentagent.Id, customMobile) } //日志存mongodb //defer func() { // // go func() { // // db := getMgoDB() // // defer db.Session.Close() // // message := "success" // // if err != nil { // // message = err.Error() // // } // // db.C("Order_add_InsertAgent_log").Insert(bson.M{ // // "orderId": orderId, // // "toOpenId": toopenId, // // "mobile": customMobile, // // "message": message, // // "created_at": time.Now().Format("2006-01-02 15:04:05"), // // }) // // }() // //}() return nil, err } func getAgentByOpenId(openid string) (*entity.AgentEntity, error) { strSql := "select id,type,openid,userparentid from t_agent a where openid = ? " db := util.GetSqlDB() var items entity.AgentEntity err := db.Get(&items, strSql, openid) if err != nil { return nil, errors.New("no record found") } return &items, nil } func (srv *OrderService) Pay(orderItem *entity.Order, extra map[string]string) (interface{}, error) { if orderItem == nil || orderItem.Id == "" { return nil, errors.New("wrong request") } ti, err := srv.IOrder.Detail(orderItem.Id) if err != nil { return nil, err } orderDb := ti.(*dal.OrderResult) //todo 检查用户是否已经生成支付请求 if orderDb == nil { return nil, errors.New("1::can not find order " + orderItem.Id) } if orderDb.Status != int(constants.ORDERSTATUS_UNPAY) { return nil, errors.New("2::order already paid or canceled") } //判断是否使用优惠券并检查券的有效性 if len(orderItem.Coupons) > 0 { coupon, err := dal.DefaultCouponDal.Get(orderItem.Coupons[0], "") if err != nil { return nil, errors.New("query coupon error") } if coupon == nil { return nil, errors.New("3::coupon not found") } if coupon.Mobile != orderDb.CustomMobile && coupon.Mobile != orderDb.Mobile { return nil, errors.New("4::wrong coupon") } if coupon.OrderId.Valid && coupon.OrderId.String == orderItem.Id { goto SkipCheck } if !coupon.IsValid(orderDb.Payment) { return nil, errors.New("5::invalid coupon") } SkipCheck: /*if coupon.TypeId == int(constants.COUPONTYPE_LIMIT) && coupon.UseMaxValue.Int64 > int64(orderDb.Payment) { return nil, errors.New("can not use this coupon") }*/ var plist = []struct { ProductId string `json:"pid"` Name string `json:"name"` }{} if coupon.BindProducts.Valid && strings.Trim(coupon.BindProducts.String, " ") != "" { err = json.Unmarshal([]byte(coupon.BindProducts.String), &plist) if err != nil { return nil, errors.New("6::invalid product limit coupon") } } var computeValidProductPrice = func() (bool, int) { var ( validTotalPrice int isValid bool ) for _, opitem := range orderDb.Products { if len(plist) > 0 { for _, pitem := range plist { if opitem.DiscountSwitch == "OFF" { continue } if strconv.Itoa(opitem.ProductId) == pitem.ProductId { validTotalPrice += opitem.ProductPrice * opitem.Quantity isValid = true break //break OUTER_PRODUCT_LOOP } } } else { isValid = true if opitem.DiscountSwitch == "ON" { validTotalPrice += opitem.ProductPrice * opitem.Quantity } } } return isValid, validTotalPrice } //计算可以优惠的产品总价 var maxProductPrice int var valid bool = false valid, maxProductPrice = computeValidProductPrice() //OUTER_PRODUCT_LOOP: if len(plist) > 0 { if !valid { return nil, errors.New("7::coupon and product dismatch") } //绑定产品的券去掉上门费 orderDb.WorkFee.SetValid(0) } if coupon.Value > maxProductPrice { coupon.Value = maxProductPrice } //满减券 if coupon.TypeId == int(constants.COUPONTYPE_LIMIT) { if coupon.UseMaxValue.Int64 > int64(maxProductPrice) { return nil, errors.New("5::invalid limit coupon") } } //免服务费券 if coupon.TypeId == int(constants.COUPONTYPE_SERVICE) && orderDb.WorkFee.Int64 > 0 { coupon.Value = int(orderDb.WorkFee.Int64) } //折扣券 if coupon.TypeId == int(constants.COUPONTYPE_DISCOUNT) { coupon.Value = maxProductPrice * (100 - coupon.Value) / 100 } orderItem.FreeFee = coupon.Value orderItem.ActualPayment -= coupon.Value } //判断是否需要上门费 //if orderDb.Payment < WORK_FEE_REQUIRED { orderItem.WorkFee = int(orderDb.WorkFee.Int64) orderItem.ActualPayment += orderItem.WorkFee //} orderItem.ActualPayment += orderDb.Payment - orderDb.RepeatItemFee //update order status //in order to update workfee to zero, let workfee euqals -1 if orderItem.WorkFee == 0 { orderItem.WorkFee = -1 } var payResult interface{} payType := constants.PaymentType(orderItem.PayType) if orderItem.ActualPayment <= 0 { //不需要使用第三方支付接口了 payResult = map[string]interface{}{ "needThirdPay": false, "payResult": "SUCCESS", } ouitem := entity.Order{ Status: int(constants.ORDERSTATUS_UNRECEIVE), FreeFee: orderItem.FreeFee, PaymentTime: time.Now().Format("2006-01-02 15:04:05"), PayType: int(constants.PAYMENTTYPE_COUPON), PayNo: GenerateOrderPayNO("P"), Id: orderDb.Id, WorkFee: orderItem.WorkFee, } _, err := srv.IOrder.Update(&ouitem) if err != nil { return nil, err } go func() { sms := &SMSService{dal.DefaultSMSCodeDal} smsType := int(constants.SMS_ORDER_PAID_INFORM) var pnames string for _, opitem := range orderDb.Products { pnames += opitem.ProductName + "," } if orderDb.Source == "jd-jicai" { smsType = int(constants.SMS_JDBOOK) } sms.SendSMS(orderDb.Mobile, smsType, map[string]string{ "OrderId": orderDb.Id, "products": pnames[0 : len(pnames)-1], "time": orderDb.VisitDate.String + " " + orderDb.VisitTimeRange.String, "address": orderDb.Address + "(" + orderDb.DetailAddress + ")", }) _, err = srv.SendWxPaySuccessMsg(map[string]string{ "pay_no": ouitem.PayNo, "openid": extra["openid"], "money": "0元", "wx_type": extra["wx_type"], "sms_ctype": "", }) if err != nil { log.Println(err) } srv.firstGiveTicket(orderDb.CustomMobile) //inform admin client := util.GetRedis() client.Select(12) msg := map[string]interface{}{ "user": "system", "orderid": orderItem.Id, "source": orderDb.Source, "status": constants.ORDERSTATUS_UNRECEIVE, } buf, _ := json.Marshal(msg) client.Publish("order-status-change", string(buf)) delUnpayFromRedis(orderItem.Id) }() goto AFTERPAY //return nil, nil } //根据支付类型调用相应支付方法 switch payType { case constants.PAYMENTTYPE_WEIXIN: payResult, err = srv.wxPay(orderItem, extra) case constants.PAYMENTTYPE_ALIPAY: payResult, err = srv.aliPay(orderItem, extra) default: return nil, errors.New("8::UnSupport pay type") } if err != nil { return nil, err } //update coupon status if used AFTERPAY: if len(orderItem.Coupons) > 0 { citem := entity.DiscountTicket{ Id: orderItem.Coupons[0], Status: 1, OrderId: orderItem.Id, } dal.DefaultCouponDal.Update(&citem) } //todo when pay success set coupon status to used go func() { if util.SourceCheck(orderDb.Source) { client := util.GetRedis() client.Select(12) msg := map[string]interface{}{ "user": "system", "orderid": orderDb.Id, "source": orderDb.Source, "status": constants.ORDERSTATUS_UNPAY, } buf, _ := json.Marshal(msg) client.Publish("order-status-change", string(buf)) } }() return payResult, nil } // 从redis删除该订单未支付状态并发布消息通知订单状态变更 func delUnpayFromRedis(orderId string) { client := util.GetRedis() client.Select(12) client.HDel("order_unpay", orderId) } func (srv *OrderService) IsAgentOrder(openid string) bool { strSql := "select openid from t_agent a where openid = ? and a.type in(2,3) and a.userparentid>0 and a.isdelete='N'" db := util.GetSqlDB() var items = []string{} db.Select(&items, strSql, openid) if len(items) <= 0 { return false } return true } //const NOORDERID string = "NOORDERID" // 微信支付统一下单 func (srv *OrderService) wxPay(orderItem *entity.Order, extra map[string]string) (interface{}, error) { //todo check whether have already done a wx union pay request if extra == nil { return nil, errors.New("miss param extra") } if _, ok := extra["ip"]; !ok { return nil, errors.New("need request ip") } if _, ok := extra["trade_type"]; !ok { return nil, errors.New("need param trade_type of weixin pay") } if _, ok := extra["device_info"]; !ok && extra["trade_type"] == "JSAPI" { extra["device_info"] = "WEB" } if _, ok := extra["openid"]; !ok && extra["trade_type"] == "JSAPI" { return nil, errors.New("miss param openid") } payNo := GenerateOrderPayNO("P") wxJsSdk := wx.NewJsSdk() var ( appid string = wx.DefaultConfig["appid"] secret = wx.DefaultConfig["secret"] ) if extra["wx_type"] == "mp" { appid = config.IniConf.Section("weixin").Key("wx.mp.appid").Value() secret = config.IniConf.Section("weixin").Key("wx.mp.secret").Value() } weixin := wx.NewWeixin(appid, secret) nonceStr := wxJsSdk.GenerateNoncestr(16) reqItem := map[string]string{ "appid": appid, "mch_id": wx.DefaultConfig["mch_id"], "nonce_str": nonceStr, "body": "小鸟快验订单-" + payNo, "notify_url": wx.DefaultConfig["paycb_notify_url"], "out_trade_no": payNo, "spbill_create_ip": extra["ip"], "total_fee": fmt.Sprintf("%d", orderItem.ActualPayment), "trade_type": extra["trade_type"], "attach": orderItem.Id, } if extra["trade_type"] == "JSAPI" { reqItem["openid"] = extra["openid"] } if extra["device_info"] != "" { reqItem["device_info"] = extra["device_info"] } sign := wxJsSdk.ComputePaySignature(reqItem, wx.DefaultConfig["apikey"]) reqItem["sign"] = sign fmt.Println(reqItem) rel, err := weixin.PrepareOrder(reqItem) if err != nil { return nil, err } if rel.ReturnCode == "FAIL" { return nil, errors.New(rel.ReturnMsg) } //update order payno upOrder := &entity.Order{ PayNo: payNo, Id: orderItem.Id, WorkFee: orderItem.WorkFee, FreeFee: orderItem.FreeFee, } _, err = srv.IOrder.Update(upOrder) if err != nil { return nil, err } resultItem := map[string]string{ "appId": appid, "timeStamp": fmt.Sprintf("%d", time.Now().Unix()), "nonceStr": wxJsSdk.GenerateNoncestr(16), "package": "prepay_id=" + rel.PrepayId, "signType": "MD5", "mweb_url": rel.MWebUrl, "code_url": rel.CodeUrl, } //小程序支付时保存prepare_id 到redis,用于后续支付成功发送模板消息 if extra["wx_type"] == "mp" { rclient := util.GetRedis() rclient.Select(12) rclient.HSet("mp_prepare_id", orderItem.Id, rel.PrepayId) } resultItem["sign"] = wxJsSdk.ComputePaySignature(resultItem, config.IniConf.Section("weixin").Key("wx.apikey").Value()) return resultItem, nil } // 支付宝支付 func (srv *OrderService) aliPay(orderItem *entity.Order, extra map[string]string) (interface{}, error) { aliClient := ali.GetAliClient() if aliClient == nil { return nil, fmt.Errorf("支付宝初始化错误") } pay := alipay.TradeCreate{} payNo := GenerateOrderPayNO("P") pay.NotifyURL = config.IniConf.Section("ali").Key("ali.paycb_notify_url").Value() //支付标题 pay.Subject = "小鸟快验订单-" + payNo //订单号,一个订单号只能支付一次 pay.OutTradeNo = payNo //商品code //pay.ProductCode = time.Now().String() //金额 pay.TotalAmount = strconv.FormatFloat(float64(orderItem.ActualPayment)/100, 'f', -1, 64) pay.BuyerId = extra["user_zfb_id"] res, err := aliClient.TradeCreate(pay) //失败 if err != nil { return nil, err } if !res.Content.Code.IsSuccess() { return nil, fmt.Errorf(res.Content.Msg) } upOrder := &entity.Order{ PayNo: payNo, Id: orderItem.Id, WorkFee: orderItem.WorkFee, FreeFee: orderItem.FreeFee, } _, err = srv.IOrder.Update(upOrder) resultItem := map[string]string{ "trade_no": res.Content.TradeNo, } return resultItem, nil } func (srv *OrderService) PayQuery(oid string, extra map[string]string) (interface{}, error) { orderDb, err := srv.IOrder.Get(oid) if err != nil { return nil, err } //todo 检查用户是否已经生成支付请求 if orderDb == nil { return nil, errors.New("1::can not find order " + oid) } if orderDb.ActualPayment.Int64 > 0 && orderDb.Status != int(constants.ORDERSTATUS_UNPAY) { return map[string]string{ "status": "SUCCESS", "order_id": oid, "actual_payment": fmt.Sprintf("%d", orderDb.ActualPayment.Int64), }, nil } switch extra["pay_type"] { case fmt.Sprintf("%d", constants.PAYMENTTYPE_WEIXIN): extra["out_trade_no"] = orderDb.PayNo.String return srv.WxPayQuery(extra) case fmt.Sprintf("%d", constants.PAYMENTTYPE_ALIPAY): extra["out_trade_no"] = orderDb.PayNo.String return srv.AliPayQuery(extra) default: return nil, errors.New("UNKNOWN PAY TYPE") } } func (srv *OrderService) WxPayQuery(extra map[string]string) (map[string]string, error) { wxJsSdk := wx.NewJsSdk() var ( appid string = wx.DefaultConfig["appid"] secret = wx.DefaultConfig["secret"] ) if extra["wx_type"] == "mp" { appid = config.IniConf.Section("weixin").Key("wx.mp.appid").Value() secret = config.IniConf.Section("weixin").Key("wx.mp.secret").Value() } weixin := wx.NewWeixin(appid, secret) nonceStr := wxJsSdk.GenerateNoncestr(16) reqItem := map[string]string{ "appid": appid, "mch_id": wx.DefaultConfig["mch_id"], "nonce_str": nonceStr, "out_trade_no": extra["out_trade_no"], "sign_type": "MD5", } sign := wxJsSdk.ComputePaySignature(reqItem, wx.DefaultConfig["apikey"]) reqItem["sign"] = sign rel, err := weixin.OrderQuery(reqItem) if err != nil { return nil, err } if rel["return_code"] == "FAIL" { return nil, errors.New(rel["return_msg"]) } if rel["return_code"] == "FAIL" { return nil, errors.New(rel["err_code"] + ":" + rel["err_code_des"]) } //kv, _ := util.StructToMap(rel) kv := rel osign := rel["sign"] delete(kv, "sign") csign := wxJsSdk.ComputePaySignature(kv, wx.DefaultConfig["apikey"]) if csign != osign { log.Println("pay_query_err: wrong sign", csign, osign, kv) return nil, errors.New("wrong sign") } log.Println("weixin pay query of order ", extra["out_trade_no"], ":", rel["trade_state"]) if rel["trade_state"] == "SUCCESS" { //TODO update order status and pay infomation srv.WxPayCB(rel) } return map[string]string{ "status": rel["trade_state"], "actual_payment": rel["total_fee"], }, nil } // 支付宝支付查询 func (srv *OrderService) AliPayQuery(extra map[string]string) (map[string]string, error) { client := ali.GetAliClient() req := alipay.TradeQuery{} req.OutTradeNo = extra["out_trade_no"] res, err := client.TradeQuery(req) if err != nil { return nil, errors.New("1::aliquery err" + err.Error()) } if !res.IsSuccess() { return nil, errors.New("2::aliquery err" + res.Content.SubMsg) } kv := map[string]string{ "gmt_payment": res.Content.SendPayDate, "total_amount": res.Content.TotalAmount, "out_trade_no": res.Content.OutTradeNo, "trade_no": res.Content.TradeNo, } srv.AliPayCB(kv) moneyFloat, _ := strconv.ParseFloat(res.Content.TotalAmount, 64) moneyInt := int64(moneyFloat * 100) actualPay := strconv.FormatInt(moneyInt, 10) return map[string]string{ "status": "SUCCESS", "actual_payment": actualPay, }, nil } // 统一支付成功后推送微信消息 type pName struct { PName string `db:"product_name"` PId string `db:"product_id"` OrderId string `db:"oid"` PreOrderId string `db:"pre_order_id"` } // 推荐有奖活动-支付成功回调处理逻辑 func (srv *OrderService) recommentActCallback(params map[string]string) { db := util.GetWriteSqlDB() var actInfo = struct { Mobile null.String `db:"mobile"` Status null.Int `db:"age"` Id int `db:"id"` }{} const source string = "SHARE_PRIZE" strSql := "select id, mobile, age from t_activity_info where custom_name = ? and source = ? order by created_at desc limit 1;" err := db.Get(&actInfo, strSql, params["openid"], source) if err != nil { if err == sql.ErrNoRows { return } log.Println("recommentActCallback: ", err) return } if actInfo.Id == 0 || actInfo.Status.Int64 == 1 { return } //自己扫自己 if params["mobile"] == actInfo.Mobile.String { return } strSql = "select id from t_activity_info where mobile = ? and custom_name = ? and source = ? and age = 1;" var hasGetId int err = db.Get(&hasGetId, strSql, actInfo.Mobile.String, source) if hasGetId > 0 { return } strSql = "select count(*) from t_activity_info where mobile = ? and source = ? and age = 1;" var ac int err = db.Get(&ac, strSql, actInfo.Mobile.String, source) if err != nil { log.Println("recommentActCallback1: ", err) return } strSql = "update t_activity_info set age = 1 where id =?;" db.Exec(strSql, actInfo.Id) //发酒精代谢酶基因检测券 now := time.Now() _, err = dal.DefaultCouponDal.Save(&entity.DiscountTicket{ Mobile: params["mobile"], TypeId: int(constants.COUPONTYPE_NORMAL), BindProducts: `[{"pid":"26", "name":"酒精代谢酶基因检测"}]`, Value: 19900, CreatedAt: now.Format("2006-01-02 15:04:05"), Deadline: now.Add(time.Hour * 24 * 30 * 6).Format("2006-01-02 15:04:05"), }) if err != nil { fmt.Println("recommentActCallback: ", err) return } var ( firstVal string = "感谢您参加分享有奖活动,现赠送您酒精基因检测一份" remarkVal string = "我们已往您的账户内发放好相应的优惠券,点击下单" linkUrl string = config.IniConf.Section("weixin").Key("wx.front_host").Value() + "/index/index/detail/id/26" toUser string = params["openid"] ) msg := map[string]interface{}{ "touser": &toUser, "template_id": "gglvp7ygDMpqMgxW7scWCAW7xochSKD1LZtojEkNxcg", "url": &linkUrl, "data": map[string]interface{}{ "first": map[string]interface{}{ "value": &firstVal, "color": "#173177", }, "keyword1": map[string]interface{}{ "value": "推荐有奖活动", "color": "#173177", }, "keyword2": map[string]interface{}{ "value": "", "color": "#173177", }, "remark": map[string]interface{}{ "value": &remarkVal, "color": "#173177", }, }, } buf, _ := json.Marshal(msg) _, err = srv.WeixinService.SendTpl(string(buf), "") if err != nil { log.Println("recommentActCallback: ", err) } if ac > 4 { return } //发100元优惠券 _, err = dal.DefaultCouponDal.Save(&entity.DiscountTicket{ Mobile: actInfo.Mobile.String, TypeId: int(constants.COUPONTYPE_NORMAL), CreatedAt: now.Format("2006-01-02 15:04:05"), Deadline: now.Add(time.Hour * 24 * 30 * 6).Format("2006-01-02 15:04:05"), Value: 10000, }) if err != nil { log.Println("recommentActCallback: ", err) return } //发送微信通知 var uextra null.String db.Get(&uextra, "select extra from t_custom where mobile = ? limit 1", actInfo.Mobile.String) if !uextra.Valid || uextra.String == "" { log.Println("fetch openid failed with mobile: ", actInfo.Mobile.String) return } var extraMap = map[string]string{} err = json.Unmarshal([]byte(uextra.String), &extraMap) if err != nil { log.Println("wrong extra json format from custom") return } var wxNickname null.String db.Get(&wxNickname, "select nickname from t_wechat_userinfo where openid = ?", params["openid"]) toUser = extraMap["openid"] firstVal = fmt.Sprintf("您的好友%s通过您分享的海报下单啦!恭喜您获得100元优惠券,赶快到账户中查看吧!", wxNickname.String) remarkVal = fmt.Sprintf("您还有%d次获得优惠券的机会哦,快快邀请其他好友吧~", 4-ac) linkUrl = config.IniConf.Section("weixin").Key("wx.front_host").Value() + "/index/user/my" buf, _ = json.Marshal(msg) _, err = srv.WeixinService.SendTpl(string(buf), "") if err != nil { log.Println("recommentActCallback: ", err) } } func (srv *OrderService) firstGiveTicket(mobile string) { //如果是用户首次下单,发优惠券50,有效期两个星期 if isFirst, err := srv.IOrder.IsFirst(mobile); err != nil && isFirst { //add coupon user account related to the order couponItem := &entity.DiscountTicket{ TypeId: int(constants.COUPONTYPE_DISCOUNT), Mobile: mobile, Value: 88, Deadline: time.Now().Add(time.Hour * 24 * 60).Format("2006-01-02 15:04:05"), CreatedAt: time.Now().Format("2006-01-02 15:04:05"), } _, err = dal.DefaultCouponDal.Save(couponItem) if err != nil { log.Println("firstGiveTicket: ", err) } } else { if err != nil { log.Println(err) } } } func (srv *OrderService) WxPayCB(payResult map[string]string) { db := util.GetWriteSqlDB() payTime, _ := time.Parse("20060102150405", payResult["time_end"]) actualPay := payResult["total_fee"] strSql := `select t1.id, mobile, address, detail_address, custom_mobile, visit_date, visit_time_range, t2.product_id,t2.product_name, t3.is_jiyin, source from t_order t1 left join t_order_product t2 on t1.id = t2.order_id left join t_product t3 on t2.product_id = t3.id where t1.pay_no = ?` var orderItem = []struct { Id string `db:"id"` Mobile string `db:"mobile"` CustomMobile string `db:"custom_mobile"` VisitDate null.String `db:"visit_date"` VisitTimeRange null.String `db:"visit_time_range"` ProductName string `db:"product_name"` ProductId int `db:"product_id"` Source string `db:"source"` Address string `db:"address"` DetailAddress string `db:"detail_address"` IsJiyin int `db:"is_jiyin"` }{} err := db.Select(&orderItem, strSql, payResult["out_trade_no"]) if len(orderItem) <= 0 { log.Println("WX callback error: can not found order of out trade no ", payResult["out_trade_no"]) if err != nil { log.Println(err) } return } srv.firstGiveTicket(orderItem[0].CustomMobile) var status = 2 if orderItem[0].Source == "web-jiaxiang" { status = 3 } //if util.ProductStatusJumpTo10(orderItem[0].ProductId) { // status = 10 //} sqlResult, err := db.Exec("update t_order set status = ?, third_pay_no = ?, actual_payment = ?, payment_time = ?, pay_type = ? where pay_no = ? and status = 1", status, payResult["transaction_id"], actualPay, payTime.Format("2006-01-02 15:04:05"), constants.PAYMENTTYPE_WEIXIN, payResult["out_trade_no"]) if err != nil { log.Println("UPDATE_PAY_STATUS:", err) return } else { if ra, _ := sqlResult.RowsAffected(); ra <= 0 { log.Println("UPDATE_PAY_STATUS:", "update failed") return } go func() { var ( pnames string ) if err == nil { for _, oitem := range orderItem { pnames += oitem.ProductName + "," } //20230413 问诊不发送短信 if util.ProductWenZhen(orderItem[0].ProductId) { goto JUMPSMS } //fmt.Println(orderItem, isJiyin, hasMBH) sms := &SMSService{dal.DefaultSMSCodeDal} smsType := int(constants.SMS_ORDER_PAID_INFORM) var params = map[string]string{} params["OrderId"] = orderItem[0].Id params["products"] = pnames[0 : len(pnames)-1] params["time"] = orderItem[0].VisitDate.String + " " + orderItem[0].VisitTimeRange.String params["address"] = orderItem[0].Address + "(" + orderItem[0].DetailAddress + ")" if orderItem[0].Source == "jd-jicai" { smsType = int(constants.SMS_JDBOOK) } sms.SendSMS(orderItem[0].Mobile, smsType, params) } JUMPSMS: //inform admin client := util.GetRedis() client.Select(12) msg := map[string]interface{}{ "user": "system", "orderid": orderItem[0].Id, "source": orderItem[0].Source, "status": constants.ORDERSTATUS_UNRECEIVE, } buf, _ := json.Marshal(msg) client.Publish("order-status-change", string(buf)) client.HDel("order_unpay", orderItem[0].Id) //call recommentActCallback srv.recommentActCallback(map[string]string{ "mobile": orderItem[0].Mobile, "openid": payResult["openid"], }) }() var wxType = "" if payResult["appid"] == config.IniConf.Section("weixin").Key("wx.mp.appid").Value() { wxType = "mp" } smsCtype := "" tfee, _ := strconv.ParseInt(payResult["total_fee"], 10, 64) _, err = srv.SendWxPaySuccessMsg(map[string]string{ "pay_no": payResult["out_trade_no"], "openid": payResult["openid"], "money": fmt.Sprintf("%.2f元", float32(tfee)/100), "wx_type": wxType, "sms_ctype": smsCtype, }) if err != nil { log.Println(err) } //qz add 拆单的操作 //go srv.SplitOrder(orderItem[0].Id, orderItem[0].ProductId) //20210816 加急核酸1274 发送钉钉 if orderItem[0].ProductId == 1274 { srv.SendDingMsg(orderItem[0].Id) } //20211019 修改web-jiaxiang 护士佣金 前置条件/order/inner/pay ->20210129 护士下单绑定护士信息 go func() { var count int db.Get(&count, "select count(1) from t_order_deliver_user where order_id = ?", orderItem[0].Id) if count > 0 && orderItem[0].Source == "web-jiaxiang" { pay, err := strconv.Atoi(actualPay) pay = pay / 10 sqlResult, err = db.Exec("update t_order_extra set paynurse = ? where order_id = ?", pay, orderItem[0].Id) if err != nil { log.Printf("error update t_order_extra paynurse id:%s payment:%s err:%v", orderItem[0].Id, actualPay, err) return } log.Printf("success update t_order_extra paynurse id:%s payment:%s ", orderItem[0].Id, actualPay) } }() } } // 支付宝callback func (srv *OrderService) AliPayCB(payResult map[string]string) { db := util.GetWriteSqlDB() _, err := time.Parse("2006-01-02 15:04:05", payResult["gmt_payment"]) // noti.GmtPayment if err != nil { payResult["gmt_payment"] = time.Now().Format("2006-01-02 15:04:05") } moneyFloat, _ := strconv.ParseFloat(payResult["total_amount"], 64) //noti.TotalAmount moneyInt := int64(moneyFloat * 100) actualPay := strconv.FormatInt(moneyInt, 10) strSql := `select t1.id, mobile, address, detail_address, custom_mobile, visit_date, visit_time_range, t2.product_id,t2.product_name, t3.is_jiyin, source from t_order t1 left join t_order_product t2 on t1.id = t2.order_id left join t_product t3 on t2.product_id = t3.id where t1.pay_no = ?` var orderItem = []struct { Id string `db:"id"` Mobile string `db:"mobile"` CustomMobile string `db:"custom_mobile"` VisitDate null.String `db:"visit_date"` VisitTimeRange null.String `db:"visit_time_range"` ProductName string `db:"product_name"` ProductId int `db:"product_id"` Source string `db:"source"` Address string `db:"address"` DetailAddress string `db:"detail_address"` IsJiyin int `db:"is_jiyin"` }{} err = db.Select(&orderItem, strSql, payResult["out_trade_no"]) //noti.OutTradeNo if len(orderItem) <= 0 { log.Println("ZFB callback error: can not found order of out trade no ", payResult["out_trade_no"]) if err != nil { log.Println(err) } return } srv.firstGiveTicket(orderItem[0].CustomMobile) var status = 2 if orderItem[0].Source == "web-jiaxiang" { status = 3 } sqlResult, err := db.Exec("update t_order set status = ?, third_pay_no = ?, actual_payment = ?, payment_time = ?, pay_type = ? where pay_no = ? and status = 1", status, payResult["trade_no"], actualPay, payResult["gmt_payment"], constants.PAYMENTTYPE_ALIPAY, payResult["out_trade_no"]) if err != nil { log.Println("UPDATE_PAY_STATUS:", err) return } else { if ra, _ := sqlResult.RowsAffected(); ra <= 0 { log.Println("UPDATE_PAY_STATUS:", "update failed") return } go func() { var ( pnames string ) if err == nil { for _, oitem := range orderItem { pnames += oitem.ProductName + "," } //fmt.Println(orderItem, isJiyin, hasMBH) sms := &SMSService{dal.DefaultSMSCodeDal} smsType := int(constants.SMS_ORDER_PAID_INFORM) var params = map[string]string{} params["OrderId"] = orderItem[0].Id params["products"] = pnames[0 : len(pnames)-1] params["time"] = orderItem[0].VisitDate.String + " " + orderItem[0].VisitTimeRange.String params["address"] = orderItem[0].Address + "(" + orderItem[0].DetailAddress + ")" if orderItem[0].Source == "jd-jicai" { smsType = int(constants.SMS_JDBOOK) } sms.SendSMS(orderItem[0].Mobile, smsType, params) } //inform admin client := util.GetRedis() client.Select(12) msg := map[string]interface{}{ "user": "system", "orderid": orderItem[0].Id, "source": orderItem[0].Source, "status": constants.ORDERSTATUS_UNRECEIVE, } buf, _ := json.Marshal(msg) client.Publish("order-status-change", string(buf)) client.HDel("order_unpay", orderItem[0].Id) //call recommentActCallback //srv.recommentActCallback(map[string]string{ // "mobile": orderItem[0].Mobile, // "openid": payResult["openid"], //}) }() //qz add 拆单的操作 //go srv.SplitOrder(orderItem[0].Id, orderItem[0].ProductId) _, err = srv.SendWxPaySuccessMsg(map[string]string{ "pay_no": payResult["out_trade_no"], }) //20210816 加急核酸1274 发送钉钉 if orderItem[0].ProductId == 1274 { srv.SendDingMsg(orderItem[0].Id) } } } func (srv *OrderService) SendWxPaySuccessMsg(params map[string]string) (interface{}, error) { strSql := "select t1.id as oid, product_name,product_id from t_order t1 left join t_order_product t2 on t1.id = t2.order_id where t1.pay_no = ?" var nameList = []pName{} db := util.GetSqlDB() err := db.Select(&nameList, strSql, params["pay_no"]) if err != nil { return nil, err } if len(nameList) == 0 { return nil, errors.New("order have no product") } go srv.IOrder.CheckAndSplitOrder(nameList[0].OrderId) //20230413 问诊不发送微信通知 pid, _ := strconv.Atoi(nameList[0].PId) if util.ProductWenZhen(pid) { return nil, nil } strPName := "" strPid := "" yuanyiMap := map[string]struct{}{} for i, item := range nameList { if i+1 == len(nameList) { strPName += item.PName strPid += item.PId } else { strPName += item.PName + " " strPid += item.PId + "," } yuanyiMap[item.PId] = struct{}{} } isExistShangMen := 1 //20220524 如果只有邮寄的,那么更换消息通知,存在上门的,就保持原来的推送 db.Get(&isExistShangMen, "select count(1) from t_product where id in ("+strPid+") and is_no_booktime =0") var wxBodyContent = config.IniConf.Section("weixin").Key("msg.pay_success").Value() if params["sms_ctype"] == "MBH" { wxBodyContent = config.IniConf.Section("weixin").Key("msg.pay_success_mbh").Value() } if isExistShangMen == 0 { wxBodyContent = config.IniConf.Section("weixin").Key("msg.pay_success_youji").Value() } //20220525 远毅 1338 ,推送新的报文 if len(yuanyiMap) == 1 { for k := range yuanyiMap { if util.ProductYuanYi(k) { wxBodyContent = config.IniConf.Section("weixin").Key("msg.pay_success_yuanyi").Value() } } } msg := map[string]interface{}{ "touser": params["openid"], "template_id": "og_C32ekCtZDIG86FodplOJ-Agj0pYYjTlQLJxt3j_0", "url": config.IniConf.Section("weixin").Key("wx.front_host").Value() + "/activity/question", // + nameList[0].OrderId, "data": map[string]interface{}{ "first": map[string]interface{}{ "value": wxBodyContent, "color": "#173177", }, "orderMoneySum": map[string]interface{}{ "value": params["money"], "color": "#173177", }, "orderProductName": map[string]interface{}{ "value": strPName, "color": "#173177", }, "Remark": map[string]interface{}{ "value": "为了能更加精准解读您的检测报告,请您点击详情填写“身体健康评估表”,我们将对您的个人信息进行严格保密。首次填写身体健康评估表并完成提交,小鸟将为您奉上金额不等优惠卷,随时供您使用。", "color": "#173177", }, }, } if params["wx_type"] == "mp" { rclient := util.GetRedis() rclient.Select(12) strCmd := rclient.HGet("mp_prepare_id", nameList[0].OrderId) if terr := strCmd.Err(); terr != nil { log.Println(terr) return false, terr } msg = map[string]interface{}{ "touser": params["openid"], "template_id": "_4Ps93u932mI8tnFhnde02aCKekEgVOLNPYc5L67Nuw", "page": "", "form_id": strCmd.Val(), "data": map[string]interface{}{ "keyword1": map[string]interface{}{ "value": params["money"], "color": "#173177", }, "keyword2": map[string]interface{}{ "value": strPName, "color": "#173177", }, "keyword3": map[string]interface{}{ "value": "为了能更加精准解读您的检测报告,请您点击详情填写“身体健康评估表”,我们将对您的个人信息进行严格保密。首次填写身体健康评估表并完成提交,小鸟将为您奉上金额不等优惠卷,随时供您使用。", "color": "#173177", }, }, } } buf, _ := json.Marshal(msg) return srv.WeixinService.SendTpl(string(buf), params["wx_type"]) /*_, err = srv.WeixinService.SendTpl(string(buf)) if err != nil { log.Println(err) } msg = map[string]interface{}{ "touser": params["openid"], "msgtype": "news", "news": map[string]interface{}{ "articles": []map[string]interface{}{ map[string]interface{}{ "title": "身体健康调查表", "description": "详细填写后,医生会为您撰写更加精准的报告", "url": config.IniConf.Section("weixin").Key("wx.front_host").Value() + "/activity/question", "picurl": config.IniConf.Section("weixin").Key("wx.front_host").Value() + "/images/wx_survey.jpg", }, }, }, } msg = map[string]interface{}{ "touser": params["openid"], "msgtype": "text", "text": map[string]interface{}{ "content": config.IniConf.Section("weixin").Key("msg.survey").Value(), }, } return srv.WeixinService.Send(msg)*/ } // 退款 func (srv *OrderService) Refund(params map[string]string) (interface{}, error) { if _, ok := params["orderId"]; !ok { return nil, errors.New("miss param orderId") } orderItem, err := srv.IOrder.Get(params["orderId"]) if err != nil { return nil, err } if orderItem.Status < int(constants.ORDERSTATUS_UNRECEIVE) || orderItem.Status == int(constants.ORDERSTATUS_COMPLETE) { return nil, errors.New("the order can not refund") } payType := constants.PaymentType(orderItem.PayType.Int64) var ( rid string payNo string thirdRefundId string refundFee interface{} cashRefundFee interface{} rtype string ) switch payType { case constants.PAYMENTTYPE_WEIXIN: rel, err := srv.wxRefund(orderItem, params) if err != nil { return nil, err } rid = rel["out_refund_no"] payNo = rel["out_trade_no"] thirdRefundId = rel["refund_id"] refundFee = rel["refund_fee"] cashRefundFee = rel["cash_refund_fee"] rtype = "WX" case constants.PAYMENTTYPE_ALIPAY: rel, err := srv.aliRefund(orderItem, params) if err != nil { return nil, err } rid = rel["out_refund_no"] payNo = rel["out_trade_no"] thirdRefundId = rel["refund_id"] refundFee = rel["refund_fee"] cashRefundFee = rel["cash_refund_fee"] rtype = "ZFB" default: return nil, errors.New("UnSupport pay type(非微信支付无法退款)") } strsql := "insert into t_refund(id, pay_no, third_refund_id, refund_fee, cash_refund_fee, type) values(?,?,?,?,?,?);" db := util.GetWriteSqlDB() tx := db.MustBegin() sqlResult := tx.MustExec(strsql, rid, payNo, thirdRefundId, refundFee, cashRefundFee, rtype) if ra, _ := sqlResult.RowsAffected(); ra <= 0 { log.Println("failed to update refund fee") return nil, errors.New("failed to update refund fee") } strsql = "update t_order set refund = refund + ? where id = ?" sqlResult = tx.MustExec(strsql, refundFee, orderItem.Id) if ra, _ := sqlResult.RowsAffected(); ra <= 0 { tx.Tx.Rollback() log.Println("failed to update refund fee2") return nil, errors.New("failed to update refund fee2") } tx.Tx.Commit() return map[string]interface{}{ "refund_id": rid, "pay_no": payNo, "third_refund_id": thirdRefundId, "type": rtype, "refund_fee": refundFee, "cash_refund_fee": cashRefundFee, }, nil } // 微信退款 func (srv *OrderService) wxRefund(orderItem *entity.OrderDB, extra map[string]string) (map[string]string, error) { wxJsSdk := wx.NewJsSdk() weixin := wx.NewWeixin(wx.DefaultConfig["appid"], wx.DefaultConfig["secret"]) nonceStr := wxJsSdk.GenerateNoncestr(16) var refundFee interface{} = orderItem.ActualPayment.Int64 if _, ok := extra["refund_fee"]; ok { refundFee = extra["refund_fee"] } reqItem := map[string]string{ "appid": wx.DefaultConfig["appid"], "mch_id": wx.DefaultConfig["mch_id"], "nonce_str": nonceStr, "out_trade_no": orderItem.PayNo.String, "out_refund_no": GenerateOrderPayNO("R"), "total_fee": fmt.Sprintf("%d", orderItem.ActualPayment.Int64), "refund_fee": fmt.Sprintf("%v", refundFee), "op_user_id": wx.DefaultConfig["mch_id"], } if _, ok := extra["device_info"]; ok { reqItem["device_info"] = extra["device_info"] } sign := wxJsSdk.ComputePaySignature(reqItem, wx.DefaultConfig["apikey"]) reqItem["sign"] = sign rel, err := weixin.Refund(reqItem, wx.DefaultConfig["cert_file"], wx.DefaultConfig["key_file"]) if err != nil { return nil, err } if rel != nil && rel["return_code"] == "FAIL" { return nil, errors.New(rel["return_msg"]) } else { if rel["result_code"] == "FAIL" { return nil, errors.New(rel["err_code_des"]) } } //todo save refund record return rel, nil } // 支付宝退款 func (srv *OrderService) aliRefund(orderItem *entity.OrderDB, extra map[string]string) (map[string]string, error) { //var refundFee interface{} = orderItem.ActualPayment.Int64 refundFee := strconv.FormatFloat(float64(orderItem.ActualPayment.Int64)/100, 'f', -1, 64) if _, ok := extra["refund_fee"]; ok { payment, err := strconv.ParseFloat(extra["refund_fee"], 64) if err == nil { refundFee = strconv.FormatFloat(payment/100, 'f', -1, 64) } } client := ali.GetAliClient() req := alipay.TradeRefund{} req.OutTradeNo = orderItem.PayNo.String req.RefundAmount = refundFee req.OutRequestNo = GenerateOrderNo("R") res, err := client.TradeRefund(req) if err != nil { return nil, errors.New("1::alirefund err" + err.Error()) } if !res.IsSuccess() { return nil, errors.New("2::alirefund err" + res.Content.SubMsg) } refund_fee, _ := strconv.ParseFloat(res.Content.SendBackFee, 64) reqItem := map[string]string{ "out_trade_no": res.Content.OutTradeNo, "refund_id": res.Content.TradeNo, "out_refund_no": req.OutRequestNo, "cash_refund_fee": fmt.Sprintf("%d", orderItem.ActualPayment.Int64), "refund_fee": fmt.Sprintf("%v", refund_fee*100), } return reqItem, nil } // 微信支付回调 func (srv *OrderService) WxPayCallBack(payResult *wx.UnionPayResult) (interface{}, error) { return nil, nil } // 支付失败 func (srv *OrderService) FailedOrder(oid string) (bool, error) { //释放优惠券 db := util.GetWriteSqlDB() oitem, err := srv.IOrder.Get(oid) if err != nil { return false, err } if oitem.Status != 1 { return false, errors.New("该订单不能取消") } //20220124 联仁订单不解绑优惠券 if oitem.Source == "lianren" { return true, nil } citem, _ := dal.DefaultCouponDal.Get(0, oitem.Id) if citem == nil { return true, nil } WORK_FEE_REQUIRED, _ := config.IniConf.Section("server").Key("work_fee_required_bound").Int() WORK_FEE, _ := config.IniConf.Section("server").Key("work_fee").Int() if citem.TypeId == int(constants.COUPONTYPE_SERVICE) || (citem.BindProducts.Valid && strings.Trim(citem.BindProducts.String, " ") != "" && oitem.OnlinePayment > 0 && oitem.Payment < WORK_FEE_REQUIRED) { db.Exec("update t_order set work_fee = ? where id = ?", WORK_FEE, oid) } strSql := "update t_discount_ticket set order_id = null, status = 0 where order_id = ?;" _, err = db.Exec(strSql, oid) if err != nil { return false, err } return true, nil } func (srv *OrderService) CancelOrder(oid string) (bool, error) { orderDb, err := srv.IOrder.Get(oid) if err != nil { return false, err } //根据订单状态判断是否可以取消 status := constants.OrderStatus(orderDb.Status) if (status == constants.ORDERSTATUS_UNPAY || status == constants.ORDERSTATUS_UNRECEIVE) && (orderDb.Source == "web" || orderDb.Source == "h5") { //20210203如果来源是web 和h5 状态时 1:待支付,2:待接单 走取消流程(还有退款) } else if status != constants.ORDERSTATUS_UNPAY && orderDb.Source != "jd-jicai" { //其他来源,状态1 才可以取消 20210203 给前端开放取消功能,所以修改此条件 return false, errors.New("该订单不能取消") } if orderDb.Source == "jd-jicai" && orderDb.Status > 3 { return false, errors.New("该订单不能取消") } //todo 释放订单占用的产能并更改订单状态 db := util.GetWriteSqlDB() tx := db.MustBegin() if orderDb.Source == "jd-jicai" { result := tx.MustExec("update t_jd_productivity set remain_pnum = remain_pnum + 1 where addr_id = ? and pdate = ?;", orderDb.MaterialId, orderDb.VisitDate.String) if ra, _ := result.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return false, errors.New("更新产能失败") } } else { if orderDb.MaterialId > 0 { result := tx.MustExec("update t_producer_info set remain_num = remain_num + 1 where id = ? ", orderDb.MaterialId) if ra, _ := result.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return false, errors.New("更新产能失败") } } } result := tx.MustExec("update t_order set status = ?, m_id = 0 where id = ?", constants.ORDERSTATUS_CANCELED, orderDb.Id) if ra, _ := result.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return false, errors.New("更新订单状态失败") } //回滚库存 var ops = []entity.OrderProduct{} db.Select(&ops, "select t1.*, t2.stock_switch from t_order_product t1 left join t_product t2 on t1.product_id = t2.id where order_id = ?", oid) sql := "update t_product set stock = stock + ? where id = ?" if orderDb.Source == "sp_zfb" { sql = "update t_product set zfb_stock = zfb_stock + ? where id = ?" } for _, op := range ops { if op.StockSwitch == "ON" { result = tx.MustExec(sql, op.Quantity, op.ProductId) if ra, _ := result.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return false, errors.New("更新库存失败") } } } tx.Commit() go delUnpayFromRedis(oid) if orderDb.Source == "jd-jicai" && (orderDb.Status == 2 || orderDb.Status == 3) { rps := map[string]string{ "orderId": orderDb.Id, "refund_fee": fmt.Sprintf("%d", orderDb.ActualPayment.Int64), } log.Println("refund: ", orderDb.Id) _, err = srv.Refund(rps) if err != nil { log.Println(err) } } else if (orderDb.Source == "web" || orderDb.Source == "h5" || orderDb.Source == "sp" || orderDb.Source == "sp_zfb") && orderDb.Status == 2 { //20210203 如果来源是web h5 而且状态是2的,走退款流程 rps := map[string]string{ "orderId": orderDb.Id, "refund_fee": fmt.Sprintf("%d", orderDb.ActualPayment.Int64), } log.Println("refund: ", orderDb.Id) _, err = srv.Refund(rps) if err != nil { log.Println(err) } //优惠券复原 strSql := "update t_discount_ticket set order_id = null, status = 0 where order_id = ?;" _, err = db.Exec(strSql, oid) } else if orderDb.Source == "lianren" { //20220124 联仁单独解绑优惠券 strSql := "update t_discount_ticket set order_id = null, status = 0 where order_id = ?;" _, err = db.Exec(strSql, oid) } if util.SourceCheck(orderDb.Source) { go pushOrderStatus(oid, orderDb.Source, orderDb.Status) } return true, nil } func (srv *OrderService) DelOrder(oid string) (bool, error) { orderDb, err := srv.IOrder.Get(oid) if err != nil { return false, err } //根据订单状态判断是否可以删除 status := constants.OrderStatus(orderDb.Status) if status > constants.ORDERSTATUS_UNPAY && status < constants.ORDERSTATUS_COMPLETE { return false, errors.New("该订单不能删除") } //todo 释放订单占用的产能并更改订单状态 db := util.GetWriteSqlDB() tx := db.MustBegin() if status == constants.ORDERSTATUS_UNPAY && orderDb.MaterialId > 0 { result := tx.MustExec("update t_producer_info set remain_num = remain_num + 1 where id = ? ", orderDb.MaterialId) if ra, _ := result.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return false, errors.New("更新产能失败") } } //result := tx.MustExec("update t_order set status = ? where id = ?", constants.ORDERSTATUS_DELETED, orderDb.Id) result := tx.MustExec("update t_order set is_delete = ? where id = ?", "Y", orderDb.Id) if ra, _ := result.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return false, errors.New("删除订单失败") } tx.Commit() if status == constants.ORDERSTATUS_UNPAY { go delUnpayFromRedis(oid) } if util.SourceCheck(orderDb.Source) { go pushOrderStatus(oid, orderDb.Source, orderDb.Status) } return true, nil } func pushOrderStatus(orderId, source string, status int) { if util.SourceCheck(source) { client := util.GetRedis() client.Select(12) msg := map[string]interface{}{ "user": "system", "orderid": orderId, "source": source, "status": status, } buf, _ := json.Marshal(msg) client.Publish("order-status-change", string(buf)) } } // 获取订单列表 func (srv *OrderService) ListOrder(customId, pageIndex, pageSize, status int, mobile string, isZFB bool, isHis bool) (interface{}, error) { return srv.IOrder.List(customId, pageIndex, pageSize, status, mobile, isZFB, isHis) } // 根据custom_file_id获取订单列表 20211210 func (srv *OrderService) ListFileOrder(customId, pageIndex, pageSize, status int, mobile string, isZFB, isHis bool) (interface{}, error) { return srv.IOrder.ListCustomFile(customId, pageIndex, pageSize, status, mobile, isZFB, isHis) } // 获取订单详细信息 func (srv *OrderService) OrderDetail(oid string) (interface{}, error) { if oid == "" { return nil, errors.New("miss param order id") } return srv.IOrder.Detail(oid) } func (srv *OrderService) OrderReport(oid string, otype string) (interface{}, error) { if oid == "" { return nil, errors.New("miss param order id") } oid = strings.ToUpper(oid) var orderTableName = "t_order" if otype == "SPECIAL" { orderTableName = "t_special_order" } strSql := "select t1.id,original_report, report_picture,complete_time, mobile,custom_mobile, t1.name,gender,birthday,age,t1.type, is_view_report, share_code, blood_codes, source, group_concat(t2.product_id) as product_ids,group_concat(t3.name) as product_names,group_concat(t3.is_jiyin) as is_jiyins from " + orderTableName + " t1 left join t_order_product t2 on t1.id = t2.order_id left join t_product t3 on t2.product_id = t3.id where t1.id = ?" db := util.GetSqlDB() var data = &struct { Id string `db:"id"` Mobile string `db:"mobile"` CustomMobile string `db:"custom_mobile"` ReportPicture string `db:"report_picture"` IsViewReport string `db:"is_view_report"` CustomName string `db:"name"` Gender int `db:"gender"` Age int `db:"age"` Birthday string `db:"birthday"` ProductIds string `db:"product_ids"` ProductNames string `db:"product_names"` BloodCodes null.String `db:"blood_codes"` ShareCode null.String `db:"share_code"` ReportData interface{} `db:"-"` CompleteTime null.String `db:"complete_time"` Type string `db:"type"` IsJinyins string `db:"is_jiyins"` OriginalReport null.String `db:"original_report"` Source string `db:"source"` }{} err := db.Get(data, strSql, oid) if err != nil { return nil, err } if !data.ShareCode.Valid || strings.Trim(data.ShareCode.String, " ") == "" { code := util.GenerateCodeStr(8) strSql := fmt.Sprintf("update %s set share_code = ? where id = ?;", orderTableName) _, err = db.Exec(strSql, code, oid) if err != nil { return nil, err } data.ShareCode.SetValid(code) } if strings.Trim(data.ReportPicture, " ") != "" { return data, err } reportData, err := GetReportData(oid, otype) if err != nil { return nil, err } data.ReportData = reportData return data, nil } type ( reportDetectItem struct { DetectProductId string `json:"detect_product_id"` Id string `json:"id"` Name string `json:"name"` UpRefValue string `json:"up_refvalue"` RefValue string `json:"refvalue"` Final string `json:"final"` FinalStatus int8 `json:"final_status"` Unit string `json:"unit"` CheckTime string `json:"check_time"` IsShowRefvalue int8 `json:"is_show_refvalue"` Description string `json:"description"` FinalTip string `json:"final_tip"` Extra string `json:"extra"` } reportDetectProduct struct { Id string `json:"id"` ProductId string `json:"product_id"` Name string `json:"name"` SortNo string `json:"sort_no"` Items []*reportDetectItem `json:"items"` Comment string `json:"comment"` AnomalyCount uint `json:"anomaly_count"` Icon string `json:"icon"` IconSmall string `json:"icon_small"` } reportProduct struct { ProductId string `json:"product_id"` ProductName string `json:"product_name"` IsJiyin int `json:"is_jiyin"` Items []*reportDetectProduct `json:"items"` } detectItemResult struct { DetectItemId string `db:"detect_item_id"` Final string `db:"final"` FinalStatus int8 `db:"final_status"` RefValue string `db:"refValue"` Age string `db:"age"` Gender string `db:"gender"` Unit string `db:"unit"` CheckTime null.String `db:"checkTime"` Description null.String `db:"description"` FinalTip null.String `db:"final_tip"` Extra null.String `db:"extra"` } analysisItemResult struct { Title null.String `db:"title"` Analysis null.String `db:"analysis"` OrderNum int `db:"order_num"` } tempCItem struct { Comment null.String Icon null.String IconSmall null.String } ) func GetReportData(oid string, otype string) (interface{}, error) { oid = strings.ToUpper(oid) var ( strSql = "select product_schema from t_order_product_snapshot where order_id = ? limit 1" productSchema string db = util.GetSqlDB() productList = []reportProduct{} ) err := db.Get(&productSchema, strSql, oid) if err != nil { return nil, err } err = json.Unmarshal([]byte(productSchema), &productList) if err != nil { return nil, err } strSql = "select t1.*, t2.description from t_custom_detect_result t1 left join t_detect_item t2 on t1.detect_item_id = t2.id where order_no = ?" ditemResultList := []detectItemResult{} ditemIdxMap := map[string]int{} err = db.Select(&ditemResultList, strSql, oid) if err != nil { return nil, err } for idx, ditem := range ditemResultList { ditemIdxMap[ditem.DetectItemId] = idx } strSql = "select detect_product_id, result_comment, t2.icon, t2.icon_small from t_detect_product_comment t1 left join t_detect_product t2 on t1.detect_product_id = t2.id where order_no = ?" rows, err := db.Queryx(strSql, oid) if err != nil { return nil, err } var ( dpid string comment null.String comments = map[string]tempCItem{} icon null.String iconSmall null.String ) for rows.Next() { rows.Scan(&dpid, &comment, &icon, &iconSmall) comments[dpid] = tempCItem{ Comment: comment, Icon: icon, IconSmall: iconSmall, } } rows.Close() analysisInfo := struct { Analysis null.String `db:"analysis" json:"analysis"` CustomName string `db:"name" json:"custom_name"` Mobile string `db:"mobile" json:"mobile"` Age string `db:"-" json:"age"` Gender string `db:"-" json:"gender"` Summarize null.String `db:"summarize" json:"summarize"` ReportItems string `db:"-" json:"report_items"` AnalysisList interface{} `db:"-" json:"analysis_desc"` }{} var orderTableName = "t_order" if otype == "SPECIAL" { orderTableName = "t_special_order" } strSql = "select analysis, mobile, name, summarize from " + orderTableName + " t1 left join t_report_analysis t2 on t1.id = t2.order_id left join t_report_summarize t3 on t1.id = t3.order_id where t1.id = ?" err = db.Get(&analysisInfo, strSql, oid) if err != nil { return nil, err } strSql = "select * from t_report_analysis_desc where order_id = ? order by order_num asc;" var alist = []analysisItemResult{} err = db.Select(&alist, strSql, oid) if err != nil { return nil, err } analysisInfo.AnalysisList = alist var ritems string db.Get(&ritems, "select item from t_report_item where order_id = ?", oid) analysisInfo.ReportItems = ritems for _, pitem := range productList { for _, dpItem := range pitem.Items { if _, ok := comments[dpItem.Id]; ok { dpItem.Comment = comments[dpItem.Id].Comment.String dpItem.Icon = comments[dpItem.Id].Icon.String dpItem.IconSmall = comments[dpItem.Id].IconSmall.String } for _, ditem := range dpItem.Items { if idx, ok := ditemIdxMap[ditem.Id]; ok { ditem.RefValue = ditemResultList[idx].RefValue ditem.FinalStatus = ditemResultList[idx].FinalStatus ditem.Final = ditemResultList[idx].Final ditem.Unit = ditemResultList[idx].Unit ditem.CheckTime = ditemResultList[idx].CheckTime.String ditem.Description = ditemResultList[idx].Description.String ditem.FinalTip = ditemResultList[idx].FinalTip.String if analysisInfo.Age == "" { analysisInfo.Age = ditemResultList[idx].Age } if analysisInfo.Gender == "" { analysisInfo.Gender = ditemResultList[idx].Gender } ditem.Extra = ditemResultList[idx].Extra.String } if ditem.FinalStatus == 1 || ditem.FinalStatus == 2 || ditem.FinalStatus == -1 || ditem.FinalStatus == 3 { dpItem.AnomalyCount++ } } } } return map[string]interface{}{ "base_info": analysisInfo, "product_list": productList, }, nil } func (srv *OrderService) OrderUpdate(oitem *entity.Order, upType string) (interface{}, error) { if oitem.Id == "" { return nil, errors.New("miss param order id") } var upItem *entity.Order switch upType { case "1": upItem = &entity.Order{ Id: oitem.Id, IsViewReport: oitem.IsViewReport, } case "2": strSql := "select blood_codes from t_order where blood_codes = ? and mobile <> ? limit 1;" db := util.GetSqlDB() var bcodes string err := db.Get(&bcodes, strSql, oitem.BloodCodes, oitem.Mobile) if err != nil && err != sql.ErrNoRows { return nil, err } if err == nil { return nil, errors.New("1:: blood code was already used") } var oids = strings.Split(oitem.Id, ",") var strOids string for _, oid := range oids { strOids += "'" + oid + "'," } strOids = strOids[0 : len(strOids)-1] db.Exec("update t_order set blood_codes = ? where id in("+strOids+");", oitem.BloodCodes) return true, nil case "4": //qz 20210105 修改 db := util.GetSqlDB() if len(oitem.Remark) > 0 { _, err := db.Exec("update t_order set remark = ? where id =? and custom_id = ? ", oitem.Remark, oitem.Id, oitem.CustomId) if err != nil { fmt.Println(err.Error()) return false, errors.New("4::update t_order remark error") } return true, nil } else { flag, err := UpdateOrderCase4(oitem) if err != nil || !flag { fmt.Println(err.Error()) return false, err } return true, nil } default: } if upItem == nil { return nil, nil } return srv.IOrder.Update(upItem) } // 20210204 修改 orderUpdate case 4 的逻辑 只有remark 单独处理,其他的提取出来 func UpdateOrderCase4(oitem *entity.Order) (bool, error) { db := util.GetSqlDB() tx := db.MustBegin() //获取原始订单,主要需要visit_date visit_time_range m_id old, err := dal.DefaultOrderDal.Get(oitem.Id) if err != nil { return false, fmt.Errorf("7::查询订单失败%s", oitem.Id) } //todo 判断新旧日期,判断不同就需要查询产能,并且归还产能 if oitem.VisitTimeRange == old.VisitTimeRange.String && oitem.VisitDate == old.VisitDate.String { //如果新旧日期相同, 就不修改产能 } else { //todo 更新产能 //todo 1 查询新产能 strSql := `SELECT t1.num, t2.remain_num, t2.pdate, t1.id AS gid, t2.id AS infoid FROM t_producer_config t1 LEFT JOIN t_producer_info t2 ON t1.id = t2.global_id AND t1.id = ? AND t2.pdate = ? AND t2.time_range = ?` var producerInfo = struct { GNum int `db:"num"` RemainNum sql.NullInt64 `db:"remain_num"` PDate sql.NullString `db:"pdate"` GId int `db:"gid"` InfoId sql.NullInt64 `db:"infoid"` }{} var pinfoSqlResult sql.Result err := db.Get(&producerInfo, strSql, oitem.PTId, oitem.VisitDate, oitem.VisitTimeRange) if err != nil { return false, errors.New("1::该城市暂未开通服务") } visitDate, err := time.Parse("2006-01-02", oitem.VisitDate) if err != nil { return false, errors.New("2::错误的上门日期") } //当天下单最早只能约第二天上门,240220 houyf改为20点 if visitDate.Before(time.Now()) || (time.Now().Hour() >= 20 && time.Now().Truncate(time.Hour*24).Add(time.Hour*48).After(visitDate)) { return false, errors.New("3::失效的上门日期") } if producerInfo.PDate.String == oitem.VisitDate && producerInfo.RemainNum.Int64 <= 0 { return false, errors.New("4::产能不足") } if !producerInfo.PDate.Valid { pinfoSqlResult = tx.MustExec("insert into t_producer_info(global_id, pdate, time_range, num, remain_num) values(?, ?, ?, ?, ?)", producerInfo.GId, visitDate, oitem.VisitTimeRange, producerInfo.GNum, producerInfo.GNum-1) lid, err := pinfoSqlResult.LastInsertId() if err != nil { tx.Tx.Rollback() return false, err } oitem.MaterialId = int(lid) } else { pinfoSqlResult = tx.MustExec("update t_producer_info set remain_num = ? where id = ? and remain_num = ?", producerInfo.RemainNum.Int64-1, producerInfo.InfoId, producerInfo.RemainNum.Int64) oitem.MaterialId = int(producerInfo.InfoId.Int64) } if ra, _ := pinfoSqlResult.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return false, errors.New("5::更新产能失败") } //归还产能 if old.MaterialId > 0 && (old.Source == "web" || old.Source == "h5") { result := tx.MustExec("update t_producer_info set remain_num = remain_num + 1 where id = ? ", old.MaterialId) if ra, _ := result.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return false, errors.New("6::更新产能失败") } } } sqlStr, kvm := util.GenerateUpdateSqlFromStruct("t_order", oitem, " where id='"+oitem.Id+"'") _, err = tx.NamedExec(sqlStr, kvm) if err != nil { tx.Tx.Rollback() return false, err } tx.Commit() if util.SourceCheck(old.Source) { client := util.GetRedis() client.Select(12) msg := map[string]interface{}{ "user": "system", "orderid": old.Id, "source": old.Source, "status": old.Status, } buf, _ := json.Marshal(msg) client.Publish("order-status-change", string(buf)) } return true, nil } func (srv *OrderService) CommitSurvey(osItem *entity.OrderSurvey, userId int) (interface{}, error) { oitem, err := srv.IOrder.Get(osItem.OrderId) if oitem == nil { return nil, errors.New("invalid order id") } if err != nil { return nil, err } /*if oitem.CustomId != userId { log.Println(oitem.CustomId, ":", userId) return nil, errors.New("forbidden") }*/ success, err := srv.IOrder.CommitSurvey(osItem) if err != nil { return false, err } if osItem.ServiceScore == "200" { return success, nil } if success { //add coupon user account related to the order couponItem := &entity.DiscountTicket{ TypeId: int(constants.COUPONTYPE_NORMAL), Mobile: oitem.Mobile, Value: 1000, Deadline: time.Now().Add(time.Hour * 24 * 365).Format("2006-01-02 15:04:05"), OrderId: oitem.Id, CreatedAt: time.Now().Format("2006-01-02 15:04:05"), } _, err = dal.DefaultCouponDal.Save(couponItem) if err != nil { return false, err } return true, nil } return success, err } func (srv *OrderService) SaveCallbackInfo(info entity.OrderCallbackInfo) (interface{}, error) { db := util.GetWriteSqlDB() var c int db.Get(&c, "select count(*) from t_order where id = ?", info.OrderId) if c <= 0 { return nil, errors.New("1::order not found") } c = 0 db.Get(&c, "select count(*) from t_order_abnormal where order_id = ?", info.OrderId) if c > 0 { return nil, errors.New("2::record already exists") } reviewDate, err := time.Parse("2006-01-02", info.VisitEndtime) if err != nil { return nil, errors.New("4::wrong visit end time") } info.ReviewDate = reviewDate.AddDate(0, 0, 60).Format("2006-01-02") strSql, mkv := util.GenerateInsertSqlFromStruct("t_order_abnormal", &info) _, err = db.NamedExec(strSql, mkv) if err != nil { return nil, errors.New("3::" + err.Error()) } return true, nil } func (srv *OrderService) GetCallbackInfo(orderId string) (interface{}, error) { var info = struct { Mobile string `json:"mobile" db:"mobile"` CustomMobile string `json:"custom_mobile" db:"custom_mobile"` OrderId string `json:"order_id" db:"id"` }{} db := util.GetSqlDB() err := db.Get(&info, "select mobile, custom_mobile, t2.id from t_order_abnormal t1 left join t_order t2 on t1.order_id = t2.id where t1.order_id = ? and is_own_date = 1 limit 1", orderId) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } return info, nil } // 根据血检条码获取订单信息 func (srv *OrderService) GetOrderInfoByCode(code string) (interface{}, error) { return srv.IOrder.GetByCode(code) } func (srv *OrderService) GetOrderInfoByNurse(orderId string, deliverUserId int) (interface{}, error) { db := util.GetSqlDB() var oid string db.Get(&oid, "select order_id from t_order_deliver_user where order_id = ? and deliver_user_id =?;", orderId, deliverUserId) if oid == "" { return nil, errors.New("forbidden") } return srv.OrderDetail(orderId) } func ComputeProductPrice(ids []int, quantityMap map[int]int, isPersonal bool) (int, int, error) { if len(ids) == 0 { return 0, 0, nil } inStr := "" for _, pid := range ids { inStr += fmt.Sprintf("%d,", pid) } inStr = inStr[0 : len(inStr)-1] db := util.GetSqlDB() var price int var strSql string var repeatFee int if isPersonal { strSql = "select sum(market_price) as price from t_detect_item where id in(" + inStr + ")" } else { strSql = `select t3.price, count(t3.id) as count from t_product_detect_product t1 left join t_detect_product_item t2 on t1.detect_product_id = t2.detect_product_id left join t_detect_item t3 on t2.detect_item_id = t3.id where t1.product_id in (` + inStr + `) group by t3.id having (count > 1);` rows, err := db.Queryx(strSql) if err != nil { return price, repeatFee, err } defer rows.Close() var ( mp int count int ) for rows.Next() { rows.Scan(&mp, &count) repeatFee += mp * (count - 1) } rows.Close() strSql = `select id, price from t_product where id in(` + inStr + ")" var ( productId int pprice int ) rows, _ = db.Queryx(strSql) for rows.Next() { rows.Scan(&productId, &pprice) price += pprice * quantityMap[productId] } } return price, repeatFee, nil } // 20221009 通过护士id 获取 产品 折扣价格 func ComputeProductPriceByDeliverID(ids []int, quantityMap map[int]int, deliverid float64) (int, int, error) { if len(ids) == 0 { return 0, 0, nil } inStr := "" for _, pid := range ids { inStr += fmt.Sprintf("%d,", pid) } inStr = inStr[0 : len(inStr)-1] db := util.GetSqlDB() var price int var strSql string var repeatFee int strSql = `select t3.price, count(t3.id) as count from t_product_detect_product t1 left join t_detect_product_item t2 on t1.detect_product_id = t2.detect_product_id left join t_detect_item t3 on t2.detect_item_id = t3.id where t1.product_id in (` + inStr + `) group by t3.id having (count > 1);` rows, err := db.Queryx(strSql) if err != nil { return price, repeatFee, err } defer rows.Close() var ( mp int count int ) for rows.Next() { rows.Scan(&mp, &count) repeatFee += mp * (count - 1) } rows.Close() strSql = `select p.id, p.price,case when d.discount_price is null then 0 else d.discount_price end as discount_price from t_product p LEFT JOIN t_product_question_deliver d on p.id = d.product_id and d.deliver_user_id = ? and d.is_delete = 0 where p.id in (` + inStr + ")" var ( productId int pprice int discount int ) rows, _ = db.Queryx(strSql, deliverid) for rows.Next() { rows.Scan(&productId, &pprice, &discount) if discount > 0 { price += discount * quantityMap[productId] } else { price += pprice * quantityMap[productId] } } return price, repeatFee, nil } func ComputeProductPrice_ZFB(ids []int, quantityMap map[int]int) (int, int, error) { if len(ids) == 0 { return 0, 0, nil } inStr := "" for _, pid := range ids { inStr += fmt.Sprintf("%d,", pid) } inStr = inStr[0 : len(inStr)-1] db := util.GetSqlDB() var price int var strSql string var repeatFee int strSql = `select t3.price, count(t3.id) as count from t_product_detect_product t1 left join t_detect_product_item t2 on t1.detect_product_id = t2.detect_product_id left join t_detect_item t3 on t2.detect_item_id = t3.id where t1.product_id in (` + inStr + `) group by t3.id having (count > 1);` rows, err := db.Queryx(strSql) if err != nil { return price, repeatFee, err } defer rows.Close() var ( mp int count int ) for rows.Next() { rows.Scan(&mp, &count) repeatFee += mp * (count - 1) } rows.Close() strSql = `select id, zfb_price from t_product where id in(` + inStr + ")" var ( productId int pprice int ) rows, _ = db.Queryx(strSql) for rows.Next() { rows.Scan(&productId, &pprice) price += pprice * quantityMap[productId] } return price, repeatFee, nil } var orderNoMutex = &sync.Mutex{} func GenerateOrderNo(prefix string) string { prefix = strings.ToUpper(prefix) timeStr := time.Now().Format("060102150405") orderNoMutex.Lock() defer orderNoMutex.Unlock() timestamp := time.Now().Unix() rand.Seed(timestamp + int64(time.Now().Nanosecond())) var rnum = rand.Intn(100000) strNum := fmt.Sprintf("0000%d", rnum) strNum = strNum[len(strNum)-5:] return fmt.Sprintf("%s%s%s", prefix, timeStr, strNum) } func GenerateOrderPayNO(prefix string) string { timeStr := time.Now().Format("20060102150405") timestamp := time.Now().Unix() + int64(time.Now().Nanosecond()) rand.Seed(timestamp) var rnum = rand.Intn(100000) strNum := fmt.Sprintf("00000%d", rnum) strNum = strNum[len(strNum)-5:] return prefix + timeStr + strNum } // qz 20200828 特定产品在交钱完成之后,拆分 func (srv *OrderService) SplitOrder(orderId string, productId int) { //20220620 取消 return /* db := util.GetWriteSqlDB() //查询拆单关系 tpsInfoList := []struct { ProductId int `db:"product_id" ` ChildId int `db:"child_id"` IsMain int `db:"is_main"` CreateAt string `db:"create_at"` Remark null.String `db:"remark"` Name null.String `db:"name"` Price null.String `db:"price"` }{} sqlSearch := "select t1.*,t2.name as name,t2.price as price from t_product_split t1 left join t_product t2 on t1.child_id = t2.id where t1.product_id =? order by t1.is_main desc" err := db.Select(&tpsInfoList, sqlSearch, productId) if err != nil || len(tpsInfoList) <= 0 { log.Println(fmt.Sprintf("订单%s,产品编号为%d 不需要拆单", orderId, productId)) return } //查询订单, orderDb, err := srv.IOrder.Get(orderId) fmt.Println(orderDb) //查询订单 产品关联 plist := struct { OrderId string `db:"order_id"` ProductId string `db:"product_id"` ProductName string `db:"product_name"` Price int `db:"price"` Picture string `db:"picture"` Quantity int `db:"quantity"` IsPersonal int `db:"is_personal"` }{} strSql := "select * from t_order_product where order_id = ? and product_id = ?" err = db.Get(&plist, strSql, orderId, productId) //查询订单 扩展 //elist :=struct { // OrderID string `db:"order_id"` // NeedEmtiness int `db:"need_emptiness"` // ReportPeriod string `db:"report_period"` // PressurePipe string `db:"pressure_pipe"` // BloodAddress string `db:"blood_address"` // Notice string `db:"notice"` // UpdatedAt time.Time `db:"updated_at"` // Remark1 string `db:"remark1"` // CancelReason int `db:"cancel_reason"` // IsNotice string `db:"is_notice"` // RelationShip string `db:"relationship"` // IsYunXue int `db:"is_yunxue"` // IsDfgoumin int `db:"is_dfguomin"` // BloodTestId string `db:"bloodtest_id"` // ReportResult string `db:"reportresult"` // PayNurse int `db:"paynurse"` // IsAddpor string `db:"isaddpro"` // Sample string `db:"sample"` // JiCaiName string `db:"jicai_name"` // Express string `db:"express"` // ExpressNumber string `db:"expressnumber"` // ExpressStatus string `db:"expressstatus"` // ExpressInfo string `db:"expressinfo"` // ExpressUser string `db:"express_user"` // ExpressnumberUser string `db:"expressnumber_user"` //}{} //strSql = "select * from t_order_extra where order_id = ?" //err = db.Select(&elist, strSql, orderId) //拆分 order tx := db.MustBegin() for i := 0; i < len(tpsInfoList); i++ { if tpsInfoList[i].IsMain == 1 { //order 不用更新 //更新order_product result := tx.MustExec("update t_order_product set product_id = ?,product_name = ?,price=? where order_id = ? and product_id = ?", tpsInfoList[i].ChildId, tpsInfoList[i].Name, tpsInfoList[i].Price, orderId, productId) if ra, _ := result.RowsAffected(); ra <= 0 { tx.Tx.Rollback() log.Println(fmt.Sprintf("订单%s,产品编号为%d 拆单失败 更新 t_order_product ", orderId, productId)) return } //order_extra 不用更新 } else { //生成订单号 orderNewId := GenerateOrderNoByOld(orderId, i) fmt.Println(orderNewId) //插入 t_order orderDb.Id = orderNewId orderDb.Payment = 0 orderDb.ServiceRemark = null.StringFrom(fmt.Sprintf("由%s拆单", orderId)) strSql, mkv := util.GenerateInsertSqlFromStruct("t_order", orderDb) osqlResult, err := tx.NamedExec(strSql, mkv) if err != nil { tx.Tx.Rollback() log.Println(fmt.Sprintf("订单%s,产品编号为%d 拆单失败 插入 t_order %s", orderId, productId, orderNewId)) return } if ra, _ := osqlResult.RowsAffected(); ra <= 0 { tx.Tx.Rollback() log.Println(fmt.Sprintf("订单%s,产品编号为%d 拆单失败 插入 t_order %s", orderId, productId, orderNewId)) return } //todo 查询product //插入 t_order_product 注意 金额 var fields = map[string]interface{}{ "order_id": orderNewId, "product_id": tpsInfoList[i].ChildId, "product_name": tpsInfoList[i].Name, "price": tpsInfoList[i].Price, "picture": plist.Picture, "quantity": plist.Quantity, "is_personal": plist.IsPersonal, } strSql = util.GenerateInsertSql("t_order_product", fields) _, err = tx.NamedExec(strSql, fields) if err != nil { tx.Tx.Rollback() log.Println(fmt.Sprintf("订单%s,产品编号为%d 拆单失败,新建 t_order_product %s", orderId, productId, orderNewId)) return } //复制 t_order_extra result := tx.MustExec(fmt.Sprintf("insert into t_order_extra(`order_id`, `need_emptiness`, `report_period`, `pressure_pipe`, `blood_address`, `notice`, `updated_at`, `remark1`, `cancel_reason`, `is_notice`, `relationship`, `is_yunxue`, `is_dfguomin`, `bloodtest_id`, `reportresult`, `paynurse`, `isaddpro`, `sample`, `jicai_name`, `express`, `expressnumber`, `expressstatus`, `expressinfo`, `express_user`, `expressnumber_user`) select '%s',`need_emptiness`, `report_period`, `pressure_pipe`, `blood_address`, `notice`, `updated_at`, `remark1`, `cancel_reason`, `is_notice`, `relationship`, `is_yunxue`, `is_dfguomin`, `bloodtest_id`, `reportresult`, `paynurse`, `isaddpro`, `sample`, `jicai_name`, `express`, `expressnumber`, `expressstatus`, `expressinfo`, `express_user`, `expressnumber_user` from t_order_extra where order_id =?", orderNewId), orderId) if ra, _ := result.RowsAffected(); ra <= 0 { tx.Tx.Rollback() log.Println(fmt.Sprintf("订单%s,产品编号为%d 拆单失败,新建 t_order_extra %s", orderId, productId, orderNewId)) return } } } tx.Commit() */ } func (srv *OrderService) SendDingMsg(orderId string) { util.SendOrderMsg(fmt.Sprintf("加急核酸来订单了,订单来源xn ,订单编号%s", orderId)) } //20210119 添加 产品1149 两人核酸,下单后拆单 20220620 删除 // 20210607 金睡莲活动,支付成功后更新状态码 func (srv *OrderService) JinShuiLianServer(id string) { //t_custom_wxscan_pay remark 保存了 t_activity_info 的 id ;t_activity_info address 保存 fcode 还需要mobile 发送短信 db := util.GetWriteSqlDB() aInfo := struct { Mobile null.String `db:"mobile"` FCode string `db:"address"` }{} db.Get(&aInfo, "select mobile,address from t_activity_info where id = ?", id) pinfoSqlResult := db.MustExec("update t_fcode set already_use_num = already_use_num +1 where code_str = ? and use_num>= already_use_num+1 ", aInfo.FCode) if ra, _ := pinfoSqlResult.RowsAffected(); ra <= 0 { fmt.Println("fcode update error") } sms := &SMSService{dal.DefaultSMSCodeDal} if aInfo.Mobile.Valid { sms.SendSMS(aInfo.Mobile.ValueOrZero(), 24, nil) } } // 20211127 联仁根据门店id 修改定价 func lianRenPrice(list []*entity.ProductDB, shopId int) { db := util.GetSqlDB() var price int db.Get(&price, "select price from t_shop_lianren where id = ? ", shopId) if price > 0 { for i := 0; i < len(list); i++ { list[i].Price = price } } } // 20230830 拆分历史表 // 提取 addorder addorderFcode addorderFcodeYuanyi addGenorder 的库存检查 func Stocklimit(mobile string, productIds []int, productQuantityMap map[int]int, isZFB bool) ([]*entity.ProductDB, error) { plist := []*entity.ProductDB{} s_source_zfb := "" if isZFB { s_source_zfb = " and t2.source='sp_zfb' " } db := util.GetSqlDB() sqlTemp := "select t1.*, sum(t4.quantity) as bought_num from (select * from t_product where id in(" + util.IntJoin(productIds, ",") + ")) t1 left join (select t3.quantity, t3.product_id from t_order t2 left join t_order_product t3 on t2.id = t3.order_id where custom_mobile = ? and t2.status not in (7,9,14) and t2.retype in ('100','110') " + s_source_zfb + " ) t4 on t1.id = t4.product_id GROUP BY t1.id order by t1.is_no_booktime desc" db.Select(&plist, sqlTemp, mobile) if len(plist) != len(productIds) { return nil, errors.New("6::wrong param productids") } if isZFB { entity.ZFBWash(plist) } for _, pitem := range plist { if pitem.StockSwitch == "ON" && pitem.Stock < productQuantityMap[pitem.Id] { return nil, errors.New("10::out of stock") } if pitem.PstockSwitch == "ON" && int(pitem.BoughtNum.Int64)+productQuantityMap[pitem.Id] > pitem.Pstock { return nil, errors.New(fmt.Sprintf("11::upper limit of product %d", pitem.Id)) } } return plist, nil } // 20240221 查询有效订单表 func (*OrderService) OrderVaildCount(customId, status int, mobile string) (interface{}, error) { strSql := "select count(distinct t2.id) from (select id, mobile from t_custom where id = ?) t1, t_order t2 " strSql += " where (t1.id = t2.custom_id or t1.mobile = t2.mobile) and status not in (7,9,14) " whereValue := []interface{}{ customId, } //if status > 0 { // whereStr += " " // whereValue = append(whereValue, status) //} else if status == -1 { // whereStr += " and status in(6,11) " //} else { // //whereStr += " and status <> 8 " // whereStr += " and is_delete='N'" //} if mobile != "" { strSql += " and t2.mobile = ? " whereValue = append(whereValue, mobile) } strSql += " and t2.retype in ('100','110')" db := util.GetSqlDB() var count1, count2 int err := db.Get(&count1, strSql, whereValue...) if err != nil { return 0, err } err = db.Get(&count2, util.ChangeOrderTableName(strSql), whereValue...) if err != nil { return 0, err } return count1 + count2, nil }