automaticPayment.go 14 KB

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