ソースを参照

子账号新增

lishiqiang 1 日 前
コミット
08844b0e9a

+ 1 - 0
ship-module-ota/ship-module-ota-api/src/main/java/com/yc/ship/module/ota/enums/ErrorCodeConstants.java

@@ -9,6 +9,7 @@ public interface ErrorCodeConstants {
      **/
     ErrorCode DISTRIBUTOR_TYPE_NOT_EXISTS = new ErrorCode(11_001, "分销商类型不存在");
     ErrorCode DISTRIBUTOR_NOT_EXISTS = new ErrorCode(11_002, "分销商信息不存在");
+    ErrorCode DISTRIBUTOR_SHORT_EXISTS = new ErrorCode(11_002, "分销商子账户已存在");
     ErrorCode DISTRIBUTOR_USER_NOT_EXISTS = new ErrorCode(11_003, "该用户无分销商信息");
     ErrorCode DISTRIBUTOR_LOGINNAME_EXISTS = new ErrorCode(11_002_001, "登录名已经存在");
 

+ 7 - 0
ship-module-ota/ship-module-ota-biz/src/main/java/com/yc/ship/module/ota/controller/admin/distributor/DistributorController.java

@@ -51,6 +51,13 @@ public class DistributorController {
         return success(distributorService.createDistributor(createReqVO));
     }
 
+    @GetMapping("/createCardNo")
+    @Operation(summary = "创建分销商子账户")
+    @PreAuthorize("@ss.hasPermission('ota:distributor:create')")
+    public CommonResult<String> createCardNo(@RequestParam("id") Long id) {
+        return success(distributorService.createCardNo(id));
+    }
+
     @PostMapping("/import")
     @Operation(summary = "导入分销商")
     @Parameters({@Parameter(name = "file", description = "Excel 文件", required = true)})

+ 1 - 0
ship-module-ota/ship-module-ota-biz/src/main/java/com/yc/ship/module/ota/dal/mysql/distributor/DistributorMapper.java

@@ -89,6 +89,7 @@ public interface DistributorMapper extends BaseMapperX<DistributorDO> {
     int addSurplus(@Param("distributorId") Long distributorId, @Param("money")BigDecimal money);
     int addPayCredit(@Param("distributorId") Long distributorId, @Param("money")BigDecimal money);
     int subPayCredit(@Param("distributorId") Long distributorId, @Param("money")BigDecimal money);
+    int updateShortCode(@Param("distributorId") Long distributorId, @Param("code")String code);
 
     int subSurplus(@Param("distributorId")Long distributorId, @Param("money") BigDecimal money);
 

+ 1 - 0
ship-module-ota/ship-module-ota-biz/src/main/java/com/yc/ship/module/ota/service/distributor/DistributorService.java

@@ -27,6 +27,7 @@ public interface DistributorService {
      * @return 编号
      */
     Long createDistributor(@Valid DistributorSaveReqVO createReqVO);
+    String createCardNo(Long id);
     DistributorImportRespVO importDistributorList(List<DistributorImportExcelVO> distributorImportExcelVOS);
 
     /**

+ 161 - 0
ship-module-ota/ship-module-ota-biz/src/main/java/com/yc/ship/module/ota/service/distributor/DistributorServiceImpl.java

@@ -4,6 +4,8 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.XML;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.IdWorker;
@@ -49,7 +51,12 @@ import org.springframework.util.Assert;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.math.BigDecimal;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -185,6 +192,160 @@ public class DistributorServiceImpl implements DistributorService {
         return distributor.getId();
     }
 
+    @Override
+    public String createCardNo(Long id) {
+        DistributorDO distributorDO = distributorMapper.selectById(id);
+        if(distributorDO==null){
+            throw exception(DISTRIBUTOR_NOT_EXISTS);
+        }
+        if(StringUtils.isNotBlank(distributorDO.getCmbShortNo())){
+           throw exception(DISTRIBUTOR_SHORT_EXISTS);
+        }
+        return createChildNo(distributorDO);
+    }
+
+    private String createChildNo(DistributorDO distributor) {
+        String host = "10.1.10.105";
+        int port = 15999;
+        String name = "宜畅式东士煌本孔凰于慊官送";
+        Random random = new Random();
+        int randomNumber = random.nextInt(90000) + 10000;
+        String cardNo = distributor.getUserId().toString()+""+randomNumber;
+        try {
+            // 1. 获取当前时间,用于组装公共包头流水与日期
+            Date now = new Date();
+            String reqDate = new SimpleDateFormat("yyyyMMdd").format(now);
+            String reqTime = new SimpleDateFormat("HHmmss").format(now);
+            // 构造请求方流水号 (保证唯一性)
+            int randomNumber2 = random.nextInt(1000) + 1000;
+            String reqSeqNo = new SimpleDateFormat("yyyyMMddHHmmss").format(now) + ""+randomNumber2+"";
+            // 2. 构造 CMLT40 增加多级账簿 XML 请求报文
+            String xmlData = "<ap>\n" +
+                    "  <CCTransCode>CMLT40</CCTransCode>\n" +
+                    "  <ProductID>ICC</ProductID>\n" +
+                    "  <ChannelType>ERP</ChannelType>\n" +
+                    "  <CorpNo>17999953800000028</CorpNo>\n" +
+                    "  <OpNo></OpNo>\n" +
+                    "  <AuthNo></AuthNo>\n" +
+                    "  <ReqSeqNo>" + reqSeqNo + "</ReqSeqNo>\n" +
+                    "  <ReqDate>" + reqDate + "</ReqDate>\n" +
+                    "  <ReqTime>" + reqTime + "</ReqTime>\n" +
+                    "  <Sign></Sign>\n" +
+                    "  <Corp>\n" +
+                    "    <DbLogAccName>"+name+distributor.getName()+"</DbLogAccName>\n" +
+                    "    <IntTyp>0</IntTyp>\n" +
+                    "    <DbIntType>F</DbIntType>\n" +
+                    "    <DbRit>0.00</DbRit>\n" +
+                    "    <DbAddType>1</DbAddType>\n" +
+                    "    <DbRatType>00002000</DbRatType>\n" +
+                    "    <DbRatPct>0.00</DbRatPct>\n" +
+                    "    <DbRatAdd>0.00</DbRatAdd>\n" +
+                    "    <CrIntType>F</CrIntType>\n" +
+                    "    <CrRit>0.00</CrRit>\n" +
+                    "    <CrAddType>1</CrAddType>\n" +
+                    "    <CrRatType>00002000</CrRatType>\n" +
+                    "    <CrRatPct>0.00</CrRatPct>\n" +
+                    "    <CrRatAdd>0.00</CrRatAdd>\n" +
+                    "    <PmtHd>01</PmtHd>\n" +
+                    "    <ActInf>0</ActInf>\n" +
+                    "  </Corp>\n" +
+                    "  <Cmp>\n" +
+                    "    <DbProv>17</DbProv>\n" +
+                    "    <DbAccNo>342101046484369</DbAccNo>\n" +
+                    "    <DbLogAccNo>"+cardNo+"</DbLogAccNo>\n" +
+                    "    <DbCur>01</DbCur>\n" +
+                    "    <CrLogAccNo>0000000000</CrLogAccNo>\n" +
+                    "    <LogAccBkOInd>0</LogAccBkOInd>\n" +
+                    "  </Cmp>\n" +
+                    "</ap>";
+
+            // 3. 按照约定编码报文 (ERP上送报文必须为GBK字符集)
+            byte[] xmlBytes = xmlData.getBytes("GBK");
+
+            // 4. 构造7字节包头
+            // 第1字节为加密标志(0-不加密),后6字节为数据包长度(包含加密标志位),不足6位在右边用空格补足
+//            int packetLen = xmlBytes.length + 1;
+            int packetLen = xmlBytes.length;
+            String lengthStr = String.format("%-6s", packetLen);
+            String headerStr = "0" + lengthStr;
+            byte[] headerBytes = headerStr.getBytes("GBK");
+
+            // 拼接完整的待发送字节数组
+            byte[] fullPacket = new byte[headerBytes.length + xmlBytes.length];
+            System.arraycopy(headerBytes, 0, fullPacket, 0, headerBytes.length);
+            System.arraycopy(xmlBytes, 0, fullPacket, headerBytes.length, xmlBytes.length);
+
+            // 5. 发送 TCP/IP Socket 同步短连接请求
+            System.out.println("正在连接到前置机 " + host + ":" + port + "...");
+            Socket socket = new Socket(host, port);
+            // 设置读取超时时间为 30 秒
+//            socket.setSoTimeout(30000);
+
+            OutputStream out = socket.getOutputStream();
+            out.write(fullPacket);
+            out.flush();
+            System.out.println("请求报文发送成功,等待响应...\n");
+            System.out.println("请求报文发送成功,等待响应...\n"+new String(fullPacket, "GBK"));
+
+//            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "GBK"));
+//            PrintWriter out2 = new PrintWriter(socket.getOutputStream(), true);
+//
+//            String inputLine;
+//            while ((inputLine = in.readLine()) != null) {
+//                System.out.println("Received: " + inputLine);
+//                out2.println("Echo: " + inputLine); // 回显客户端发送的数据
+//            }
+
+            // 6. 接收并解析前置机返回的报文
+            InputStream in = socket.getInputStream();
+            byte[] buffer = new byte[1024];
+            int bytesRead;
+            // 使用 StringBuilder 拼接读取到的报文内容,必须以 GBK 解码
+            StringBuilder responseBuilder = new StringBuilder();
+
+            while ((bytesRead = in.read(buffer)) != -1) {
+                responseBuilder.append(new String(buffer, 0, bytesRead, "GBK"));
+            }
+
+            socket.close();
+            in.close();
+            out.close();
+
+            String recvData = responseBuilder.toString();
+            if (recvData.length() >= 7) {
+                String respHeader = recvData.substring(0, 7);
+                String respXml = recvData.substring(7);
+                System.out.println("收到前置机响应头: '" + respHeader + "'");
+                System.out.println("收到前置机响应报文:\n" + respXml);
+                JSONObject jsonObject = XML.toJSONObject(respXml);
+                System.out.println(jsonObject);
+
+                JSONObject ap = jsonObject.getJSONObject("ap");
+                String respCode = ap.getStr("RespCode");
+                if(!"0000".equals(respCode)){
+                    System.err.println("交易发送异常,异常信息:"+ap.getStr("RespInfo"));
+                    return "创建子账薄失败:"+ap.getStr("RespInfo");
+                }else {
+                    String resultCode = ap.getJSONObject("Cmp").getStr("DbLogAccNo");
+                    System.err.println("卡号:" + resultCode);
+                    distributorMapper.updateShortCode(distributor.getId(),resultCode);
+                    return "创建子账薄成功:"+resultCode;
+                }
+            } else {
+                System.out.println("响应报文异常或为空");
+                return "创建子账薄失败:响应报文异常或为空";
+            }
+
+        } catch (SocketTimeoutException e) {
+            System.err.println("交易发送超时,请检查ICT前置机通讯状态。");
+            return "创建子账薄失败:交易发送超时";
+        } catch (Exception e) {
+            System.err.println("交易发送异常: " + e.getMessage());
+            e.printStackTrace();
+            return "创建子账薄失败:"+e.getMessage();
+        }
+    }
+
     @Override
     public DistributorImportRespVO importDistributorList(List<DistributorImportExcelVO> distributorImportExcelVOS) {
         for (DistributorImportExcelVO distributorImportExcelVO : distributorImportExcelVOS) {

+ 6 - 0
ship-module-ota/ship-module-ota-biz/src/main/resources/mapper/distributor/DistributorMapper.xml

@@ -101,6 +101,12 @@
         where id = #{distributorId} and pay_credit >= #{money}
     </update>
 
+    <update id="updateShortCode">
+        update ota_distributor
+        set cmb_short_no = #{code}
+        where id = #{distributorId} and cmb_short_no is null
+    </update>
+
     <update id="subSurplus">
         update ota_distributor set surplus = surplus - #{money} where id =#{distributorId}
     </update>

+ 145 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/utils/CMLT40Demo.java

@@ -0,0 +1,145 @@
+package com.yc.ship.module.trade.utils;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.XML;
+
+import java.io.*;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+
+public class CMLT40Demo {
+
+    public static void main(String[] args) {
+        String host = "10.3.10.50";
+        int port = 15999;
+
+        try {
+            // 1. 获取当前时间,用于组装公共包头流水与日期
+            Date now = new Date();
+            String reqDate = new SimpleDateFormat("yyyyMMdd").format(now);
+            String reqTime = new SimpleDateFormat("HHmmss").format(now);
+            // 构造请求方流水号 (保证唯一性)
+            String reqSeqNo = new SimpleDateFormat("yyyyMMddHHmmss").format(now) + "0001";
+
+            // 2. 构造 CMLT40 增加多级账簿 XML 请求报文
+            String xmlData = "<ap>\n" +
+                    "  <CCTransCode>CMLT40</CCTransCode>\n" +
+                    "  <ProductID>ICC</ProductID>\n" +
+                    "  <ChannelType>ERP</ChannelType>\n" +
+                    "  <CorpNo>17999953800000028</CorpNo>\n" +
+                    "  <OpNo></OpNo>\n" +
+                    "  <AuthNo></AuthNo>\n" +
+                    "  <ReqSeqNo>" + reqSeqNo + "</ReqSeqNo>\n" +
+                    "  <ReqDate>" + reqDate + "</ReqDate>\n" +
+                    "  <ReqTime>" + reqTime + "</ReqTime>\n" +
+                    "  <Sign></Sign>\n" +
+                    "  <Corp>\n" +
+                    "    <DbLogAccName>宜畅式东士煌本孔凰于慊官送测试子账号2</DbLogAccName>\n" +
+                    "    <IntTyp>0</IntTyp>\n" +
+                    "    <DbIntType>F</DbIntType>\n" +
+                    "    <DbRit>0.00</DbRit>\n" +
+                    "    <DbAddType>1</DbAddType>\n" +
+                    "    <DbRatType>00002000</DbRatType>\n" +
+                    "    <DbRatPct>0.00</DbRatPct>\n" +
+                    "    <DbRatAdd>0.00</DbRatAdd>\n" +
+                    "    <CrIntType>F</CrIntType>\n" +
+                    "    <CrRit>0.00</CrRit>\n" +
+                    "    <CrAddType>1</CrAddType>\n" +
+                    "    <CrRatType>00002000</CrRatType>\n" +
+                    "    <CrRatPct>0.00</CrRatPct>\n" +
+                    "    <CrRatAdd>0.00</CrRatAdd>\n" +
+                    "    <PmtHd>01</PmtHd>\n" +
+                    "    <ActInf>0</ActInf>\n" +
+                    "  </Corp>\n" +
+                    "  <Cmp>\n" +
+                    "    <DbProv>17</DbProv>\n" +
+                    "    <DbAccNo>342101046484369</DbAccNo>\n" +
+                    "    <DbLogAccNo>0000000006</DbLogAccNo>\n" +
+                    "    <DbCur>01</DbCur>\n" +
+                    "    <CrLogAccNo>0000000000</CrLogAccNo>\n" +
+                    "    <LogAccBkOInd>0</LogAccBkOInd>\n" +
+                    "  </Cmp>\n" +
+                    "</ap>";
+
+            // 3. 按照约定编码报文 (ERP上送报文必须为GBK字符集)
+            byte[] xmlBytes = xmlData.getBytes("GBK");
+
+            // 4. 构造7字节包头
+            // 第1字节为加密标志(0-不加密),后6字节为数据包长度(包含加密标志位),不足6位在右边用空格补足
+//            int packetLen = xmlBytes.length + 1;
+            int packetLen = xmlBytes.length;
+            String lengthStr = String.format("%-6s", packetLen);
+            String headerStr = "0" + lengthStr;
+            byte[] headerBytes = headerStr.getBytes("GBK");
+
+            // 拼接完整的待发送字节数组
+            byte[] fullPacket = new byte[headerBytes.length + xmlBytes.length];
+            System.arraycopy(headerBytes, 0, fullPacket, 0, headerBytes.length);
+            System.arraycopy(xmlBytes, 0, fullPacket, headerBytes.length, xmlBytes.length);
+
+            // 5. 发送 TCP/IP Socket 同步短连接请求
+            System.out.println("正在连接到前置机 " + host + ":" + port + "...");
+            Socket socket = new Socket(host, port);
+            // 设置读取超时时间为 30 秒
+//            socket.setSoTimeout(30000);
+
+            OutputStream out = socket.getOutputStream();
+            out.write(fullPacket);
+            out.flush();
+            System.out.println("请求报文发送成功,等待响应...\n");
+            System.out.println("请求报文发送成功,等待响应...\n"+new String(fullPacket, "GBK"));
+
+//            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "GBK"));
+//            PrintWriter out2 = new PrintWriter(socket.getOutputStream(), true);
+//
+//            String inputLine;
+//            while ((inputLine = in.readLine()) != null) {
+//                System.out.println("Received: " + inputLine);
+//                out2.println("Echo: " + inputLine); // 回显客户端发送的数据
+//            }
+
+            // 6. 接收并解析前置机返回的报文
+            InputStream in = socket.getInputStream();
+            byte[] buffer = new byte[1024];
+            int bytesRead;
+            // 使用 StringBuilder 拼接读取到的报文内容,必须以 GBK 解码
+            StringBuilder responseBuilder = new StringBuilder();
+
+            while ((bytesRead = in.read(buffer)) != -1) {
+                responseBuilder.append(new String(buffer, 0, bytesRead, "GBK"));
+            }
+
+            socket.close();
+            in.close();
+            out.close();
+
+            String recvData = responseBuilder.toString();
+            if (recvData.length() >= 7) {
+                String respHeader = recvData.substring(0, 7);
+                String respXml = recvData.substring(7);
+                System.out.println("收到前置机响应头: '" + respHeader + "'");
+                System.out.println("收到前置机响应报文:\n" + respXml);
+                JSONObject jsonObject = XML.toJSONObject(respXml);
+                System.out.println(jsonObject);
+                JSONObject ap = jsonObject.getJSONObject("ap");
+                String respCode = ap.getStr("RespCode");
+                if(!"0000".equals(respCode)){
+                    System.err.println("交易发送异常,异常信息:"+ap.getStr("RespInfo"));
+                }else {
+                    String cardNo = ap.getJSONObject("Cmp").getStr("DbLogAccNo");
+                    System.err.println("卡号:" + cardNo);
+                }
+            } else {
+                System.out.println("响应报文异常或为空");
+            }
+
+        } catch (SocketTimeoutException e) {
+            System.err.println("交易发送超时,请检查ICT前置机通讯状态。");
+        } catch (Exception e) {
+            System.err.println("交易发送异常: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}