diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/AppletController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/AppletController.java index 1fa9568..d711cdf 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/AppletController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/AppletController.java @@ -11,6 +11,7 @@ import com.ruoyi.system.domain.*; import com.ruoyi.system.domain.AppleDoMain.OrderApple; import com.ruoyi.system.domain.AppleDoMain.AddressApple; import com.ruoyi.system.service.*; +import com.winnerlook.model.VoiceResponseResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; @@ -126,6 +127,12 @@ public class AppletController extends BaseController { private IQuoteMaterialTypeService quoteMaterialTypeService; @Autowired private IQuoteMaterialService quoteMaterialService; + @Autowired + private WechatPayUtil wechatPayUtil; + @Autowired + private IGoodsOrderCursorService goodsOrderCursorService; + + // /** @@ -264,7 +271,7 @@ public class AppletController extends BaseController { // 构建查询条件:状态启用且类型为服务 ServiceCate serviceCateQuery = new ServiceCate(); serviceCateQuery.setStatus(1L); // 启用状态 - serviceCateQuery.setType(1L); // 服务类型 + // serviceCateQuery.setType(1L); // 服务类型 // 查询分类列表 List categoryList = serviceCateService.selectServiceCateList(serviceCateQuery); @@ -1148,6 +1155,39 @@ public class AppletController extends BaseController { } } +// /** +// * 获取服务商品详细信息 +// * +// * @param id 商品ID +// * @param request HTTP请求对象 +// * @return 商品详细信息 +// *

+// * 接口说明: +// * - 根据商品ID获取详细信息 +// * - 返回格式化的商品数据 +// * - 包含图片、基础信息等数组数据 +// */ +// @GetMapping(value = "/api/service/info/id/{id}") +// public AjaxResult serviceGoodsQuery(@PathVariable("id") long id, HttpServletRequest request) { +// try { +// // 参数验证 +// if (id <= 0) { +// return error("商品ID无效"); +// } +// +// // 查询商品信息 +// ServiceGoods serviceGoodsData = serviceGoodsService.selectServiceGoodsById(id); +// if (serviceGoodsData != null) { +// // 使用工具类转换数据格式 +// AppletControllerUtil.ServiceGoodsResponse response = new AppletControllerUtil.ServiceGoodsResponse(serviceGoodsData); +// return success(response); +// } else { +// return error("商品不存在或已下架"); +// } +// } catch (Exception e) { +// return error("查询商品详情失败:" + e.getMessage()); +// } +// } /** * 获取服务商品详细信息 * @@ -1157,7 +1197,8 @@ public class AppletController extends BaseController { *

* 接口说明: * - 根据商品ID获取详细信息 - * - 返回格式化的商品数据 + * - type=1时返回ServiceGoodsResponse格式 + * - type=2时返回json.txt中的商品格式 * - 包含图片、基础信息等数组数据 */ @GetMapping(value = "/api/service/info/id/{id}") @@ -1165,23 +1206,29 @@ public class AppletController extends BaseController { try { // 参数验证 if (id <= 0) { - return error("商品ID无效"); + return AppletControllerUtil.appletError("商品ID无效"); } // 查询商品信息 ServiceGoods serviceGoodsData = serviceGoodsService.selectServiceGoodsById(id); - if (serviceGoodsData != null) { - // 使用工具类转换数据格式 - AppletControllerUtil.ServiceGoodsResponse response = new AppletControllerUtil.ServiceGoodsResponse(serviceGoodsData); - return success(response); + if (serviceGoodsData == null) { + return AppletControllerUtil.appletError("商品不存在或已下架"); + } + + // 根据商品类型返回不同格式的数据 + if (serviceGoodsData.getType() != null && serviceGoodsData.getType().intValue() == 2) { + // type=2时返回json.txt格式 + Map goodsData = AppletControllerUtil.buildType2ServiceGoodsResponse(serviceGoodsData); + return AppletControllerUtil.appletSuccess(goodsData); } else { - return error("商品不存在或已下架"); + // type=1时返回ServiceGoodsResponse格式 + AppletControllerUtil.ServiceGoodsResponse response = new AppletControllerUtil.ServiceGoodsResponse(serviceGoodsData); + return AppletControllerUtil.appletSuccess(response); } } catch (Exception e) { - return error("查询商品详情失败:" + e.getMessage()); + return AppletControllerUtil.appletError("查询商品详情失败:" + e.getMessage()); } } - /** * 获取广告图片列表 * @@ -4197,7 +4244,7 @@ public class AppletController extends BaseController { // 6. 构建返回数据 Map countData = new HashMap<>(); countData.put("one", unusedCount); // 未使用数量 - countData.put("two", usedCount); // 已使用数量 + countData.put("two", usedCount); // 已使用数量 countData.put("three", expiredCount); // 已过期数量 countData.put("four", availableToReceiveCount); // 待领取数量 @@ -4384,7 +4431,7 @@ public class AppletController extends BaseController { // 4. 验证必要参数 Long productId = Long.valueOf(orderParams.get("product_id").toString()); Long addressId = Long.valueOf(orderParams.get("address_id").toString()); - String makeTime = orderParams.get("make_time").toString(); + String sku = orderParams.get("sku") != null ? orderParams.get("sku").toString() : ""; @@ -4402,26 +4449,101 @@ public class AppletController extends BaseController { //判断如果是商品就添加商品订单,如果是服务就添加服务订单 if(serviceGoods.getType()==2){ Long num = Long.valueOf(orderParams.get("num").toString()); - GoodsOrder goodsOrder = new GoodsOrder(); + + // 创建商品订单 + GoodsOrder goodsOrder = new GoodsOrder(); goodsOrder.setType(2); - goodsOrder.setMainOrderId("WX" + System.currentTimeMillis()); - goodsOrder.setOrderId("B" + System.currentTimeMillis()); + String mainOrderId = "WX" + System.currentTimeMillis(); + String orderId = "B" + System.currentTimeMillis(); + goodsOrder.setMainOrderId(mainOrderId); + goodsOrder.setOrderId(orderId); goodsOrder.setUid(user.getId()); goodsOrder.setProductId(productId); goodsOrder.setName(userAddress.getName()); goodsOrder.setPhone(userAddress.getPhone()); goodsOrder.setAddress(userAddress.getAddressName()); goodsOrder.setNum(num); - goodsOrder.setTotalPrice(serviceGoods.getPrice().multiply(BigDecimal.valueOf(num))); + + // 计算订单金额 + BigDecimal totalPrice = serviceGoods.getPrice().multiply(BigDecimal.valueOf(num)); + goodsOrder.setTotalPrice(totalPrice); goodsOrder.setGoodPrice(serviceGoods.getPrice()); - goodsOrder.setPayPrice(new BigDecimal(0)); + goodsOrder.setPayPrice(totalPrice); // 实际支付金额 goodsOrder.setDeduction(new BigDecimal(0)); - goodsOrder.setStatus(1L); + goodsOrder.setStatus(1L); // 待支付状态 goodsOrder.setAddressId(addressId); goodsOrder.setSku(sku); - goodsOrderService.insertGoodsOrder(goodsOrder); - return success("预约成功"); + + // 保存订单到数据库 + int insertResult = goodsOrderService.insertGoodsOrder(goodsOrder); + if (insertResult <= 0) { + return error("订单创建失败,请稍后重试"); + } + + // 调用微信支付统一下单 + try { + // 准备支付参数 + String openid = user.getOpenid(); + if (openid == null || openid.trim().isEmpty()) { + return error("用户openid为空,无法发起支付"); + } + + // 金额转换为分(微信支付要求) + int totalFeeInCents = totalPrice.multiply(new BigDecimal(100)).intValue(); + String body = "商品订单-" + serviceGoods.getTitle(); + String notifyUrl = "https://4d983d7f.r3.cpolar.top/api/wechat/pay/notify"; // TODO: 预留回调地址,需要根据实际部署环境配置 + String attach = "goods_order:" + goodsOrder.getId(); // 附加数据,用于回调时识别订单类型 + + // 调用微信支付工具类 + Map payResult = wechatPayUtil.unifiedOrder( + openid, + orderId, + totalFeeInCents, + body, + notifyUrl, + attach + ); + + if (payResult != null && (Boolean) payResult.get("success")) { + // 支付下单成功,返回支付参数给前端 + Map payParams = (Map) payResult.get("payParams"); + Map responseData = new HashMap<>(); + responseData.put("orderId", goodsOrder.getId()); + responseData.put("orderNo", orderId); + responseData.put("totalPrice", totalPrice.toString()); + responseData.put("prepayId", payResult.get("prepayId")); + + // 添加微信支付所需的参数(根据图片显示的格式) + if (payParams != null) { + responseData.put("appId", payParams.get("appId")); + responseData.put("timeStamp", payParams.get("timeStamp")); + responseData.put("nonceStr", payParams.get("nonceStr")); + responseData.put("package", payParams.get("package")); + responseData.put("signType", payParams.get("signType")); + responseData.put("paySign", payParams.get("paySign")); + } + + return AppletControllerUtil.appletSuccess(responseData); + } else { + // 支付下单失败,删除已创建的订单或标记为失败 + goodsOrder.setStatus(0L); // 标记为失败状态 + goodsOrderService.updateGoodsOrder(goodsOrder); + + String errorMsg = payResult != null ? (String) payResult.get("message") : "微信支付下单失败"; + return error("支付下单失败:" + errorMsg); + } + + } catch (Exception e) { + // 支付异常,删除已创建的订单或标记为失败 + goodsOrder.setStatus(0L); // 标记为失败状态 + goodsOrderService.updateGoodsOrder(goodsOrder); + + logger.error("微信支付下单异常:", e); + return error("支付下单异常:" + e.getMessage()); + } + }else{ + String makeTime = orderParams.get("make_time").toString(); // 7. 创建订单对象 Order order = new Order(); order.setType(1); // 1:服务项目 @@ -4482,10 +4604,10 @@ public class AppletController extends BaseController { orderLogService.insertOrderLog(orderLog); //完成订单的初步创建之后开始进行派单和发送订阅,以及预约师傅的操作 //完成订单后的第一步,向用户发送订阅消息 - Order orderNewData= orderService.selectOrderById(order.getId()); - String wxsendmsg=WXsendMsgUtil.sendMsgForUserInfo(user.getOpenid(), orderNewData, serviceGoods); + Order orderNewData= orderService.selectOrderById(order.getId()); + String wxsendmsg=WXsendMsgUtil.sendMsgForUserInfo(user.getOpenid(), orderNewData, serviceGoods); //第二步系统派单 - Users worker = AppletControllerUtil.creatWorkerForOrder(orderNewData); + Users worker = AppletControllerUtil.creatWorkerForOrder(orderNewData); //第三步向师傅发送订阅消息 if(worker!=null){ //确定派单之后,更新订单状态为已派单,并添加订单日志记录 @@ -4515,7 +4637,7 @@ public class AppletController extends BaseController { //给师傅发送微信订阅消息进行通知需要他进行接单 WXsendMsgUtil.sendMsgForWorkerInfo(worker.getOpenid(), orderNewData, serviceGoods); //第四步给师傅进行电话通知 - YunXinPhoneUtilAPI.httpsAxbTransfer(worker.getPhone(), "404168"); + YunXinPhoneUtilAPI.httpsAxbTransfer(worker.getPhone()); } @@ -5166,6 +5288,7 @@ public class AppletController extends BaseController { @GetMapping("/api/worker/change/order/{orderId}") public AjaxResult changeWorkerOrder(@PathVariable("orderId") Long orderId, @RequestParam("id") Long newWorkerId) { try { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 1. 查询订单 Order order = orderService.selectOrderById(orderId); if (order == null) { @@ -5187,29 +5310,71 @@ public class AppletController extends BaseController { if (updateOrder <= 0) { return AjaxResult.error("订单更新失败"); } - // 5. 插入转单日志 - OrderLog log = new OrderLog(); - log.setOid(order.getId()); - log.setOrderId(order.getOrderId()); - log.setTitle("转单"); - log.setType(new BigDecimal(1.1)); - log.setWorkerId(newWorkerId); - log.setIsPause(2); // 转单 - // 构造content - JSONObject content = new JSONObject(); - content.put("name", "师傅" + newWorker.getName() + "将继续为您服务 不需要预约时间"); - String oldName = oldWorker != null ? oldWorker.getName() : ""; - content.put("convert", oldName + "将订单转给" + newWorker.getName()); - log.setContent(content.toString()); - log.setCreatedAt(new Date()); - orderLogService.insertOrderLog(log); - // 6. 查询所有日志并批量更新worker_id - OrderLog queryLog = new OrderLog(); - queryLog.setOid(order.getId()); - List logList = orderLogService.selectOrderLogList(queryLog); - for (OrderLog l : logList) { - l.setWorkerId(newWorkerId); - orderLogService.updateOrderLog(l); + //查询最新的订单日志 + OrderLog log = orderLogService.selectDataTheFirstNew(order.getId()); + OrderLog log1=new OrderLog(); + log1.setOid(order.getId()); + //原订单日志下所有师傅id需更换为新的师傅id + List logList = orderLogService.selectOrderLogList(log1); + if (!logList.isEmpty()) { + for (OrderLog l : logList){ + if (l.getWorkerId()!=null){ + l.setWorkerId(newWorkerId); + orderLogService.updateOrderLog(l); + } + } + } + //如果订单状态为6就继续做 + if (log.getType().compareTo(new BigDecimal("6.0"))==0) { + // 2. 组装日志内容为数组格式 + Map logItem = new LinkedHashMap<>(); + + JSONArray jsonArray = JSONArray.parseArray(log.getContent()); + logItem.put("name", "师傅"+newWorker.getName()+"将继续为您服务 不需要预约时间"); + logItem.put("name", "暂停服务"); + logItem.put("image", ""); + logItem.put("reson", "由于订单师傅更换,自动暂停服务"); + logItem.put("next_time", ""); + logItem.put("type", 2); + logItem.put("date", sdf.format(new Date())); + jsonArray.add(logItem); + if (log != null) { + log.setIsPause(2); + log.setContent(jsonArray.toJSONString()); + orderLogService.updateOrderLog(log); + order.setIsPause(2); + order.setWorkerId(newWorkerId); + orderService.updateOrder(order); + } + + }else{ + // 5. 插入转单日志 + OrderLog newlog = new OrderLog(); + if(log.getType().compareTo(new BigDecimal("5.0"))==0){ + newlog.setType(new BigDecimal(6.0)); + }else{ + newlog.setType(new BigDecimal(1.1)); + } + JSONObject jsonObjectnew=new JSONObject(); + jsonObjectnew.put("name", "师傅"+newWorker.getName()+"将继续为您服务 不需要预约时间"); + jsonObjectnew.put("convert", oldWorker.getName()+"将订单转给"+newWorker.getName()); + newlog.setContent(jsonObjectnew.toJSONString()); + newlog.setOid(order.getId()); + newlog.setOrderId(order.getOrderId()); + newlog.setWorkerId(newWorkerId); + newlog.setWorkerLogId(newWorkerId); + newlog.setIsPause(2); + orderLogService.insertOrderLog(newlog); + //需要解绑原订单上原师傅和客户的虚拟号 + VoiceResponseResult resultObj=YunXinPhoneUtilAPI.httpsPrivacyUnbind(order.getWorkerPhone(), order.getUserPhone(), order.getMiddlePhone()); + if (resultObj.getResult().equals("000000")){ + order.setWorkerPhone(null); + order.setUserPhone(null); + order.setMiddlePhone(null); + orderService.updateOrder(order); + } + //给新师傅进行电话通知 + YunXinPhoneUtilAPI.httpsAxbTransfer(newWorker.getPhone()); } return AjaxResult.success("转单成功"); } catch (Exception e) { @@ -5327,7 +5492,7 @@ public class AppletController extends BaseController { } } }else{ - filteredList.addAll(commentList); + filteredList.addAll(commentList); } int totalCount = filteredList.size(); int from = (page - 1) * limit; @@ -5712,8 +5877,19 @@ public class AppletController extends BaseController { * 逻辑:查找订单最新日志,设置workerCost和price,并设置paid=1 */ @PostMapping("/api/worker/set/price") - public AjaxResult setWorkerPrice(@RequestBody Map params) { + public AjaxResult setWorkerPrice(@RequestBody Map params,HttpServletRequest request) { try { + // 1. 校验token并获取用户 + String token = request.getHeader("token"); + Map userValidation = AppletLoginUtil.validateUserToken(token, usersService); + if (!(Boolean) userValidation.get("valid")) { + return AppletControllerUtil.appletUnauthorized(); + } + Users user = (Users) userValidation.get("user"); + if (user == null) { + return AppletControllerUtil.appletWarning("用户信息获取失败"); + } + Long orderId = params.get("id") != null ? Long.parseLong(params.get("id").toString()) : null; String priceStr = params.get("price") != null ? params.get("price").toString() : null; if (orderId == null || StringUtils.isEmpty(priceStr)) { @@ -5726,15 +5902,32 @@ public class AppletController extends BaseController { return AppletControllerUtil.appletWarning("订单不存在"); } // 查询最新订单日志 - List logList = orderLogService.selectOrderLogByOrderId(order.getOrderId()); - if (logList == null || logList.isEmpty()) { - return AppletControllerUtil.appletWarning("订单日志不存在"); - } - OrderLog latestLog = logList.get(0); - latestLog.setWorkerCost(price); - latestLog.setPrice(price); - latestLog.setPaid(1L); - orderLogService.updateOrderLog(latestLog); + OrderLog neworderLog = orderLogService.selectDataTheFirstNew(order.getId()); + if (neworderLog!=null){ + //修改订单日志添加费用 + neworderLog.setPrice(price); + neworderLog.setPaid(1L); + neworderLog.setWorkerCost(price); + neworderLog.setLogId(GenerateCustomCode.generCreateOrder("FEE")); + //修改订单状态 + order.setJsonStatus(3); + JSONObject jsonObject3 = new JSONObject(); + jsonObject3.put("type", 2); + order.setLogJson(jsonObject3.toJSONString()); + orderService.updateOrder(order); + Users userinfo = usersService.selectUsersById(order.getUid()); + ServiceGoods serviceGoods = serviceGoodsService.selectServiceGoodsById(order.getProductId()); + //给用户发送微信推送消息 + WXsendMsgUtil.sendMsgForUserDoorMoney(userinfo.getOpenid(),order,serviceGoods); } +// List logList = orderLogService.selectOrderLogByOrderId(order.getOrderId()); +// if (logList == null || logList.isEmpty()) { +// return AppletControllerUtil.appletWarning("订单日志不存在"); +// } +// OrderLog latestLog = logList.get(0); +// latestLog.setWorkerCost(price); +// latestLog.setPrice(price); +// latestLog.setPaid(1L); +// orderLogService.updateOrderLog(latestLog); return AjaxResult.success("设置上门费成功"); } catch (Exception e) { return AppletControllerUtil.appletError("设置上门费失败:" + e.getMessage()); @@ -5747,7 +5940,7 @@ public class AppletController extends BaseController { * 逻辑参考OrderController的edit接口中jsonStatus=4的流程 */ @GetMapping("/api/worker/start/door/{id}") - public AjaxResult workerStartDoor(@PathVariable("id") Long id, HttpServletRequest request) { + public AjaxResult workerStartDoor(@PathVariable("id") Long id, HttpServletRequest request) throws Exception { // 1. 获取当前登录师傅ID(token在header) String token = request.getHeader("token"); Map userValidation = AppletLoginUtil.validateUserToken(token, usersService); @@ -5775,26 +5968,27 @@ public class AppletController extends BaseController { // return error("订单进度不正确"); // } - // 3. 更新订单状态 order.setJsonStatus(4); // 出发上门 JSONObject typeJson = new JSONObject(); typeJson.put("type", 3); order.setLogJson(typeJson.toJSONString()); - + // 5. 保存 + orderService.updateOrder(order); // 4. 写订单日志 OrderLog orderLog = new OrderLog(); orderLog.setOid(order.getId()); orderLog.setOrderId(order.getOrderId()); orderLog.setWorkerId(workerId); - orderLog.setTitle("师傅已出发"); + orderLog.setWorkerLogId(workerId); + orderLog.setTitle("出发上门"); + JSONObject typeJson1 = new JSONObject(); + typeJson1.put("name", "师傅收到派单信息准备出发"); orderLog.setType(new BigDecimal("3.0")); - JSONObject content = new JSONObject(); - content.put("name", "师傅已出发"); - orderLog.setContent(content.toJSONString()); - - // 5. 保存 - orderService.updateOrder(order); + orderLog.setContent(typeJson1.toJSONString()); orderLogService.insertOrderLog(orderLog); + ServiceGoods serviceGoods = serviceGoodsService.selectServiceGoodsById(order.getProductId()); + //小程序推送给用户师傅已经出发 + WXsendMsgUtil.sendStartDoorMoney(user.getOpenid(),order,serviceGoods); return AppletControllerUtil.appletSuccess("出发上门成功"); } @@ -5804,7 +5998,7 @@ public class AppletController extends BaseController { * 参数:{id: 订单id, phone_two: 用户手机号后两位} */ @PostMapping("/api/worker/confirm/door") - public AjaxResult workerConfirmDoor(@RequestBody Map params, HttpServletRequest request) { + public AjaxResult workerConfirmDoor(@RequestBody Map params, HttpServletRequest request) throws Exception { // 1. 获取参数 if (params == null || !params.containsKey("id") || !params.containsKey("phone_two")) { return AppletControllerUtil.appletWarning("参数不完整"); @@ -5850,27 +6044,36 @@ public class AppletController extends BaseController { return AppletControllerUtil.appletWarning("手机号后两位不正确"); } + //解绑订单虚拟号 + YunXinPhoneUtilAPI.httpsPrivacyUnbind(order.getWorkerPhone(), order.getUserPhone(), order.getMiddlePhone()); + + // 4. 更新订单状态 order.setJsonStatus(5); // 确认到达 JSONObject typeJson = new JSONObject(); typeJson.put("type", 4); order.setLogJson(typeJson.toJSONString()); + // 6. 保存 + orderService.updateOrder(order); // 5. 写订单日志 OrderLog orderLog = new OrderLog(); orderLog.setOid(order.getId()); orderLog.setOrderId(order.getOrderId()); orderLog.setWorkerId(workerId); - orderLog.setTitle("师傅已到达"); + orderLog.setWorkerLogId(workerId); + orderLog.setTitle("师傅到达"); orderLog.setType(new BigDecimal("4.0")); JSONObject content = new JSONObject(); - content.put("name", "师傅已到达"); + content.put("name", "师傅到达服务地点"); orderLog.setContent(content.toJSONString()); - // 6. 保存 - orderService.updateOrder(order); orderLogService.insertOrderLog(orderLog); - return AppletControllerUtil.appletSuccess("确认到达成功"); + + // 小程序推送给用户师傅已经到达 + ServiceGoods serviceGoods = serviceGoodsService.selectServiceGoodsById(order.getProductId()); + WXsendMsgUtil.sendMsgForWorkerInfo(user.getOpenid(),order,serviceGoods); + return AppletControllerUtil.appletSuccess("师傅已经上门"); } /** @@ -6027,7 +6230,7 @@ public class AppletController extends BaseController { * 参数格式参考json.txt */ @PostMapping("/api/worker/estimate") - public AjaxResult workerEstimate(@RequestBody Map params, HttpServletRequest request) { + public AjaxResult workerEstimate(@RequestBody Map params, HttpServletRequest request) throws Exception { if (params == null) { return AppletControllerUtil.appletError("参数错误"); } @@ -6120,25 +6323,39 @@ public class AppletController extends BaseController { return AppletControllerUtil.appletError("订单不存在"); } order.setJsonStatus(6); - order.setLogJson(contentStr); - order.setTotalPrice(totalPrice); - order.setUpdatedAt(new Date()); + JSONObject jsonObject =new JSONObject(); + jsonObject.put("type", 5); + order.setLogJson(jsonObject.toJSONString()); + // order.setTotalPrice(totalPrice); + // order.setUpdatedAt(new Date()); int update = orderService.updateOrder(order); if (update > 0) { OrderLog log = new OrderLog(); log.setOid(order.getId()); - log.setTitle("已检查评估报价"); - log.setPrice(totalPrice); - log.setPaid(1l); - log.setWorkerCost(totalPrice); - log.setReductionPrice(BigDecimal.ZERO); - log.setWorkerLogId(order.getWorkerId()); - log.setWorkerId(order.getWorkerId()); log.setOrderId(order.getOrderId()); log.setType(new BigDecimal(5)); log.setContent(contentStr); - log.setCreatedAt(new Date()); + log.setTitle("已检查评估报价"); + if (params.get("price")!=null) { + log.setDeposit(new BigDecimal(params.get("price").toString())); + log.setDepPaid(1); + log.setDepLogId(GenerateCustomCode.generCreateOrder("RED")); + } + log.setPrice(totalPrice); + log.setPaid(1l); + if (params.get("reduction")!=null) { + log.setReductionPrice(new BigDecimal(params.get("reduction").toString())); + }else{ + log.setReductionPrice(BigDecimal.ZERO); + } + log.setLogId(GenerateCustomCode.generCreateOrder("EST")); + log.setWorkerLogId(order.getWorkerId()); + log.setWorkerId(order.getWorkerId()); orderLogService.insertOrderLog(log); + Users user = usersService.selectUsersById(order.getUid()); + ServiceGoods serviceGoods = serviceGoodsService.selectServiceGoodsById(order.getProductId()); + //小程序推送用户报价成功 + WXsendMsgUtil.sendWorkerADDmoney(user.getOpenid(), order, serviceGoods); return AppletControllerUtil.appletSuccess("报价成功"); } else { return AppletControllerUtil.appletError("报价失败"); @@ -6268,7 +6485,7 @@ public class AppletController extends BaseController { * 参数:{"id":订单id,"image":[图片url数组]} */ @PostMapping("/api/worker/finish/service") - public AjaxResult workerFinishService(@RequestBody Map params, HttpServletRequest request) { + public AjaxResult workerFinishService(@RequestBody Map params, HttpServletRequest request) throws Exception { if (params == null || params.get("id") == null) { return AppletControllerUtil.appletError("参数错误"); } @@ -6287,7 +6504,6 @@ public class AppletController extends BaseController { order.setReceiveType(3L); // 完成类型 order.setJsonStatus(9); // 完成 order.setLogJson("{\"type\":8}"); - order.setUpdatedAt(new Date()); int update = orderService.updateOrder(order); if (update > 0) { // 2. 组装日志内容 @@ -6305,6 +6521,9 @@ public class AppletController extends BaseController { log.setWorkerId(order.getWorkerId()); log.setCreatedAt(new Date()); orderLogService.insertOrderLog(log); + Users users = usersService.selectUsersById(order.getUid()); + ServiceGoods serviceGoods = serviceGoodsService.selectServiceGoodsById(order.getProductId()); + WXsendMsgUtil.sendWorkerFinishOrder(users.getOpenid(), order, serviceGoods); return AppletControllerUtil.appletSuccess("服务已完成"); } else { return AppletControllerUtil.appletError("操作失败"); @@ -6368,5 +6587,583 @@ public class AppletController extends BaseController { return resp; } + /** + * 抢单大厅 - 获取可抢订单列表 + * + * 功能说明: + * - 查询状态为1(待接单)的订单 + * - 支持分页查询 + * - 组装商品信息和系统配置 + * - 返回标准分页格式数据 + * + * @param params 请求参数,包含分页信息 + * @param request HTTP请求对象 + * @return 抢单大厅订单列表数据 + */ + @PostMapping(value = "/api/worker/grab/lst") + public AjaxResult getWorkerGrabOrderList(@RequestBody Map params, HttpServletRequest request) { + try { + // 1. 解析分页参数 + int page = 1; + int limit = 15; + + if (params.get("page") != null) { + try { + page = Integer.parseInt(params.get("page").toString()); + if (page < 1) page = 1; + } catch (NumberFormatException e) { + page = 1; + } + } + + if (params.get("limit") != null) { + try { + limit = Integer.parseInt(params.get("limit").toString()); + if (limit < 1) limit = 15; + if (limit > 100) limit = 100; // 限制最大每页数量 + } catch (NumberFormatException e) { + limit = 15; + } + } + + // 2. 设置分页 + PageHelper.startPage(page, limit); + + // 3. 查询状态为1(待接单)的订单 + Order orderQuery = new Order(); + orderQuery.setStatus(1L); // 待接单状态 + + List orderList = orderService.selectOrderList(orderQuery); + PageInfo pageInfo = new PageInfo<>(orderList); + + // 4. 组装订单数据 + List> formattedOrderList = new ArrayList<>(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + for (Order order : orderList) { + Map orderData = new HashMap<>(); + + // 基础订单信息 + orderData.put("id", order.getId()); + orderData.put("type", order.getType()); + orderData.put("main_order_id", order.getMainOrderId()); + orderData.put("order_id", order.getOrderId()); + orderData.put("transaction_id", order.getTransactionId() != null ? order.getTransactionId() : ""); + orderData.put("create_type", order.getCreateType()); + orderData.put("create_phone", order.getCreatePhone()); + orderData.put("uid", order.getUid()); + orderData.put("product_id", order.getProductId()); + orderData.put("name", order.getName()); + orderData.put("phone", order.getPhone()); + orderData.put("address", order.getAddress()); + + // 预约时间处理 + String makeTime = ""; + if (order.getMakeTime() != null) { + try { + // 将时间戳转换为日期格式 + Date makeDate = new Date(order.getMakeTime() * 1000L); + SimpleDateFormat makeDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + makeTime = makeDateFormat.format(makeDate); + if (order.getMakeHour() != null) { + makeTime += " " + order.getMakeHour(); + } + } catch (Exception e) { + makeTime = ""; + } + } + orderData.put("make_time", makeTime); + orderData.put("make_hour", order.getMakeHour()); + + orderData.put("num", order.getNum()); + orderData.put("total_price", order.getTotalPrice() != null ? order.getTotalPrice().toString() : "0.00"); + orderData.put("good_price", order.getGoodPrice() != null ? order.getGoodPrice().toString() : "0.00"); + orderData.put("service_price", order.getServicePrice() != null ? order.getServicePrice().toString() : null); + orderData.put("pay_price", order.getPayPrice() != null ? order.getPayPrice().toString() : "0.00"); + orderData.put("coupon_id", order.getCouponId()); + orderData.put("deduction", order.getDeduction() != null ? order.getDeduction().toString() : "0.00"); + orderData.put("pay_time", order.getPayTime()); + orderData.put("status", order.getStatus()); + orderData.put("is_pause", order.getIsPause()); + orderData.put("mark", order.getMark()); + orderData.put("address_id", order.getAddressId()); + orderData.put("sku", order.getSku()); + orderData.put("worker_id", order.getWorkerId()); + orderData.put("first_worker_id", order.getFirstWorkerId()); + orderData.put("receive_time", order.getReceiveTime() != null ? dateFormat.format(order.getReceiveTime()) : null); + orderData.put("is_comment", order.getIsComment()); + orderData.put("receive_type", order.getReceiveType()); + orderData.put("is_accept", order.getIsAccept()); + orderData.put("middle_phone", order.getMiddlePhone()); + orderData.put("user_phone", order.getUserPhone()); + orderData.put("worker_phone", order.getWorkerPhone()); + orderData.put("address_en", order.getAddressEn()); + orderData.put("uid_admin", order.getUidAdmin()); + orderData.put("address_admin", order.getAddressAdmin()); + orderData.put("log_status", order.getLogStatus()); + orderData.put("log_json", order.getLogJson()); + orderData.put("json_status", order.getJsonStatus()); + orderData.put("log_images", order.getLogImages()); + orderData.put("created_at", order.getCreatedAt() != null ? dateFormat.format(order.getCreatedAt()) : null); + orderData.put("updated_at", order.getUpdatedAt() != null ? dateFormat.format(order.getUpdatedAt()) : null); + orderData.put("deleted_at", order.getDeletedAt()); + + // 5. 组装商品信息 + Map productData = new HashMap<>(); + if (order.getProductId() != null) { + try { + ServiceGoods serviceGoods = serviceGoodsService.selectServiceGoodsById(order.getProductId()); + if (serviceGoods != null) { + productData.put("id", serviceGoods.getId()); + productData.put("title", serviceGoods.getTitle()); + productData.put("icon", AppletControllerUtil.buildImageUrl(serviceGoods.getIcon())); + productData.put("price", serviceGoods.getPrice() != null ? serviceGoods.getPrice().toString() : "0.00"); + } else { + // 商品不存在时的默认信息 + productData.put("id", order.getProductId()); + productData.put("title", "商品已下架"); + productData.put("icon", ""); + productData.put("price", "0.00"); + } + } catch (Exception e) { + // 查询异常时的默认信息 + productData.put("id", order.getProductId()); + productData.put("title", "商品信息获取失败"); + productData.put("icon", ""); + productData.put("price", "0.00"); + } + } else { + productData.put("id", null); + productData.put("title", ""); + productData.put("icon", ""); + productData.put("price", "0.00"); + } + orderData.put("product", productData); + + formattedOrderList.add(orderData); + } + + // 6. 构建分页数据 + String baseUrl = request.getRequestURL().toString().split("\\?")[0]; + Map paginationData = AppletControllerUtil.buildPageResult(pageInfo, formattedOrderList, baseUrl); + + // 7. 查询系统配置(config_one) + Map configData = new HashMap<>(); + try { + SiteConfig siteConfigQuery = new SiteConfig(); + siteConfigQuery.setName("config_one"); + List configList = siteConfigService.selectSiteConfigList(siteConfigQuery); + + if (!configList.isEmpty()) { + com.ruoyi.system.domain.SiteConfig config = configList.get(0); + String configValue = config.getValue(); + + if (configValue != null && !configValue.trim().isEmpty()) { + try { + // 尝试解析JSON配置 + JSONObject configJson = JSONObject.parseObject(configValue); + configData.putAll(configJson); + } catch (Exception e) { + // JSON解析失败时的默认配置 + setDefaultConfig(configData); + } + } else { + // 配置值为空时的默认配置 + setDefaultConfig(configData); + } + } else { + // 配置不存在时的默认配置 + setDefaultConfig(configData); + } + } catch (Exception e) { + // 查询配置异常时的默认配置 + setDefaultConfig(configData); + } + + // 8. 构建最终返回数据 + Map responseData = new HashMap<>(); + responseData.put("data", paginationData); + responseData.put("config", configData); + + return AppletControllerUtil.appletSuccess(responseData); + + } catch (Exception e) { + logger.error("获取抢单大厅订单列表异常:", e); + return AppletControllerUtil.appletError("获取抢单大厅订单列表失败:" + e.getMessage()); + } + } + + + + + /** + * 商品订单游标接口 + * + * @param params 请求参数,包含product_id(商品ID)、num(数量)、sku(规格信息) + * @param request HTTP请求对象 + * @return 返回商品ID + *

+ * 接口说明: + * - 接收商品订单相关信息 + * - 调用IGoodsOrderCursorService新增一条数据 + * - 返回商品ID作为响应数据 + *

+ * 请求参数格式: + * { + * "product_id": 2, + * "num": 1, + * "sku": "{\"价格\":\"长青泉 1.5L*6瓶 29.4元\",\"pic\":\"\",\"price\":\"29.4\",\"stock\":\"1000\"}" + * } + *

+ * 返回格式: + * { + * "code": 200, + * "msg": "OK", + * "data": 79 + * } + */ + @PostMapping("/api/service/cursor") + public AjaxResult serviceCursor(@RequestBody Map params, HttpServletRequest request) { + try { + // 1. 参数验证 + if (params == null) { + return error("请求参数不能为空"); + } + + // 验证必要参数 + if (params.get("product_id") == null) { + return error("商品ID不能为空"); + } + + if (params.get("num") == null) { + return error("商品数量不能为空"); + } + + // 2. 解析参数 + Long productId; + Integer num; + String sku = params.get("sku") != null ? params.get("sku").toString() : ""; + + try { + productId = Long.valueOf(params.get("product_id").toString()); + num = Integer.valueOf(params.get("num").toString()); + } catch (NumberFormatException e) { + return error("参数格式错误"); + } + + // 3. 验证参数有效性 + if (productId <= 0) { + return error("商品ID无效"); + } + + if (num <= 0) { + return error("商品数量必须大于0"); + } + + // 4. 验证商品是否存在 + ServiceGoods serviceGoods = serviceGoodsService.selectServiceGoodsById(productId); + if (serviceGoods == null) { + return error("商品不存在"); + } + + // 5. 验证商品状态 + if (serviceGoods.getStatus() == null || !"1".equals(serviceGoods.getStatus())) { + return error("商品已下架或不可购买"); + } + + // 6. 验证库存(如果有库存管理) + if (serviceGoods.getStock() != null && serviceGoods.getStock() < num) { + return error("商品库存不足"); + } + + // 7. 解析并验证SKU信息(如果有的话) + if (sku != null && !sku.trim().isEmpty()) { + try { + JSONObject skuJson = JSONObject.parseObject(sku); + + // 验证SKU中的库存 + if (skuJson.containsKey("stock")) { + String stockStr = skuJson.getString("stock"); + try { + int skuStock = Integer.parseInt(stockStr); + if (skuStock < num) { + return error("SKU库存不足"); + } + } catch (NumberFormatException e) { + // SKU库存格式错误,忽略验证 + } + } + } catch (Exception e) { + // SKU JSON格式错误,但不影响主流程 + } + } + + // 8. 构建GoodsOrderCursor对象 + GoodsOrderCursor goodsOrderCursor = new GoodsOrderCursor(); + goodsOrderCursor.setProductId(productId); + goodsOrderCursor.setType(2l); + goodsOrderCursor.setNum(num.longValue()); + goodsOrderCursor.setSku(sku); + + // 设置其他必要字段 +// goodsOrderCursor.setCreatedAt(new Date()); +// goodsOrderCursor.setUpdatedAt(new Date()); + + // 计算价格(从SKU或商品价格) + BigDecimal totalPrice = serviceGoods.getPrice().multiply(BigDecimal.valueOf(num)); + if (sku != null && !sku.trim().isEmpty()) { + try { + JSONObject skuJson = JSONObject.parseObject(sku); + if (skuJson.containsKey("price")) { + String priceStr = skuJson.getString("price"); + try { + BigDecimal skuPrice = new BigDecimal(priceStr); + totalPrice = skuPrice.multiply(BigDecimal.valueOf(num)); + } catch (NumberFormatException e) { + // SKU价格解析失败,使用商品原价 + totalPrice = serviceGoods.getPrice().multiply(BigDecimal.valueOf(num)); + } + } + } catch (Exception e) { + // SKU解析失败,使用商品原价 + totalPrice = serviceGoods.getPrice().multiply(BigDecimal.valueOf(num)); + } + } + + goodsOrderCursor.setTotalPrice(totalPrice); + // goodsOrderCursor.setProductName(serviceGoods.getTitle()); + + // 9. 调用服务新增数据 + int result = goodsOrderCursorService.insertGoodsOrderCursor(goodsOrderCursor); + + if (result > 0) { + // 10. 返回商品ID + return success(goodsOrderCursor.getId()); + } else { + return error("数据保存失败"); + } + + } catch (Exception e) { + System.err.println("商品订单游标接口异常:" + e.getMessage()); + return error("操作失败:" + e.getMessage()); + } + } + + + /** + * 获取商品订单临时信息 + * + * @param id 订单临时ID + * @param request HTTP请求对象 + * @return 订单临时信息及关联商品信息 + *

+ * 接口说明: + * - 根据ID查询商品订单临时数据 + * - 通过product_id关联查询商品信息 + * - 返回包含订单信息和商品详情的完整数据 + */ + @GetMapping(value = "/api/service/cursor/info/{id}") + public AjaxResult getGoodsOrderCursorInfo(@PathVariable("id") long id, HttpServletRequest request) { + try { + // 参数验证 + if (id <= 0) { + return AppletControllerUtil.appletError("订单ID无效"); + } + + // 1. 查询商品订单临时数据 + GoodsOrderCursor orderCursor = goodsOrderCursorService.selectGoodsOrderCursorById(id); + if (orderCursor == null) { + return AppletControllerUtil.appletError("订单信息不存在"); + } + + // 2. 根据product_id查询商品信息 + ServiceGoods product = null; + if (orderCursor.getProductId() != null) { + product = serviceGoodsService.selectServiceGoodsById(orderCursor.getProductId()); + } + + // 3. 构建返回数据 + Map responseData = new HashMap<>(); + + // 订单基本信息 + responseData.put("id", orderCursor.getId()); + responseData.put("product_id", orderCursor.getProductId()); + responseData.put("type", orderCursor.getType()); + responseData.put("num", orderCursor.getNum()); + responseData.put("sku", orderCursor.getSku()); + responseData.put("total_price", orderCursor.getTotalPrice() != null ? orderCursor.getTotalPrice().toString() : "0.00"); + responseData.put("postage", orderCursor.getPostage() != null ? orderCursor.getPostage().toString() : null); + responseData.put("created_at", AppletControllerUtil.formatDateToString(orderCursor.getCreatedAt())); + responseData.put("updated_at", AppletControllerUtil.formatDateToString(orderCursor.getUpdatedAt())); + + // 4. 添加商品信息 + if (product != null) { + Map productInfo = new HashMap<>(); + productInfo.put("id", product.getId()); + productInfo.put("title", product.getTitle()); + productInfo.put("price", product.getPrice() != null ? product.getPrice().toString() : "0.00"); + productInfo.put("stock", product.getStock()); + productInfo.put("sku_type", product.getSkuType()); + productInfo.put("icon", AppletControllerUtil.buildImageUrl(product.getIcon())); + + responseData.put("product", productInfo); + } else { + // 商品不存在时的默认信息 + Map productInfo = new HashMap<>(); + productInfo.put("id", orderCursor.getProductId()); + productInfo.put("title", "商品已下架"); + productInfo.put("price", "0.00"); + productInfo.put("stock", 0); + productInfo.put("sku_type", 1); + productInfo.put("icon", ""); + + responseData.put("product", productInfo); + } + + return AppletControllerUtil.appletSuccess(responseData); + + } catch (Exception e) { + return AppletControllerUtil.appletError("查询订单信息失败:" + e.getMessage()); + } + } + + + /** + * 微信支付回调通知接口 + * + * @param request HTTP请求对象 + * @return XML格式的响应给微信服务器 + *

+ * 接口说明: + * - 接收微信支付成功后的异步通知 + * - 验证签名确保数据安全性 + * - 处理支付成功后的业务逻辑 + * - 返回XML格式响应告知微信处理结果 + *

+ * 微信支付回调特点: + * - 微信会POST XML数据到此接口 + * - 必须返回XML格式的成功/失败响应 + * - 如果返回失败,微信会重复发送通知 + * - 建议做幂等性处理,避免重复处理同一笔订单 + */ + @PostMapping(value = "/api/wechat/pay/notify") + public String handleWechatPayNotify(HttpServletRequest request) { + try { + // 记录回调日志(可选) + logger.info("收到微信支付回调通知,开始处理..."); + + // 1. 使用WechatPayUtil处理支付回调 + Map notifyResult = wechatPayUtil.handlePayNotify(request); + + // 2. 检查处理结果 + boolean success = (Boolean) notifyResult.get("success"); + String message = (String) notifyResult.get("message"); + + if (success) { + // 3. 获取支付信息 + Map paymentInfo = (Map) notifyResult.get("paymentInfo"); + + // 4. 处理支付成功的业务逻辑 + handlePaymentSuccessLogic(paymentInfo); + + // 5. 记录成功日志 + logger.info("微信支付回调处理成功:订单号={}, 微信交易号={}", + paymentInfo.get("outTradeNo"), paymentInfo.get("transactionId")); + + // 6. 返回成功响应给微信 + return buildSuccessResponse(); + + } else { + // 7. 处理失败情况 + logger.error("微信支付回调处理失败:{}", message); + + // 8. 返回失败响应给微信 + return buildFailResponse(message); + } + + } catch (Exception e) { + // 9. 异常处理 + logger.error("微信支付回调处理异常:", e); + + // 10. 返回异常响应给微信 + return buildFailResponse("处理异常:" + e.getMessage()); + } + } + + /** + * 处理支付成功后的业务逻辑 + * + * @param paymentInfo 支付信息 + */ + private void handlePaymentSuccessLogic(Map paymentInfo) { + try { + String outTradeNo = (String) paymentInfo.get("outTradeNo"); + String transactionId = (String) paymentInfo.get("transactionId"); + String totalFee = (String) paymentInfo.get("totalFee"); + String timeEnd = (String) paymentInfo.get("timeEnd"); + String attach = (String) paymentInfo.get("attach"); + String openid = (String) paymentInfo.get("openid"); + + logger.info("开始处理支付成功业务逻辑:订单号={}, 金额={}", outTradeNo, totalFee); + + // TODO: 根据实际业务需求实现以下逻辑 + // 1. 更新订单状态为已支付 + // 2. 记录支付流水 + // 3. 发送支付成功通知给用户 + // 4. 触发后续业务流程(如发货、服务安排等) + // 5. 更新库存(如果是商品订单) + + // 示例:如果是商品订单临时表的支付 + if (attach != null && attach.startsWith("cursor_order:")) { + String cursorOrderId = attach.replace("cursor_order:", ""); + // 可以在这里处理商品订单临时表的状态更新 + logger.info("处理商品订单临时表支付:ID={}", cursorOrderId); + } + + // 调用AppletControllerUtil的通用支付成功处理方法 + AppletControllerUtil.handlePaymentSuccess(paymentInfo, false); + + logger.info("支付成功业务逻辑处理完成:订单号={}", outTradeNo); + + } catch (Exception e) { + logger.error("处理支付成功业务逻辑异常:", e); + // 注意:即使业务逻辑处理失败,也应该向微信返回成功响应 + // 避免微信重复发送通知,可以通过日志或其他方式处理业务异常 + } + } + + /** + * 构建成功响应XML + */ + private String buildSuccessResponse() { + return "" + + "" + + "" + + ""; + } + + /** + * 构建失败响应XML + */ + private String buildFailResponse(String message) { + return "" + + "" + + "" + + ""; + } + /** + * 设置默认配置信息 + * + * @param configData 配置数据Map + */ + private void setDefaultConfig(Map configData) { + configData.put("phone", "02988256922"); + configData.put("loot_start", "00:59"); + configData.put("loot_end", "23:30"); + configData.put("margin", "10"); + configData.put("integral", "100"); + configData.put("hot", new String[]{"水电维修", "家电清洗", "灯具维修", "墙面翻新", "门窗家具", "改造维修", "防水维修"}); + configData.put("kf", ""); + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/CoursorUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/CoursorUtil.java new file mode 100644 index 0000000..6b0155d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/CoursorUtil.java @@ -0,0 +1,205 @@ +package com.ruoyi.system.controller; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.system.ControllerUtil.AppletControllerUtil; +import com.ruoyi.system.ControllerUtil.WechatPayUtil; +import com.ruoyi.system.domain.ServiceGoods; +import com.ruoyi.system.domain.GoodsOrderCursor; +import com.ruoyi.system.service.IServiceGoodsService; +import com.ruoyi.system.service.IGoodsOrderCursorService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.util.*; + +/** + * 游标工具控制器 + * + * @author Mr. Zhang Pan + * @version 1.0 + * @date 2025-01-10 + */ +@RestController +public class CoursorUtil extends BaseController { + + @Autowired + private IServiceGoodsService serviceGoodsService; + + @Autowired + private IGoodsOrderCursorService goodsOrderCursorService; + + @Autowired + private WechatPayUtil wechatPayUtil; + + /** + * 获取服务商品详细信息 + * + * @param id 商品ID + * @param request HTTP请求对象 + * @return 商品详细信息 + *

+ * 接口说明: + * - 根据商品ID获取详细信息 + * - type=1时返回ServiceGoodsResponse格式 + * - type=2时返回json.txt中的商品格式 + * - 包含图片、基础信息等数组数据 + */ + @GetMapping(value = "/api/service/infoData/id/{id}") + public AjaxResult serviceGoodsDataQuery(@PathVariable("id") long id, HttpServletRequest request) { + try { + // 参数验证 + if (id <= 0) { + return AppletControllerUtil.appletError("商品ID无效"); + } + + // 查询商品信息 + ServiceGoods serviceGoodsData = serviceGoodsService.selectServiceGoodsById(id); + if (serviceGoodsData == null) { + return AppletControllerUtil.appletError("商品不存在或已下架"); + } + + // 根据商品类型返回不同格式的数据 + if (serviceGoodsData.getType() != null && serviceGoodsData.getType().intValue() == 2) { + // type=2时返回json.txt格式 + Map goodsData = AppletControllerUtil.buildType2ServiceGoodsResponse(serviceGoodsData); + return AppletControllerUtil.appletSuccess(goodsData); + } else { + // type=1时返回ServiceGoodsResponse格式 + AppletControllerUtil.ServiceGoodsResponse response = new AppletControllerUtil.ServiceGoodsResponse(serviceGoodsData); + return AppletControllerUtil.appletSuccess(response); + } + } catch (Exception e) { + return AppletControllerUtil.appletError("查询商品详情失败:" + e.getMessage()); + } + } + + /** + * 微信支付回调通知接口 + * + * @param request HTTP请求对象 + * @return XML格式的响应给微信服务器 + *

+ * 接口说明: + * - 接收微信支付成功后的异步通知 + * - 验证签名确保数据安全性 + * - 处理支付成功后的业务逻辑 + * - 返回XML格式响应告知微信处理结果 + *

+ * 微信支付回调特点: + * - 微信会POST XML数据到此接口 + * - 必须返回XML格式的成功/失败响应 + * - 如果返回失败,微信会重复发送通知 + * - 建议做幂等性处理,避免重复处理同一笔订单 + */ + @PostMapping(value = "/api/wechatdata/pay/notify") + public String handleWechatPayNotify(HttpServletRequest request) { + try { + // 记录回调日志(可选) + logger.info("收到微信支付回调通知,开始处理..."); + + // 1. 使用WechatPayUtil处理支付回调 + Map notifyResult = wechatPayUtil.handlePayNotify(request); + + // 2. 检查处理结果 + boolean success = (Boolean) notifyResult.get("success"); + String message = (String) notifyResult.get("message"); + + if (success) { + // 3. 获取支付信息 + Map paymentInfo = (Map) notifyResult.get("paymentInfo"); + + // 4. 处理支付成功的业务逻辑 + handlePaymentSuccessLogic(paymentInfo); + + // 5. 记录成功日志 + logger.info("微信支付回调处理成功:订单号={}, 微信交易号={}", + paymentInfo.get("outTradeNo"), paymentInfo.get("transactionId")); + + // 6. 返回成功响应给微信 + return buildSuccessResponse(); + + } else { + // 7. 处理失败情况 + logger.error("微信支付回调处理失败:{}", message); + + // 8. 返回失败响应给微信 + return buildFailResponse(message); + } + + } catch (Exception e) { + // 9. 异常处理 + logger.error("微信支付回调处理异常:", e); + + // 10. 返回异常响应给微信 + return buildFailResponse("处理异常:" + e.getMessage()); + } + } + + /** + * 处理支付成功后的业务逻辑 + * + * @param paymentInfo 支付信息 + */ + private void handlePaymentSuccessLogic(Map paymentInfo) { + try { + String outTradeNo = (String) paymentInfo.get("outTradeNo"); + String transactionId = (String) paymentInfo.get("transactionId"); + String totalFee = (String) paymentInfo.get("totalFee"); + String timeEnd = (String) paymentInfo.get("timeEnd"); + String attach = (String) paymentInfo.get("attach"); + String openid = (String) paymentInfo.get("openid"); + + logger.info("开始处理支付成功业务逻辑:订单号={}, 金额={}", outTradeNo, totalFee); + + // TODO: 根据实际业务需求实现以下逻辑 + // 1. 更新订单状态为已支付 + // 2. 记录支付流水 + // 3. 发送支付成功通知给用户 + // 4. 触发后续业务流程(如发货、服务安排等) + // 5. 更新库存(如果是商品订单) + + // 示例:如果是商品订单临时表的支付 + if (attach != null && attach.startsWith("cursor_order:")) { + String cursorOrderId = attach.replace("cursor_order:", ""); + // 可以在这里处理商品订单临时表的状态更新 + logger.info("处理商品订单临时表支付:ID={}", cursorOrderId); + } + + // 调用AppletControllerUtil的通用支付成功处理方法 + AppletControllerUtil.handlePaymentSuccess(paymentInfo, false); + + logger.info("支付成功业务逻辑处理完成:订单号={}", outTradeNo); + + } catch (Exception e) { + logger.error("处理支付成功业务逻辑异常:", e); + // 注意:即使业务逻辑处理失败,也应该向微信返回成功响应 + // 避免微信重复发送通知,可以通过日志或其他方式处理业务异常 + } + } + + /** + * 构建成功响应XML + */ + private String buildSuccessResponse() { + return "" + + "" + + "" + + ""; + } + + /** + * 构建失败响应XML + */ + private String buildFailResponse(String message) { + return "" + + "" + + "" + + ""; + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/AppletControllerUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/AppletControllerUtil.java index f8c5ca8..2acbb0e 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/AppletControllerUtil.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/AppletControllerUtil.java @@ -2833,4 +2833,188 @@ public class AppletControllerUtil { } return formattedDate.replace("-", "~"); } + + /** + * 生成自定义前缀+16位唯一数字码 + * 例如:FEEB724145038871780 + * @param prefix 前缀字母(可为null或空,自动生成4位大写字母) + * @return 生成的码 + */ + public static String generateCustomCode(String prefix) { + // 生成前缀 + String codePrefix = prefix; + if (codePrefix == null || codePrefix.trim().isEmpty()) { + // 随机生成4位大写字母 + StringBuilder sb = new StringBuilder(); + Random random = new Random(); + for (int i = 0; i < 4; i++) { + char c = (char) ('A' + random.nextInt(26)); + sb.append(c); + } + codePrefix = sb.toString(); + } + // 生成16位唯一数字 + // 用当前时间戳+3位随机数,保证唯一性和随机性 + long timestamp = System.currentTimeMillis(); + String timePart = String.valueOf(timestamp); + // 补足到16位 + StringBuilder numberPart = new StringBuilder(timePart); + Random random = new Random(); + while (numberPart.length() < 16) { + numberPart.append(random.nextInt(10)); + } + // 如果超出16位则截取 + String finalNumber = numberPart.substring(0, 16); + return codePrefix + finalNumber; + } + + /** + * 构建type=2商品的响应数据(按照json.txt格式) + * + * @param goods ServiceGoods商品对象 + * @return 格式化的商品响应数据 + */ + public static Map buildType2ServiceGoodsResponse(ServiceGoods goods) { + Map data = new HashMap<>(); + + // 基础信息 + data.put("id", goods.getId()); + data.put("title", goods.getTitle()); + data.put("icon", buildImageUrl(goods.getIcon())); + data.put("sub_title", goods.getSubTitle()); + data.put("info", goods.getInfo()); + data.put("price", goods.getPrice() != null ? goods.getPrice().toString() : "0.00"); + data.put("price_zn", goods.getPriceZn()); + data.put("sales", goods.getSales() != null ? goods.getSales().intValue() : 0); + data.put("stock", goods.getStock() != null ? goods.getStock().intValue() : 0); + data.put("status", goods.getStatus() != null ? goods.getStatus() : "1"); + data.put("description", goods.getDescription()); + data.put("sku_type", goods.getSkuType() != null ? goods.getSkuType().intValue() : 1); + data.put("latitude", goods.getLatitude()); + data.put("longitude", goods.getLongitude()); + data.put("type", goods.getType() != null ? goods.getType().intValue() : 2); + data.put("cate_id", goods.getCateId()); + data.put("project", goods.getProject()); + data.put("sort", goods.getSort() != null ? goods.getSort().intValue() : 1); + data.put("material", goods.getMaterial()); + data.put("postage", goods.getPostage() != null ? goods.getPostage().toString() : null); + data.put("basic", parseBasicStringToArray(goods.getBasic())); + data.put("margin", goods.getMargin() != null ? goods.getMargin().toString() : null); + data.put("skill_ids", goods.getSkillIds()); + + // 处理图片数组 + data.put("imgs", parseImagesStringToArray(goods.getImgs())); + + // 处理SKU数据 + data.put("sku", parseSkuStringToObject(goods.getSku())); + + // 时间字段 + data.put("created_at", formatDateToString(goods.getCreatedAt())); + data.put("updated_at", formatDateToString(goods.getUpdatedAt())); + data.put("deleted_at", goods.getDeletedAt() != null ? formatDateToString(goods.getDeletedAt()) : null); + + // 评论对象(预留) + data.put("comment", null); + + return data; + } + + /** + * 解析图片字符串为数组(支持JSON和逗号分隔) + * + * @param imgs 图片字符串 + * @return 图片URL列表 + */ + public static List parseImagesStringToArray(String imgs) { + List imageList = new ArrayList<>(); + if (imgs != null && !imgs.trim().isEmpty()) { + try { + // 尝试解析为JSON数组 + com.alibaba.fastjson2.JSONArray jsonArray = com.alibaba.fastjson2.JSONArray.parseArray(imgs); + for (int i = 0; i < jsonArray.size(); i++) { + String img = jsonArray.getString(i); + if (img != null && !img.trim().isEmpty()) { + imageList.add(buildImageUrl(img)); + } + } + } catch (Exception e) { + // JSON解析失败,尝试按逗号分割 + String[] imgArray = imgs.split("[,,]"); + for (String img : imgArray) { + if (img != null && !img.trim().isEmpty()) { + imageList.add(buildImageUrl(img.trim())); + } + } + } + } + return imageList; + } + + /** + * 解析基础现象字符串为数组 + * + * @param basic 基础现象字符串 + * @return 基础现象列表,如果为空则返回null + */ + public static List parseBasicStringToArray(String basic) { + if (basic == null || basic.trim().isEmpty()) { + return null; + } + + List basicList = new ArrayList<>(); + try { + // 尝试解析为JSON数组 + com.alibaba.fastjson2.JSONArray jsonArray = com.alibaba.fastjson2.JSONArray.parseArray(basic); + for (int i = 0; i < jsonArray.size(); i++) { + String item = jsonArray.getString(i); + if (item != null && !item.trim().isEmpty()) { + basicList.add(item.trim()); + } + } + } catch (Exception e) { + // JSON解析失败,尝试按换行或逗号分割 + String[] basicArray = basic.split("[\\n,,]"); + for (String item : basicArray) { + if (item != null && !item.trim().isEmpty()) { + basicList.add(item.trim()); + } + } + } + return basicList.isEmpty() ? null : basicList; + } + + /** + * 解析SKU字符串为对象 + * + * @param sku SKU字符串 + * @return SKU对象,解析失败返回null + */ + public static Map parseSkuStringToObject(String sku) { + if (sku == null || sku.trim().isEmpty()) { + return null; + } + + try { + // 尝试解析为JSON对象 + com.alibaba.fastjson2.JSONObject skuJson = com.alibaba.fastjson2.JSONObject.parseObject(sku); + return skuJson.toJavaObject(Map.class); + } catch (Exception e) { + // 解析失败,返回null + return null; + } + } + + /** + * 格式化日期为字符串 + * + * @param date 日期对象 + * @return 格式化后的日期字符串,格式为 "yyyy-MM-dd HH:mm:ss" + */ + public static String formatDateToString(java.util.Date date) { + if (date == null) { + return null; + } + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return sdf.format(date); + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/GenerateCustomCode.java b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/GenerateCustomCode.java new file mode 100644 index 0000000..9bb3e64 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/GenerateCustomCode.java @@ -0,0 +1,41 @@ +package com.ruoyi.system.ControllerUtil; + +import java.util.Random; + +public class GenerateCustomCode { + /** + * 生成自定义前缀+16位唯一数字码 + * 例如:FEEB724145038871780 + * @param prefix 前缀字母(可为null或空,自动生成4位大写字母) + * @return 生成的码 + */ + public static String generCreateOrder(String prefix) { + // 生成前缀 + String codePrefix = prefix; + if (codePrefix == null || codePrefix.trim().isEmpty()) { + // 随机生成4位大写字母 + StringBuilder sb = new StringBuilder(); + Random random = new Random(); + for (int i = 0; i < 4; i++) { + char c = (char) ('A' + random.nextInt(26)); + sb.append(c); + } + codePrefix = sb.toString(); + } + // 生成16位唯一数字 + // 用当前时间戳+3位随机数,保证唯一性和随机性 + long timestamp = System.currentTimeMillis(); + String timePart = String.valueOf(timestamp); + // 补足到16位 + StringBuilder numberPart = new StringBuilder(timePart); + Random random = new Random(); + while (numberPart.length() < 16) { + numberPart.append(random.nextInt(10)); + } + // 如果超出16位则截取 + String finalNumber = numberPart.substring(0, 16); + return codePrefix + finalNumber; + } +} + + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/WXsendMsgUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/WXsendMsgUtil.java index a336865..4329b4b 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/WXsendMsgUtil.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/WXsendMsgUtil.java @@ -170,4 +170,126 @@ public class WXsendMsgUtil { return PublicPush(wxMssVo); } + //师傅设置上门费的时候的推送 + public static String sendMsgForUserDoorMoney(String openid, Order order, ServiceGoods serviceGoods) throws Exception { + // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + //拼接推送的模版 + WxMssVo wxMssVo = new WxMssVo(); + wxMssVo.setTouser(openid);//用户的openid(要发送给那个用户,通常这里应该动态传进来的) + wxMssVo.setTemplate_id(ORDER_STATUS);//订阅消息模板id + wxMssVo.setPage("/pages/mine/serveOrder/details?id="+order.getId()); +// String mark = order.getMark(); +// if (StringUtils.isEmpty( mark)){ +// mark="暂无备注"; +// } + Map m = new HashMap<>(3); + m.put("thing15", new TemplateData(serviceGoods.getTitle())); + m.put("character_string5", new TemplateData(order.getOrderId())); + m.put("date3", new TemplateData(AppletControllerUtil.timeStamp2Date(order))); + m.put("thing1", new TemplateData(order.getAddress())); + m.put("thing9", new TemplateData("师傅已设置上门费,请及时支付")); + + System.out.println("师傅设置上门费的时候的推送:" + m.toString()); + wxMssVo.setData(m); + return PublicPush(wxMssVo); + } + + + //出发上门的微信推送 + public static String sendStartDoorMoney(String openid, Order order, ServiceGoods serviceGoods) throws Exception { + // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + //拼接推送的模版 + WxMssVo wxMssVo = new WxMssVo(); + wxMssVo.setTouser(openid);//用户的openid(要发送给那个用户,通常这里应该动态传进来的) + wxMssVo.setTemplate_id(ORDER_STATUS);//订阅消息模板id + wxMssVo.setPage("/pages/mine/serveOrder/details?id="+order.getId()); +// String mark = order.getMark(); +// if (StringUtils.isEmpty( mark)){ +// mark="暂无备注"; +// } + Map m = new HashMap<>(3); + m.put("thing15", new TemplateData(serviceGoods.getTitle())); + m.put("character_string5", new TemplateData(order.getOrderId())); + m.put("date3", new TemplateData(AppletControllerUtil.timeStamp2Date(order))); + m.put("thing1", new TemplateData(order.getAddress())); + m.put("thing9", new TemplateData("师傅已经开始出发上门")); + + System.out.println("师傅设置上门费的时候的推送:" + m.toString()); + wxMssVo.setData(m); + return PublicPush(wxMssVo); + } + + //师傅到达的微信推送 + public static String sendWorkerIsComing(String openid, Order order, ServiceGoods serviceGoods) throws Exception { + // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + //拼接推送的模版 + WxMssVo wxMssVo = new WxMssVo(); + wxMssVo.setTouser(openid);//用户的openid(要发送给那个用户,通常这里应该动态传进来的) + wxMssVo.setTemplate_id(ORDER_STATUS);//订阅消息模板id + wxMssVo.setPage("/pages/mine/serveOrder/details?id="+order.getId()); +// String mark = order.getMark(); +// if (StringUtils.isEmpty( mark)){ +// mark="暂无备注"; +// } + Map m = new HashMap<>(3); + m.put("thing15", new TemplateData(serviceGoods.getTitle())); + m.put("character_string5", new TemplateData(order.getOrderId())); + m.put("date3", new TemplateData(AppletControllerUtil.timeStamp2Date(order))); + m.put("thing1", new TemplateData(order.getAddress())); + m.put("thing9", new TemplateData("师傅已经开始出发上门")); + + System.out.println("师傅设置上门费的时候的推送:" + m.toString()); + wxMssVo.setData(m); + return PublicPush(wxMssVo); + } + + //师傅到达的微信推送 + public static String sendWorkerADDmoney(String openid, Order order, ServiceGoods serviceGoods) throws Exception { + // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + //拼接推送的模版 + WxMssVo wxMssVo = new WxMssVo(); + wxMssVo.setTouser(openid);//用户的openid(要发送给那个用户,通常这里应该动态传进来的) + wxMssVo.setTemplate_id(ORDER_STATUS);//订阅消息模板id + wxMssVo.setPage("/pages/mine/serveOrder/details?id="+order.getId()); +// String mark = order.getMark(); +// if (StringUtils.isEmpty( mark)){ +// mark="暂无备注"; +// } + Map m = new HashMap<>(3); + m.put("thing15", new TemplateData(serviceGoods.getTitle())); + m.put("character_string5", new TemplateData(order.getOrderId())); + m.put("date3", new TemplateData(AppletControllerUtil.timeStamp2Date(order))); + m.put("thing1", new TemplateData(order.getAddress())); + m.put("thing9", new TemplateData("师傅已生成项目费用报价")); + + System.out.println("师傅设置上门费的时候的推送:" + m.toString()); + wxMssVo.setData(m); + return PublicPush(wxMssVo); + } + + + //师傅到达的微信推送 + public static String sendWorkerFinishOrder(String openid, Order order, ServiceGoods serviceGoods) throws Exception { + // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + //拼接推送的模版 + WxMssVo wxMssVo = new WxMssVo(); + wxMssVo.setTouser(openid);//用户的openid(要发送给那个用户,通常这里应该动态传进来的) + wxMssVo.setTemplate_id(ORDER_STATUS);//订阅消息模板id + wxMssVo.setPage("/pages/mine/serveOrder/details?id="+order.getId()); +// String mark = order.getMark(); +// if (StringUtils.isEmpty( mark)){ +// mark="暂无备注"; +// } + Map m = new HashMap<>(3); + m.put("thing15", new TemplateData(serviceGoods.getTitle())); + m.put("character_string5", new TemplateData(order.getOrderId())); + m.put("date3", new TemplateData(AppletControllerUtil.timeStamp2Date(order))); + m.put("thing1", new TemplateData(order.getAddress())); + m.put("thing9", new TemplateData("师傅已经已经接单")); + + System.out.println("师傅设置上门费的时候的推送:" + m.toString()); + wxMssVo.setData(m); + return PublicPush(wxMssVo); + } + } \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/YunXinPhoneUtilAPI.java b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/YunXinPhoneUtilAPI.java index b43e435..bd23b85 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/YunXinPhoneUtilAPI.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/YunXinPhoneUtilAPI.java @@ -38,6 +38,10 @@ public class YunXinPhoneUtilAPI { private static String AXB_CALLBACK_URL = ""; private static String NOTIFY_CALLBACK_URL = ""; + + // 回调地址常量(可通过set方法动态设置) + private static String templateID = "404168"; + /** * 设置AXB绑定的回调URL */ @@ -55,12 +59,12 @@ public class YunXinPhoneUtilAPI { /** * 语音通知接口调用 * @param phone 通知目标手机号 - * @param templateID 语音通知模板ID + * @return VoiceResponseResult 结果对象 * @throws Exception 异常 *

回调地址使用 NOTIFY_CALLBACK_URL 常量,如需动态设置请调用 setNotifyCallbackUrl 方法

*/ - public static VoiceResponseResult httpsAxbTransfer(String phone, String templateID) throws Exception { + public static VoiceResponseResult httpsAxbTransfer(String phone) throws Exception { VoiceResponseResult resultObj = new VoiceResponseResult(); String url = NOTIFY_URL; try { @@ -166,6 +170,7 @@ public class YunXinPhoneUtilAPI { JSONObject rjson = JSONObject.parseObject(result); resultObj.setResult(rjson.getString("result")); resultObj.setMessage(rjson.getString("message")); + } catch (Exception e) { System.out.println("[YunXinPhoneUtilAPI] 解绑异常: " + e.getMessage()); throw e;