RepositoryExtension.MallRefund.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. using Aliyun.Acs.Core.Logging;
  2. using JiaZhiQuan.Common.Hubs;
  3. using JiaZhiQuan.Common.Messaging;
  4. using JiaZhiQuan.Common.Messaging.Models;
  5. using JiaZhiQuan.Common.Response;
  6. using JiaZhiQuan.Common.Utils;
  7. using Newtonsoft.Json;
  8. using NLog;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Data;
  12. using System.Linq;
  13. using System.Threading.Tasks;
  14. using JiaZhiQuan.Common.Models.IM;
  15. using Wicture.DbRESTFul;
  16. using Wicture.DbRESTFul.Infrastructure.Repository;
  17. using static JiaZhiQuan.Common.Models.MallGoodsModel.RefundModel;
  18. using System.Collections;
  19. namespace JiaZhiQuan.Common {
  20. public static partial class RepositoryExtension
  21. {
  22. public static void SendAfterSaleMsg(this DbRESTFulRepository repository,
  23. AfterSale refund, DealType dealType, Producer producer) {
  24. List<ImInfo> imInfos = GetImInfo(refund, dealType);
  25. if (imInfos.Count == 0) return;
  26. //写系统消息
  27. try {
  28. List<ImInfo> sysMsgs = imInfos.Where(x => x.senderId == (long)UserType.系统).ToList();
  29. sysMsgs.ForEach(x => _ = producer.ProduceAsync(NotificationModel.GetMsgKey(), new NotificationModel {
  30. Type = NotificationType.CommonWithAfterSale,
  31. Content = JsonConvert.SerializeObject(new AfterSaleSubModel {
  32. UserId = x.acceptId,
  33. Message = x.msg,
  34. AfterSaleId = x.afterSaleId,
  35. ActionLink = $"app://mall/afterSale?afterSaleId={x.afterSaleId}"
  36. })
  37. }));
  38. //发IM其他消息。
  39. List<ImInfo> chatMsgs = imInfos.Where(x => x.senderId != (long)UserType.系统).ToList();
  40. chatMsgs.ForEach(x => _ = producer.ProduceAsync(IMMessageModel.GetMsgKey(), new IMMessageModel {
  41. FromUserId = x.senderId,
  42. TargetUserId = x.acceptId,
  43. Content = new MessageContentDTO {
  44. Content = JsonConvert.SerializeObject(new AfterSalesRemindMessageStorageFormat { AfterSaleId = x.afterSaleId, Message = x.msg }),
  45. ContentType = MessageContentType.AfterSalesRemind
  46. }
  47. }));
  48. }catch (Exception ex) { LoggerManager.Logger.Error(ex, "发送售后单状态消息失败:" + ex.Message); }
  49. }
  50. private static List<ImInfo> GetImInfo(AfterSale refund, DealType dealType) {
  51. string amountStr = BuildYuan(refund.refundOrderAmount);
  52. string typeStr = BuildTypeDesc(refund.type);
  53. switch (dealType) {
  54. case DealType.买家申请退单:
  55. return new List<ImInfo>{new ImInfo {
  56. msg = $"【买家】我发起了{typeStr}申请,等待你处理",
  57. afterSaleId = refund.afterSaleId, senderId = refund.userId,
  58. acceptId = refund.sellerId,
  59. }, };
  60. case DealType.卖家同意退单:
  61. if (refund.type == 1 || refund.type == 2) {
  62. return new List<ImInfo>{new ImInfo {
  63. msg = $"【系统】卖家已同意退款{amountStr},钱款将原路退回",
  64. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  65. acceptId = refund.userId,
  66. } };
  67. }
  68. return new List<ImInfo>{new ImInfo {
  69. msg = $"【卖家】我已发送退货地址,等待你寄回货物。",
  70. afterSaleId = refund.afterSaleId, senderId = refund.sellerId,
  71. acceptId = refund.userId,
  72. } };
  73. case DealType.卖家超时未处理:
  74. if (refund.type == 1 || refund.type == 2) {
  75. return new List<ImInfo>{new ImInfo {
  76. msg = $"【系统】系统已同意退款{amountStr},钱款将原路退回。",
  77. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  78. acceptId = refund.userId,
  79. },new ImInfo {
  80. msg = $"【系统】您超时未处理退款,系统已退款{amountStr}给买家。",
  81. afterSaleId = refund.afterSaleId, senderId =(long)UserType.系统,
  82. acceptId = refund.sellerId,
  83. } };
  84. }
  85. return new List<ImInfo>{new ImInfo {
  86. msg = $"【系统】您超时未处理退款,系统已同意退货退款申请,需买家寄回货物,请您尽快发送退货地址。",
  87. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  88. acceptId = refund.sellerId,
  89. },new ImInfo {
  90. msg = $"【买家】等待你发送退货地址",
  91. afterSaleId = refund.afterSaleId, senderId = refund.userId,
  92. acceptId = refund.sellerId,
  93. } };
  94. case DealType.卖家拒绝退单:
  95. return new List<ImInfo>{new ImInfo {
  96. msg = $"【卖家】我拒绝{typeStr},等待你处理",
  97. afterSaleId = refund.afterSaleId, senderId = refund.sellerId,
  98. acceptId = refund.userId,
  99. } };
  100. case DealType.买家撤销退单:
  101. return new List<ImInfo>{new ImInfo {
  102. msg = $"【系统】买家已撤销{typeStr}。",
  103. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  104. acceptId = refund.sellerId,
  105. } };
  106. case DealType.买家超时未申请维权:
  107. return new List<ImInfo>{new ImInfo {
  108. msg = $"【系统】您超时未处理退款,系统关闭了您的{typeStr}申请。",
  109. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  110. acceptId = refund.userId,
  111. },new ImInfo {
  112. msg = $"【系统】买家超时未处理退款,系统关闭了买家的{typeStr}申请。",
  113. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  114. acceptId = refund.sellerId,
  115. } };
  116. case DealType.买家发起维权:
  117. return new List<ImInfo>{new ImInfo {
  118. msg = $"【买家】我已发起{typeStr}维权,双方提交证据中",
  119. afterSaleId = refund.afterSaleId, senderId = refund.userId,
  120. acceptId = refund.sellerId,
  121. },new ImInfo {
  122. msg = $"【系统】您发起了{typeStr}维权,双方补充中;" +
  123. $"举证结束后平台会介入处理。",
  124. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  125. acceptId = refund.userId,
  126. },new ImInfo {
  127. msg = $"【系统】买家发起了{typeStr}维权," +
  128. $"双方补充中,请尽快提交证据,举证结束后平台会介入处理。",
  129. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  130. acceptId = refund.sellerId,
  131. } };
  132. case DealType.买家退单维权补充证据:
  133. case DealType.买家货损维权补充证据:
  134. return new List<ImInfo>{new ImInfo {
  135. msg = $"【系统】{refund.eventDesc}",
  136. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  137. acceptId = refund.sellerId,
  138. } };
  139. case DealType.卖家退单维权补充证据:
  140. case DealType.卖家货损维权补充证据:
  141. return new List<ImInfo>{new ImInfo {
  142. msg = $"【系统】{refund.eventDesc}",
  143. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  144. acceptId = refund.userId,
  145. } };
  146. case DealType.退单维权买家胜:
  147. return new List<ImInfo>{new ImInfo {
  148. msg = refund.type==2?$"【系统】平台判定您获胜,同意退款{amountStr},钱款退回中。":
  149. $"【系统】平台判定您获胜,同意{typeStr}" +
  150. $"{amountStr},需您寄回货物,请等待卖家发送退货地址。",
  151. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  152. acceptId = refund.userId,
  153. },new ImInfo {
  154. msg = refund.type==2?$"【系统】平台判定买家获胜,已退款{amountStr}给买家。":
  155. $"【系统】平台判定买家获胜,已同意退货退款申请,需买家寄回货物,请您尽快发送退货地址。",
  156. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  157. acceptId = refund.sellerId,
  158. } };
  159. case DealType.退单维权卖家胜:
  160. return new List<ImInfo>{new ImInfo {
  161. msg = $"【系统】平台判定卖家获胜,系统关闭了您的{typeStr}申请。",
  162. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  163. acceptId = refund.userId,
  164. },new ImInfo {
  165. msg = $"【系统】平台判定您获胜,系统关闭了买家的{typeStr}申请。",
  166. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  167. acceptId = refund.sellerId,
  168. } };
  169. case DealType.卖家超时未发送退货地址:
  170. return new List<ImInfo>{new ImInfo {
  171. msg = $"【系统】卖家超时未发生退货地址,系统退款" +
  172. $"{amountStr},钱款原路退回中。",
  173. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  174. acceptId = refund.userId,
  175. },new ImInfo {
  176. msg = $"【系统】您超时未发生退货地址,系统已退款" +
  177. $"{amountStr}给买家。",
  178. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  179. acceptId = refund.sellerId,
  180. } };
  181. case DealType.卖家发送退货地址:
  182. return new List<ImInfo>{new ImInfo {
  183. msg = $"【卖家】我已发送退货地址,等待你寄回货物。",
  184. afterSaleId = refund.afterSaleId, senderId = refund.sellerId,
  185. acceptId = refund.userId, } };
  186. case DealType.买家超时未寄出退货:
  187. return new List<ImInfo>{new ImInfo {
  188. msg = $"【系统】您超时未寄回货物,系统关闭了您的退货退款申请。",
  189. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  190. acceptId = refund.userId,
  191. },new ImInfo {
  192. msg = $"【系统】买家超时未寄回货物,系统关闭了买家的退货退款申请。",
  193. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  194. acceptId = refund.sellerId,
  195. } };
  196. case DealType.买家寄出退货:
  197. return new List<ImInfo>{new ImInfo {
  198. msg = $"【买家】我已退货,等待你确认收货",
  199. afterSaleId = refund.afterSaleId, senderId = refund.userId,
  200. acceptId = refund.sellerId, } };
  201. case DealType.卖家确认收货:
  202. return new List<ImInfo>{new ImInfo {
  203. msg = $"【系统】退货退款成功,退款{amountStr}原路退回中。",
  204. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  205. acceptId = refund.userId,
  206. },new ImInfo {
  207. msg = $"【系统】退货退款成功,已退款{amountStr}给买家。",
  208. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  209. acceptId = refund.sellerId,
  210. } };
  211. case DealType.卖家超时未确认收货:
  212. return new List<ImInfo>{new ImInfo {
  213. msg = $"【系统】退货退款成功,退款{amountStr}原路退回中。",
  214. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  215. acceptId = refund.userId,
  216. },new ImInfo {
  217. msg = $"【系统】您超时未确认收到退货,系统自动确认收到退货,已退款" +
  218. $"{amountStr}给买家。",
  219. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  220. acceptId = refund.sellerId,
  221. } };
  222. case DealType.卖家申请货损:
  223. return new List<ImInfo>{new ImInfo {
  224. msg = $"【卖家】我发起了货物损失申请,等待你处理",
  225. afterSaleId = refund.afterSaleId, senderId = refund.sellerId,
  226. acceptId = refund.userId, } };
  227. case DealType.买家同意货损:
  228. return new List<ImInfo>{
  229. new ImInfo {
  230. msg = $"【系统】买家已同意货物损失" +
  231. $"{amountStr}",
  232. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  233. acceptId = refund.sellerId,
  234. } };
  235. case DealType.买家拒绝货损:
  236. return new List<ImInfo>{new ImInfo {
  237. msg = $"【买家】我拒绝货物损失,等待你处理",
  238. afterSaleId = refund.afterSaleId, senderId = refund.userId,
  239. acceptId = refund.sellerId, } };
  240. case DealType.买家超时未处理货损:
  241. return new List<ImInfo>{new ImInfo {
  242. msg = $"【系统】您超时未处理货物损失,系统已同意卖家的货物损失申请。",
  243. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  244. acceptId = refund.userId,
  245. },new ImInfo {
  246. msg = $"【系统】买家超时未处理货物损失,系统已同意您的货物损失申请。" +
  247. $"{amountStr}给买家。",
  248. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  249. acceptId = refund.sellerId,
  250. } };
  251. case DealType.卖家撤销货损:
  252. return new List<ImInfo>{new ImInfo {
  253. msg = $"【系统】卖家已撤销货物损失,退货退款将继续进行。",
  254. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  255. acceptId = refund.userId,
  256. } };
  257. case DealType.卖家超时未申请维权:
  258. return new List<ImInfo>{new ImInfo {
  259. msg = $"【系统】卖家超时未处理货物损失,系统关闭了卖家的货物损失申请,退货退款将继续进行。",
  260. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  261. acceptId = refund.userId,
  262. },new ImInfo {
  263. msg = $"【系统】您超时未处理货物损失,系统关闭了您的货物损失申请,退货退款将继续进行。" +
  264. $"{amountStr}给买家。",
  265. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  266. acceptId = refund.sellerId,
  267. } };
  268. case DealType.卖家发起货损维权:
  269. return new List<ImInfo>{new ImInfo {
  270. msg = $"【卖家】我已发起货物损失维权,双方提交证据中",
  271. afterSaleId = refund.afterSaleId, senderId = refund.sellerId,
  272. acceptId = refund.userId,
  273. },new ImInfo {
  274. msg = $"【系统】卖家发起了货物损失维权,双方补充中,请尽快提交证据,举证结束后平台会介入处理。",
  275. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  276. acceptId = refund.userId,
  277. },new ImInfo {
  278. msg = $"【系统】您发起了货物损失维权,双方补充中;举证结束后平台会介入处理。",
  279. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  280. acceptId = refund.sellerId,
  281. } };
  282. case DealType.货损维权买家胜:
  283. return new List<ImInfo>{new ImInfo {
  284. msg = $"【系统】平台判定您获胜,关闭了卖家的货物损失申请,退货退款将继续进行。",
  285. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  286. acceptId = refund.userId,
  287. },new ImInfo {
  288. msg = $"【系统】平台判定买家获胜,关闭了您的货物损失申请,退货退款将继续进行。",
  289. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  290. acceptId = refund.sellerId,
  291. } };
  292. case DealType.货损维权卖家胜:
  293. return new List<ImInfo>{new ImInfo {
  294. msg = $"【系统】平台判定卖家获胜,同意货物损失{amountStr}。",
  295. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  296. acceptId = refund.userId,
  297. },new ImInfo {
  298. msg = $"【系统】平台判定您获胜,同意货物损失{amountStr}。",
  299. afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  300. acceptId = refund.sellerId,
  301. } };
  302. //case DealType.买家退款失败:
  303. // return new List<ImInfo>{new ImInfo {
  304. // msg = $"【系统】退款{amountStr}原路退回失败!" +
  305. // $"请通过意见反馈或客服电话021-63333317(工作日早9~晚6)联系我们。",
  306. // afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  307. // acceptId = refund.userId,
  308. // } };
  309. //case DealType.买家退款成功:
  310. // return new List<ImInfo>{new ImInfo {
  311. // msg = $"【系统】您的退款{amountStr}已原路退回," +
  312. // $"可前往支付宝账户{refund.loginName}的账单中查看。",
  313. // afterSaleId = refund.afterSaleId, senderId = (long)UserType.系统,
  314. // acceptId = refund.userId,
  315. // } };
  316. case DealType.买家提醒卖家收货:
  317. return new List<ImInfo>{new ImInfo {
  318. msg = $"【买家】请在检查宝贝无误后,及时确认收货",
  319. afterSaleId = refund.afterSaleId, senderId = refund.userId,
  320. acceptId = refund.sellerId, } };
  321. default:
  322. return new List<ImInfo>();
  323. }
  324. }
  325. private static string BuildYuan(int amount) {
  326. var yuan = AmountUtils.ConvertCentToYuanStr(amount);
  327. return $"¥{yuan}";
  328. }
  329. private static string BuildTypeDesc(int type) {
  330. return type switch {
  331. 1 => "退款",
  332. 2 => "退款",
  333. 3 => "退货退款",
  334. _ => null
  335. };
  336. }
  337. //启用订单03计时器。
  338. public static async Task<OperateResult> EnableOrderTimer(this DbRESTFulRepository repository,
  339. long orderId, IDbConnection conn, IDbTransaction trans)
  340. {
  341. if (await HasRuningAfterSaleInOrder(repository, orderId, conn, trans))
  342. return new OperateResult { count = 1 };
  343. //无进行中的退单,则需要更新订单中的退单完成时间和订单的收货超时时间
  344. var orderCnt = await repository.QuerySingleOrDefaultAsync<int>(
  345. $@"update mall_order set refundCompleteAt=@now,receiveExpireAt=date_add(@now,
  346. interval TIMESTAMPDIFF(SECOND, refundCreateAt,receiveExpireAt) SECOND),
  347. sysRemindBuyerReceiptTime=date_add(receiveExpireAt,interval -1 day),
  348. aftersaleRunningState=0
  349. where orderId={orderId} and orderState=3;
  350. select row_count();", new { now = DateTime.Now }, conn, trans);
  351. return new OperateResult { count = orderCnt };
  352. }
  353. public static async Task<bool> HasRuningAfterSaleInOrder(this DbRESTFulRepository repository,
  354. long orderId, IDbConnection conn, IDbTransaction trans) {
  355. //查询进行中的退单个数
  356. return (await repository.QuerySingleOrDefaultAsync<long?>(
  357. $@"select afterSaleId from mall_after_sale where orderId={orderId}
  358. and state>0 and state<>5 limit 1", null, conn, trans) ?? 0) > 0;
  359. }
  360. public static async Task<OperateResult> ChangeUserPoints(this DbRESTFulRepository repository,
  361. long userId, int points, string way, string extra, string desc,
  362. IDbConnection conn = null, IDbTransaction trans = null)
  363. {
  364. var count = 0;
  365. if (points != 0)
  366. {
  367. var data = new { way, extra, desc };
  368. // 更新价值币总数
  369. count = await repository.QuerySingleOrDefaultAsync<int>(
  370. @$"update n_user_statistic set points=points+{points}
  371. where userId={userId};select row_count();", null, conn, trans);
  372. // 添加价值币变动记录
  373. await repository.QueryAsync<dynamic>(
  374. @$"insert into n_user_points_record (userId, points, `way`,
  375. extra, description, createAt, createAtDate) values({userId},
  376. {points}, @way, @extra, @desc, now(), now())", data, conn, trans);
  377. }
  378. return new OperateResult { count= count };
  379. }
  380. /// <summary>
  381. /// 卖家超时发货-订单行为
  382. /// </summary>
  383. /// <param name="param"></param>
  384. /// <returns></returns>
  385. public static async Task<OperateResult> SellerDeliveryExpire(this DbRESTFulRepository repository,
  386. long orderId, IDbConnection conn, IDbTransaction trans)
  387. {
  388. var count = await repository.QuerySingleOrDefaultAsync<int>(
  389. $@"update mall_after_sale set state=-1,closeAt=@now where type=1 and state=1
  390. [and orderId=@orderId ];select row_count();",
  391. new { now = DateTime.Now, orderId }, conn, trans);
  392. if (count > 0)
  393. {
  394. //插入退单事件
  395. await InsertAfterSaleEventByOrderId(repository, orderId, -1, "系统关闭交易,系统关闭", null, conn, trans);
  396. }
  397. return new OperateResult { count = count };
  398. }
  399. public static async Task<OperateResult> InsertAfterSaleEventByOrderId(this DbRESTFulRepository repository,
  400. long orderId, int eventType, string eventDesc, string resourceJson,
  401. IDbConnection conn = null, IDbTransaction trans = null)
  402. {
  403. var eventInfo = new
  404. {
  405. eventType, eventDesc,
  406. resources = resourceJson,
  407. createAt = DateTime.Now,
  408. };
  409. var count = await repository.QuerySingleOrDefaultAsync<int>(
  410. $@"insert into mall_after_sale_event (afterSaleId,orderId,
  411. orderDetailId,eventType,eventDesc,resources,createAt,)
  412. select afterSaleId,orderId,orderDetailId,@eventType,
  413. concat(@eventDesc,case when type=1 or type=2 then '退款' else '退货退款' end)
  414. ,@resources,@createAt from mall_after_sale
  415. where orderId={orderId} and state>0 and state<>5
  416. ;select row_count();", eventInfo, conn, trans);
  417. return new OperateResult { count = count };
  418. }
  419. // 退单成功,处理退单结果
  420. public static async Task<bool> DealAfterSaleSuccess(this DbRESTFulRepository repository,
  421. AfterSale refund, long newBillId) {
  422. var amount = CalRefundAmount(refund);
  423. using var conn = repository.ConnectionManager.GetConnection();
  424. conn.Open();
  425. var trans = conn.BeginTransaction();
  426. try {
  427. //插入流水表
  428. //判断是否存在
  429. var billId = await repository.QuerySingleOrDefaultAsync<long>(
  430. $@"select billId from mall_bill_record where afterSaleId={refund.afterSaleId}
  431. and billType={(int)BillType.退单流水} limit 1 ", null, conn, trans);
  432. if (billId > 0) return true;
  433. var cnt = await repository.InsertRefundBill(refund, (object)amount, newBillId, conn, trans);
  434. if (cnt.count <= 0) return false;
  435. //插入退款表
  436. await repository.InsertRefundTask(refund, (object)amount, conn, trans);
  437. string way = "AFTERSALE", extra = $"{refund.afterSaleId}",
  438. desc = $"交易退回-退款{refund.afterSaleId}";
  439. await repository.ChangeUserPoints((long)refund.userId, (int)amount.points,
  440. way, extra, desc, conn, trans);
  441. trans.Commit();
  442. return true;
  443. } catch (Exception ex) {
  444. trans.Rollback(); throw;
  445. }
  446. }
  447. //判断订单是否全额退货退款
  448. public static async Task<dynamic> RefundAllForSuccess(this DbRESTFulRepository repository,
  449. AfterSale refundInfo,long newBillId,long newCashoutId, IDbConnection conn, IDbTransaction trans) {
  450. var order = await repository.QuerySingleOrDefaultAsync<dynamic>(
  451. $@"select orderId,orderAmount,serviceAmount from mall_order where orderId={refundInfo.orderId}",
  452. null, conn, trans);
  453. List<dynamic> successRefunds = (await repository.QueryAsync<dynamic>(
  454. $@"select afterSaleId,refundOrderAmount,lossAmount from mall_after_sale
  455. where orderId={refundInfo.orderId} and state=5 ",
  456. null, conn, trans)).ToList();
  457. var now = DateTime.Now;
  458. //表示全额退,这里不能考虑货损
  459. var refundSum = successRefunds.Where(x => x.afterSaleId != refundInfo.afterSaleId)
  460. .Sum(x => x.refundOrderAmount);
  461. if (refundInfo.refundOrderAmount == order.orderAmount - refundSum) {
  462. var orderCnt = await repository.QuerySingleOrDefaultAsync<int>(
  463. $@"update mall_order set orderState=4,autoCompletedAt=@now,refundCompleteAt=@now,
  464. aftersaleRunningState=0
  465. where orderId={refundInfo.orderId} and orderState=3;
  466. select row_count();", new { now }, conn, trans);
  467. if (orderCnt <= 0) throw new Exception("订单异常,无法更新订单状态");
  468. await repository.MallOrderSellerSettlement((long)refundInfo.orderId, newBillId,
  469. newCashoutId);
  470. return new { count = orderCnt };
  471. }
  472. //部分退
  473. else if (refundInfo.refundOrderAmount < order.orderAmount - refundSum) {
  474. var count = await repository.EnableOrderTimer((long)refundInfo.orderId, conn, trans);
  475. if (count.count <= 0) throw new Exception("订单异常,无法启用定时器03");
  476. return count;
  477. } else {
  478. throw new Exception("退单总金额,超过订单总金额。");
  479. }
  480. }
  481. //判断订单是否全额退货退款
  482. public static async Task<dynamic> RefundAllForClose(this DbRESTFulRepository repository,
  483. AfterSale refundInfo, IDbConnection conn, IDbTransaction trans) {
  484. var order = await repository.QuerySingleOrDefaultAsync<dynamic>(
  485. $@"select orderId,orderAmount,serviceAmount from mall_order where orderId={refundInfo.orderId}",
  486. null, conn, trans);
  487. List<dynamic> successRefunds = (await repository.QueryAsync<dynamic>(
  488. $@"select afterSaleId,refundOrderAmount,lossAmount from mall_after_sale
  489. where orderId={refundInfo.orderId} and state=5 ",
  490. null, conn, trans)).ToList();
  491. var now = DateTime.Now;
  492. //表示全额退
  493. var refundSum = successRefunds.Where(x => x.afterSaleId != refundInfo.afterSaleId)
  494. .Sum(x => x.refundOrderAmount);
  495. if (refundInfo.refundOrderAmount == order.orderAmount - refundSum) {
  496. var orderCnt = await repository.QuerySingleOrDefaultAsync<int>(
  497. $@"update mall_order set orderState=-9,closeAt=@now,closeReasonType=4,refundCompleteAt=@now,
  498. aftersaleRunningState=0
  499. where orderId={refundInfo.orderId} and orderState=3;
  500. select row_count();", new { now }, conn, trans);
  501. if (orderCnt <= 0) throw new Exception("订单异常,无法更新订单状态");
  502. return new { count = orderCnt };
  503. }
  504. //部分退
  505. else if (refundInfo.refundOrderAmount < order.orderAmount - refundSum) {
  506. var count = await repository.EnableOrderTimer((long)refundInfo.orderId, conn, trans);
  507. if (count.count <= 0) throw new Exception("订单异常,无法启用定时器03");
  508. return count;
  509. } else {
  510. throw new Exception("退单总金额,超过订单总金额。");
  511. }
  512. }
  513. // 插入退单流水
  514. private static async Task<OperateResult> InsertRefundBill(this DbRESTFulRepository repository,
  515. AfterSale refund, dynamic amount, long newBillId, IDbConnection conn, IDbTransaction trans) {
  516. //插入退款流水。
  517. var count = await repository.SimpleInsert("mall_bill_record", new {
  518. billId = newBillId, refund.orderId, refund.userId,
  519. refund.afterSaleId, billType = (int)BillType.退单流水,
  520. taskOrderId= $"{refund.orderId}_{refund.afterSaleId}",
  521. amount.realAmount, billPoints = amount.points,
  522. pointsDeductionAmount = amount.totalAmount - amount.realAmount,
  523. billAmount = amount.totalAmount, billState = 1, createAt = DateTime.Now
  524. }, conn, trans);
  525. return new OperateResult { count = count.count };
  526. }
  527. // 插入退款任务表
  528. private static async Task<OperateResult> InsertRefundTask(this DbRESTFulRepository repository,
  529. AfterSale refund, dynamic amount, IDbConnection conn, IDbTransaction trans) {
  530. if (amount.realAmount <= 0) return new OperateResult { count = 1 };
  531. var payId = await repository.QuerySingleOrDefaultAsync<string>(
  532. $@"select payId from mall_order_pay_relation
  533. where orderId={refund.orderId}", null, conn, trans);
  534. if (string.IsNullOrWhiteSpace(payId)) throw new Exception("插入退款任务时,支付单号id不能为空");
  535. var count = await repository.SimpleInsert("mall_order_refund",
  536. new {
  537. id = $"{refund.orderId}_{refund.afterSaleId}", refund.userId, refund.orderId, payId,
  538. refund.afterSaleId, amount = amount.realAmount, state = 0, createAt = DateTime.Now
  539. }, conn, trans);
  540. return new OperateResult { count = count.count };
  541. }
  542. private static dynamic CalRefundAmount(AfterSale refund) {
  543. var totalAmount = refund.refundOrderAmount - refund.lossAmount;
  544. //这样计算比例会二次失真。不考虑失真问题,向下取分即可。
  545. var realAmount = (int)(totalAmount * ((double)refund.refundAmount / refund.refundOrderAmount));
  546. var points = (int)(totalAmount * ((double)refund.refundPoints / refund.refundOrderAmount));
  547. return new { totalAmount, realAmount, points };
  548. }
  549. //根据对象插入表
  550. public static async Task<InsertResult> SimpleInsert(this DbRESTFulRepository repository,
  551. string tableName, object insertInfo,
  552. IDbConnection conn = null, IDbTransaction trans = null)
  553. {
  554. var count = new InsertResult { count = 0 };
  555. if (string.IsNullOrWhiteSpace(tableName) || insertInfo == null) return count;
  556. if (insertInfo is IEnumerable<dynamic> insertList) {
  557. if (insertList.Count() == 0) return count;
  558. var first = insertList.First();
  559. IDictionary<string, object?> inSet = ExpandoUtils.ConvertToDynamic(first);
  560. if (inSet.Count == 0) return count;
  561. var insertSet = inSet.Where(x => x.Value != null);
  562. string fields = string.Join(",", insertSet.Select(x => x.Key)),
  563. valuesFields = string.Join(",", insertSet.Select(x => $"@{x.Key}"));
  564. string insert = $"({fields}) values({valuesFields})";
  565. //批量插入的id,获取的不准确。
  566. string sql = $"insert into {tableName} {insert} ";
  567. var cnt= await repository.QuerySingleOrDefaultAsync<int>(sql, insertList, conn, trans, false);
  568. return new InsertResult { count = cnt };
  569. } else {
  570. IDictionary<string, object?> inSet = ExpandoUtils.ConvertToDynamic(insertInfo);
  571. if (inSet.Count == 0) return count;
  572. var insertSet = inSet.Where(x => x.Value != null);
  573. string fields = string.Join(",", insertSet.Select(x => x.Key)),
  574. valuesFields = string.Join(",", insertSet.Select(x => $"@{x.Key}"));
  575. string insert = $"({fields}) values({valuesFields})";
  576. string sql = $"insert into {tableName} {insert} ;select row_count() count,LAST_INSERT_ID() insertId;";
  577. return await repository.QuerySingleOrDefaultAsync<InsertResult>(sql, insertInfo, conn, trans, false);
  578. }
  579. }
  580. public static async Task<OperateResult> SimpleUpdate(this DbRESTFulRepository repository,
  581. string tableName, object updateInfo, object condition,
  582. IDbConnection conn = null, IDbTransaction trans = null, bool updateNull = true) {
  583. var count = new OperateResult { count = 0 };
  584. if (string.IsNullOrWhiteSpace(tableName) ||
  585. updateInfo == null || condition == null) return count;
  586. IDictionary<string, object?> updateSet = ExpandoUtils.ConvertToDynamic(updateInfo);
  587. IDictionary<string, object?> conditionSet = ExpandoUtils.ConvertToDynamic(condition);
  588. if (updateSet.Count == 0 || conditionSet.Count == 0) return count;
  589. //updateNull为false时,表示不更新空值。
  590. string upContent = string.Join(",", updateSet.Where(x => updateNull || x.Value != null).
  591. Select(x => $"{x.Key}=@{x.Key}"));
  592. string conContent = string.Join(" and ", conditionSet.Select(x => !(x.Value is string)&& x.Value is IEnumerable ?
  593. $"{x.Key} in @c2{x.Key}" : $"{x.Key}=@c2{x.Key}"));
  594. string sql = $"update {tableName} set {upContent} where {conContent} ;select row_count();";
  595. var upObj = new Dictionary<string, object>(updateSet);
  596. conditionSet.ForEach(x => upObj.Add($"c2{x.Key}", x.Value));
  597. return new OperateResult { count = await repository.QuerySingleOrDefaultAsync<int>(sql, upObj, conn, trans, false) };
  598. }
  599. }
  600. }