automaticPayment.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. package timedTask
  2. import (
  3. "app.yhyue.com/moapp/jybase/redis"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "github.com/gogf/gf/v2/database/gdb"
  8. "github.com/gogf/gf/v2/errors/gerror"
  9. "github.com/gogf/gf/v2/frame/g"
  10. "github.com/gogf/gf/v2/util/gconv"
  11. "jyOrderManager/internal/consts"
  12. "jyOrderManager/internal/jyutil"
  13. "jyOrderManager/internal/logic/order"
  14. "jyOrderManager/internal/logic/product"
  15. "log"
  16. "math"
  17. "net/rpc"
  18. "regexp"
  19. "strings"
  20. "time"
  21. )
  22. var (
  23. reg = regexp.MustCompile("[\\s()]+")
  24. )
  25. type FilterStr struct {
  26. PaybackNum int
  27. paybackListArr []map[string]interface{}
  28. }
  29. func AutomaticPayment() {
  30. var ctx = context.Background()
  31. log.Println("自动回款匹配。。。开始")
  32. query := `select * from cadmin.transaction where ISRELATION = 0 and OTHNAM is not null and OTHNAM != "" order by id`
  33. data, _ := g.DB().Query(ctx, query)
  34. if !data.IsEmpty() {
  35. returnOrderMap := make(map[string]int)
  36. orderData, _ := g.DB().Query(ctx, `SELECT
  37. a.*,
  38. b.paybackNum,
  39. b.paybackList
  40. FROM
  41. dataexport_order a
  42. LEFT JOIN (
  43. SELECT
  44. order_code,
  45. JSON_EXTRACT( plantList, "$.paybackNum" ) AS paybackNum,
  46. JSON_EXTRACT( plantList, "$.paybackList" ) AS paybackList
  47. FROM
  48. return_money_plant
  49. WHERE
  50. state != -1 and JSON_VALID( plantList )
  51. ) b ON a.order_code = b.order_code
  52. WHERE
  53. ( a.return_status = 0 OR a.return_status = 2 )
  54. AND a.audit_status = 3
  55. AND ( a.order_status = 1 OR a.order_status = 0 )`)
  56. returnOrderData, _ := g.DB().Query(ctx, `SELECT order_code, COUNT(*) as total_count FROM return_money_record WHERE state =1 GROUP BY order_code `)
  57. if !returnOrderData.IsEmpty() {
  58. for _, i2 := range returnOrderData.List() {
  59. returnOrderMap[gconv.String(i2["order_code"])] = gconv.Int(i2["total_count"])
  60. }
  61. }
  62. filterMap := make(map[string]FilterStr)
  63. if !orderData.IsEmpty() { //优化 获取filter字段中的数据
  64. for _, o := range orderData.List() {
  65. var paybackList string
  66. if pList := gconv.String(o["paybackList"]); pList != "" && pList != `""` && strings.Contains(pList, "合计") {
  67. paybackList = strings.ReplaceAll(pList[1:len(pList)-1], "\\", "")
  68. }
  69. var paybackListArr []map[string]interface{}
  70. if paybackList != "" {
  71. _ = json.Unmarshal([]byte(paybackList), &paybackListArr)
  72. }
  73. paybackNum := strings.ReplaceAll(gconv.String(o["paybackNum"]), `"`, "")
  74. filterMap[gconv.String(o["order_code"])] = FilterStr{
  75. gconv.Int(paybackNum),
  76. paybackListArr,
  77. }
  78. }
  79. }
  80. for _, v := range data.List() {
  81. returnMoney := int(math.Floor(float64(float64(gconv.Float64(v["TRSBAL"]))*100) + 0.5))
  82. companyName := gconv.String(v["OTHNAM"])
  83. remark := gconv.String(v["TXTDSM"])
  84. remarks := gconv.String(v["NUSAGE"])
  85. id := gconv.Int(v["id"])
  86. returnTime := gconv.String(v["BNKTIM"])
  87. BNKFLW := gconv.String(v["BNKFLW"])
  88. BNKNAM := gconv.String(v["BNKNAM"])
  89. if !orderData.IsEmpty() {
  90. for _, o := range orderData.List() {
  91. //filterMap := make(map[string]interface{})
  92. //_ = json.Unmarshal([]byte(gconv.String(o["filter"])), &filterMap)
  93. money := gconv.Int(o["pay_money"]) - gconv.Int(o["commission"]) - gconv.Int(o["procedures_money"])
  94. paymentUser := gconv.String(o["payment_user"])
  95. if paymentUser == "" {
  96. paymentUser = gconv.String(o["company_name"])
  97. }
  98. orderCode := gconv.String(o["order_code"])
  99. if PaymentPlanMatching(money, returnMoney, paymentUser, companyName, remark, remarks, orderCode, filterMap[orderCode].PaybackNum, filterMap[orderCode].paybackListArr, returnOrderMap) {
  100. isExists, err := redis.Exists("qmx_filter", "qmx_auto_return_"+fmt.Sprint(id))
  101. if !isExists || err != nil {
  102. log.Println("自动回款匹配成功", id, orderCode)
  103. var returned_money int
  104. if calculation, _ := g.DB().GetOne(ctx, fmt.Sprintf("SELECT SUM(return_money) as returned_money FROM return_money_record WHERE order_code=%s AND state=1", orderCode)); !calculation.IsEmpty() {
  105. returned_money = gconv.Int(calculation.Map()["returned_money"])
  106. }
  107. //isFirstReturn := config.JysqlDB.Count("return_money_record", map[string]interface{}{"state": 1, "order_code": orderCode}) == 0
  108. returnReq, _ := g.DB().Insert(ctx, "return_money_record", map[string]interface{}{
  109. "order_code": orderCode,
  110. "return_time": returnTime,
  111. "return_money": returnMoney,
  112. "return_type": 3,
  113. "return_invoice_status": 0,
  114. "operate_time": time.Now().Format("2006-01-02 15:04:05"),
  115. "flow_money": returnMoney,
  116. "bank_name": BNKNAM,
  117. "bank_flow": BNKFLW,
  118. "operate_type": 2,
  119. "state": 1,
  120. "pay_account_name": paymentUser,
  121. })
  122. returnId, _ := returnReq.LastInsertId()
  123. //计算已回款金额
  124. if returned_money == 0 {
  125. if err := order.CommonChange(ctx, orderCode, returnTime, order.ReturnMoney); err != nil {
  126. log.Printf("%s 回款销售业绩生效异常 %v", orderCode, err)
  127. continue
  128. }
  129. }
  130. updateData := map[string]interface{}{"return_status": 2, "order_status": 1}
  131. if returned_money+returnMoney == money {
  132. redis.Put("qmx_filter", "qmx_auto_return_"+fmt.Sprint(id), 1, 3600)
  133. updateData["return_status"] = 1
  134. }
  135. if returnId > 0 {
  136. log.Println("自动回款创建回款记录成功", id, orderCode)
  137. g.DB().Update(ctx, "dataexport_order", updateData, map[string]interface{}{"order_code": orderCode})
  138. g.DB().Update(ctx, "cadmin.transaction", map[string]interface{}{"ISRELATION": 1, "return_id": fmt.Sprint(returnId)}, map[string]interface{}{"id": id})
  139. log.Println("自动回款开通权益", orderCode, gconv.Int(o["return_status"]), updateData["return_status"], returned_money, returnMoney, money)
  140. if gconv.Int(o["return_status"]) != 1 && updateData["return_status"] == 1 {
  141. productDetail, err := g.DB().Ctx(ctx).Query(ctx, fmt.Sprintf(`SELECT * FROM jy_order_detail WHERE order_code ='%s' and returned_open =1 and is_service_open = 0 and status =1`, orderCode))
  142. if err != nil || productDetail.IsEmpty() {
  143. continue
  144. }
  145. //全额回款开通权益
  146. if !consts.PhoneRegex.MatchString(gconv.String(o["user_phone"])) {
  147. continue
  148. }
  149. uData, entId, userPositionId, err := jyutil.GetCreateUserData(gconv.String(o["user_phone"]), gconv.String(o["company_name"]), gconv.Int(o["buy_subject"]) == 2)
  150. if err != nil {
  151. continue
  152. }
  153. if err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
  154. // 产品服务开通
  155. for _, m := range productDetail.List() {
  156. if !jyutil.IsServiceOpen(m) {
  157. continue
  158. }
  159. //参数注入
  160. m["userMap"] = map[string]interface{}{
  161. "userData": uData, "entId": entId, "userPositionId": userPositionId,
  162. }
  163. m["phone"] = o["user_phone"]
  164. m["order_code"] = orderCode
  165. m["amount"] = m["final_price"]
  166. m["reqCompanyName"] = o["company_name"]
  167. m["reqSubject"] = o["buy_subject"]
  168. productCode := gconv.String(m["product_code"])
  169. pFunc, err := product.JyProFunc.GetProductInitFuncByCode(productCode)
  170. if err != nil {
  171. continue
  172. }
  173. pObj, err := pFunc(m)
  174. if err != nil {
  175. gerror.Wrap(err, fmt.Sprintf("获取%s商品异常", productCode))
  176. continue
  177. }
  178. if err = pObj.OpenService(ctx, time.Now()); err != nil {
  179. continue
  180. }
  181. }
  182. if orderUserId := gconv.String(o["user_id"]); orderUserId == "" || orderUserId != gconv.String(uData["userId"]) {
  183. log.Printf("同步更新订单用户身份:orderUserId:%s,userId:%s,entId:%d\n", orderUserId, uData["userId"], entId)
  184. upData := g.Map{
  185. "user_id": uData["userId"],
  186. }
  187. if entId > 0 { //企业服务
  188. upData["ent_id"] = entId
  189. if personPhone := gconv.String(o["personPhone"]); personPhone != "" {
  190. jyutil.EndAddUser(ctx, entId, gconv.String(o["user_phone"]), personPhone, gconv.String(o["personName"]))
  191. }
  192. }
  193. //更新订单
  194. _, err = g.DB().Update(ctx, consts.OrderListTableName, upData, "order_code=?", orderCode)
  195. if err != nil {
  196. return err
  197. }
  198. }
  199. return nil
  200. }); err != nil {
  201. log.Println(err)
  202. continue
  203. }
  204. }
  205. } else {
  206. log.Println("自动回款创建回款记录失败", id, orderCode)
  207. }
  208. } else {
  209. log.Println("自动回款重复匹配过滤", id, orderCode)
  210. }
  211. //回款关闭相应的支付码支付
  212. go CancelOnlinePay(ctx, orderCode)
  213. }
  214. }
  215. }
  216. }
  217. } else {
  218. log.Println("回款信息查询为空")
  219. }
  220. log.Println("自动回款匹配。。。结束")
  221. }
  222. // PaymentPlanMatching 回款计划匹配
  223. func PaymentPlanMatching(money, returnMoney int, paymentUser, companyName, remark, remarks, orderCode string, paybackNum int, paybackListArr []map[string]interface{}, returnOrderMap map[string]int) bool {
  224. pUser := reg.ReplaceAllString(paymentUser, "")
  225. if money == returnMoney && (pUser == companyName || remark == orderCode || remarks == orderCode) {
  226. return true
  227. }
  228. if paybackNum > 1 { //仅订单回款计划有多期时,才匹配回款计划,回款计划如只有1期,则无需匹配
  229. isFirstReturn := returnOrderMap[orderCode]
  230. if isFirstReturn < paybackNum && len(paybackListArr) > 0 && len(paybackListArr) >= isFirstReturn {
  231. //② 如该订单已有X笔回款,则匹配第“X+1”期的回款计划,其他期的回款计划不进行匹配,例如:该笔订单无回款,则匹配第1期的回款计划;该笔订单有1笔回款,则匹配第2期的回款计划。
  232. if gconv.String(paybackListArr[isFirstReturn]["code"]) != "合计" && returnMoney == gconv.Int(paybackListArr[isFirstReturn]["money"])*100 && (pUser == companyName) {
  233. return true
  234. }
  235. }
  236. //}
  237. }
  238. return false
  239. }
  240. func CancelOnlinePay(ctx context.Context, orderCode string) {
  241. if total, _ := g.DB().Ctx(ctx).Query(ctx, "SELECT count(*) from return_money_online WHERE status=0 and order_code=? and expire_time >?", orderCode, time.Now().Format("2006-01-02 15:04:05")); !total.IsEmpty() {
  242. r, err := rpc.DialHTTP("tcp", g.Cfg().MustGet(context.Background(), "jyPayRpc").String())
  243. defer r.Close()
  244. res := ""
  245. err = r.Call("JyPayRpc.CloseReturnOrder", orderCode, &res)
  246. if err != nil {
  247. log.Printf("%s关闭支付宝微信支付码失败 %s", orderCode, err)
  248. }
  249. }
  250. }