Blame view

service-acct/src/main/java/com/java110/acct/cmd/alipay/PayTempCarFeeCmd.java 13.1 KB
88e030b7   王彪总   init project
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
  package com.java110.acct.cmd.alipay;
  
  import com.alibaba.fastjson.JSONArray;
  import com.alibaba.fastjson.JSONObject;
  import com.alipay.api.AlipayApiException;
  import com.alipay.api.AlipayClient;
  import com.alipay.api.DefaultAlipayClient;
  import com.alipay.api.request.AlipayTradeCreateRequest;
  import com.alipay.api.response.AlipayTradeCreateResponse;
  import com.java110.core.annotation.Java110Cmd;
  import com.java110.core.context.ICmdDataFlowContext;
  import com.java110.core.event.cmd.Cmd;
  import com.java110.core.event.cmd.CmdEvent;
  import com.java110.core.factory.CommunitySettingFactory;
  import com.java110.core.factory.GenerateCodeFactory;
  import com.java110.dto.fee.FeeDto;
  import com.java110.dto.owner.OwnerCarOpenUserDto;
  import com.java110.dto.parking.ParkingAreaDto;
  import com.java110.dto.wechat.SmallWeChatDto;
  import com.java110.intf.community.IParkingAreaV1InnerServiceSMO;
  import com.java110.intf.fee.ITempCarFeeCreateOrderV1InnerServiceSMO;
  import com.java110.intf.store.ISmallWechatV1InnerServiceSMO;
  import com.java110.intf.user.IOwnerCarOpenUserV1InnerServiceSMO;
  import com.java110.po.owner.OwnerCarOpenUserPo;
  import com.java110.utils.cache.CommonCache;
  import com.java110.utils.cache.MappingCache;
  import com.java110.utils.exception.CmdException;
  import com.java110.utils.util.Assert;
  import com.java110.utils.util.DateUtil;
  import com.java110.vo.ResultVo;
  import org.apache.commons.lang.StringUtils;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.http.HttpStatus;
  import org.springframework.http.ResponseEntity;
  
  import java.text.ParseException;
  import java.util.ArrayList;
  import java.util.List;
  
  /**
   * 支付宝临时停车费支付命令类
   * 处理临时车辆停车费的支付宝支付流程,包括订单创建、支付处理、回调通知等
   * 主要功能包括:
   * 1. 验证支付请求参数
   * 2. 创建停车费订单
   * 3. 调用支付宝支付接口
   * 4. 处理支付结果和回调
   * 5. 记录用户车辆关联信息
   * 
   * @author Java110
   * @version 1.0
   * @since 2023
   */
  @Java110Cmd(serviceCode = "alipay.payTempCarFee")
  public class PayTempCarFeeCmd extends Cmd {
  
      /** 车主开放用户信息服务接口 */
      @Autowired
      private IOwnerCarOpenUserV1InnerServiceSMO ownerCarOpenUserV1InnerServiceSMOImpl;
  
      /** 停车场区域信息服务接口 */
      @Autowired
      private IParkingAreaV1InnerServiceSMO parkingAreaV1InnerServiceSMOImpl;
  
      /** 微信小程序信息服务接口 */
      @Autowired
      private ISmallWechatV1InnerServiceSMO smallWechatV1InnerServiceSMOImpl;
  
      /** 临时车辆费用创建订单服务接口 */
      @Autowired
      private ITempCarFeeCreateOrderV1InnerServiceSMO tempCarFeeCreateOrderV1InnerServiceSMOImpl;
  
      /**
       * 验证请求参数
       * 检查请求报文中是否包含必要的参数信息
       *
       * @param event   命令事件对象,包含事件相关信息
       * @param context 命令数据流上下文,用于获取和设置上下文数据
       * @param reqJson 请求JSON对象,包含客户端请求的所有参数
       * @throws CmdException 当参数验证失败时抛出异常
       */
      @Override
      public void validate(CmdEvent event, ICmdDataFlowContext context, JSONObject reqJson) throws CmdException {
          // 验证请求报文中必须包含的关键参数
          Assert.jsonObjectHaveKey(reqJson, "carNum", "请求报文中未包含房屋信息节点");
          Assert.jsonObjectHaveKey(reqJson, "appId", "请求报文中未包含appId节点");
          Assert.jsonObjectHaveKey(reqJson, "openId", "请求报文中未包含openId节点");
          Assert.jsonObjectHaveKey(reqJson, "paId", "请求报文中未包含paId节点");
          Assert.jsonObjectHaveKey(reqJson, "inoutId", "请求报文中未包含inoutId节点");
          Assert.jsonObjectHaveKey(reqJson, "couponList", "请求报文中未包含couponList节点");
      }
  
      /**
       * 执行临时停车费支付命令
       * 处理停车费订单创建、支付流程、结果回调等完整业务流程
       *
       * @param event   命令事件对象,包含事件相关信息
       * @param context 命令数据流上下文,用于获取和设置上下文数据
       * @param reqJson 请求JSON对象,包含客户端请求的所有参数
       * @throws CmdException    命令执行异常,当业务逻辑执行失败时抛出
       * @throws ParseException  数据解析异常,当数据格式不正确时抛出
       */
      @Override
      public void doCmd(CmdEvent event, ICmdDataFlowContext context, JSONObject reqJson) throws CmdException, ParseException {
          ResponseEntity responseEntity = null;
  
          // 根据停车场ID查询小区ID
          ParkingAreaDto parkingAreaDto = new ParkingAreaDto();
          parkingAreaDto.setPaId(reqJson.getString("paId"));
          List<ParkingAreaDto> parkingAreaDtos = parkingAreaV1InnerServiceSMOImpl.queryParkingAreas(parkingAreaDto);
  
          // 验证停车场存在且唯一
          Assert.listOnlyOne(parkingAreaDtos, "停车场不存在");
          // 将小区ID添加到请求参数中,供后续使用
          reqJson.put("communityId", parkingAreaDtos.get(0).getCommunityId());
  
          // 处理优惠券列表
          JSONArray couponList = reqJson.getJSONArray("couponList");
          List<String> couponIds = new ArrayList<String>();
          if (couponList != null && couponList.size() > 0) {
              // 遍历优惠券列表,提取所有优惠券ID
              for (int couponIndex = 0; couponIndex < couponList.size(); couponIndex++) {
                  couponIds.add(couponList.getJSONObject(couponIndex).getString("couponId"));
              }
          }
  
          // 设置默认用户ID,用于订单创建
          reqJson.put("userId", "-1");
  
          // 构建订单创建参数
          JSONObject paramIn = new JSONObject();
          paramIn.put("paId", reqJson.getString("paId"));
          paramIn.put("carNum", reqJson.getString("carNum"));
          paramIn.put("machineId", reqJson.getString("machineId"));
          paramIn.put("couponIds", StringUtils.join(couponIds, ","));
          
          // 调用订单创建服务,生成停车费订单
          responseEntity = tempCarFeeCreateOrderV1InnerServiceSMOImpl.createOrder(paramIn);
  
          // 检查订单创建结果,如果创建失败则直接返回错误响应
          if (responseEntity.getStatusCode() != HttpStatus.OK) {
              context.setResponseEntity(responseEntity);
              return;
          }
          
          // 解析订单信息,获取订单详情
          JSONObject orderInfo = JSONObject.parseObject(responseEntity.getBody().toString());
          if (orderInfo.getIntValue("code") != 0) {
              throw new IllegalArgumentException("缴费失败");
          }
          JSONObject fee = orderInfo.getJSONObject("data");
  
          // 获取实际支付金额和订单ID
          double money = fee.getDouble("receivedAmount");
          String orderId = fee.getString("oId");
          
          // 处理金额为0的情况(免费订单)
          if (money <= 0) {
              JSONObject paramOut = new JSONObject();
              paramOut.put("oId", orderId);
              // 对于免费订单,直接调用回调通知接口
              responseEntity = tempCarFeeCreateOrderV1InnerServiceSMOImpl.notifyOrder(paramIn);
              JSONObject param = new JSONObject();
              if (responseEntity.getStatusCode() != HttpStatus.OK) {
                  param.put("code", "101");
                  param.put("msg", "扣费为0回调失败");
                  context.setResponseEntity(new ResponseEntity(JSONObject.toJSONString(param), HttpStatus.OK));
                  return;
              }
              param.put("code", "100");
              param.put("msg", "扣费为0回调成功");
              context.setResponseEntity(new ResponseEntity(JSONObject.toJSONString(param), HttpStatus.OK));
              return;
          }
          
          // 调用支付宝支付接口进行实际支付
          String openId = reqJson.getString("openId");
          ResultVo result = doAlipay(reqJson, paramIn.getString("feeName"), orderId, money, openId);
  
          responseEntity = new ResponseEntity(JSONObject.toJSONString(result), HttpStatus.OK);
          context.setResponseEntity(responseEntity);
          if (!"0".equals(result.getCode())) {
              return;
          }
          
          // 保存支付信息到缓存,用于后续查询和回调处理
          JSONObject saveFees = new JSONObject();
          saveFees.put("orderId", paramIn.getString("inoutId"));
          saveFees.put("carNum", paramIn.getString("carNum"));
          saveFees.put("amount", money);
          saveFees.put("paId", paramIn.getString("paId"));
          saveFees.put("payTime", DateUtil.getNow(DateUtil.DATE_FORMATE_STRING_A));
          saveFees.put("payType", "2"); // 支付类型:2表示支付宝支付
          CommonCache.setValue(FeeDto.REDIS_PAY_TEMP_CAR_FEE + orderId, saveFees.toJSONString(), CommonCache.PAY_DEFAULT_EXPIRE_TIME);
          CommonCache.setValue(FeeDto.REDIS_PAY_TEMP_CAR_FEE_COMMUNITY + orderId, reqJson.getString("communityId"), CommonCache.PAY_DEFAULT_EXPIRE_TIME);
          
          // 记录用户与车辆的关联关系,方便下次使用
          OwnerCarOpenUserPo ownerCarOpenUserPo = new OwnerCarOpenUserPo();
          ownerCarOpenUserPo.setCarNum(reqJson.getString("carNum"));
          ownerCarOpenUserPo.setNickname("未获取");
          ownerCarOpenUserPo.setHeadimgurl("为获取");
          ownerCarOpenUserPo.setOpenId(openId);
          ownerCarOpenUserPo.setOpenType(OwnerCarOpenUserDto.OPEN_TYPE_ALIPAY);
          ownerCarOpenUserPo.setOpenUserId(GenerateCodeFactory.getGeneratorId("10"));
          ownerCarOpenUserV1InnerServiceSMOImpl.saveOwnerCarOpenUser(ownerCarOpenUserPo);
      }
  
      /**
       * 执行支付宝支付流程
       * 创建支付宝交易订单并调用支付宝API进行支付
       *
       * @param reqJson  请求JSON对象,包含社区ID等配置信息
       * @param feeName  费用名称,用于支付宝订单标题
       * @param orderId  订单ID,作为支付宝商户订单号
       * @param money    支付金额,单位元
       * @param openId   用户支付宝openId,用于标识支付用户
       * @return ResultVo 支付结果对象,包含支付状态码和消息
       */
      private ResultVo doAlipay(JSONObject reqJson, String feeName, String orderId, double money, String openId) {
          // 初始化支付宝客户端,使用社区配置的支付宝参数
          AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
                  CommunitySettingFactory.getValue(reqJson.getString("communityId"), "APP_ID"),
                  CommunitySettingFactory.getRemark(reqJson.getString("communityId"), "APP_PRIVATE_KEY"),
                  "json",
                  "UTF-8",
                  CommunitySettingFactory.getRemark(reqJson.getString("communityId"), "ALIPAY_PUBLIC_KEY"),
                  "RSA2");
          
          // 创建支付宝交易请求
          AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
          // 设置支付宝回调通知地址
          request.setNotifyUrl(MappingCache.getValue("ALIPAY", "temp"));
          
          // 构建业务参数
          JSONObject bizContent = new JSONObject();
          bizContent.put("out_trade_no", orderId);      // 商户订单号,使用系统生成的订单ID
          bizContent.put("total_amount", money);        // 订单总金额,单位元
          bizContent.put("subject", feeName);           // 订单标题,显示给用户的费用名称
          bizContent.put("buyer_id", openId);           // 买家支付宝用户ID
          bizContent.put("timeout_express", "10m");     // 订单超时时间,10分钟
          request.setBizContent(bizContent.toString());
          
          AlipayTradeCreateResponse response = null;
          try {
              // 执行支付宝API调用,创建交易订单
              response = alipayClient.execute(request);
              if (response.isSuccess()) {
                  // 支付成功,返回成功结果
                  return new ResultVo(ResultVo.CODE_OK, ResultVo.MSG_OK, orderId);
              } else {
                  // 支付失败,返回错误信息
                  return new ResultVo(ResultVo.CODE_ERROR, response.getMsg());
              }
          } catch (AlipayApiException e) {
              throw new CmdException("支付宝下单失败" + e);
          }
      }
  
      /**
       * 获取小程序微信配置信息
       * 根据小区ID和应用ID查询对应的微信小程序配置
       *
       * @param paramIn 输入参数对象,包含小区ID和应用ID
       * @return SmallWeChatDto 微信小程序配置信息,未找到时返回null
       */
      private SmallWeChatDto getSmallWechat(JSONObject paramIn) {
          SmallWeChatDto smallWeChatDto = new SmallWeChatDto();
          smallWeChatDto.setAppId(paramIn.getString("appId"));
          smallWeChatDto.setObjId(paramIn.getString("communityId"));
          smallWeChatDto.setRow(1);
          smallWeChatDto.setPage(1);
          
          // 查询微信小程序配置
          List<SmallWeChatDto> smallWeChatDtos = smallWechatV1InnerServiceSMOImpl.querySmallWechats(smallWeChatDto);
  
          if (smallWeChatDtos == null || smallWeChatDtos.size() < 1) {
              return null;
          }
  
          return smallWeChatDtos.get(0);
      }
  }