Procházet zdrojové kódy

<feat>:新增
1、新增srm退回

wanghaiwu před 1 měsícem
rodič
revize
e30d4d841e

+ 42 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/form/PublicReimburseBillSRMEditPlugin.java

@@ -0,0 +1,42 @@
+package nckd.jimin.jyyy.fi.plugin.form;
+
+import com.alibaba.druid.util.StringUtils;
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.entity.operate.result.IOperateInfo;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+
+import java.util.List;
+
+public class PublicReimburseBillSRMEditPlugin extends AbstractBillPlugIn {
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        super.afterDoOperation(afterDoOperationEventArgs);
+
+        String opKey = afterDoOperationEventArgs.getOperateKey();
+        if(StringUtils.equals("nckd_returnsrm", opKey)){
+            OperationResult operationResult = afterDoOperationEventArgs.getOperationResult();
+            operationResult.setShowMessage(false);
+            String message = operationResult.getMessage();
+
+            List<IOperateInfo> errorInfos = operationResult.getAllErrorOrValidateInfo();
+            boolean isSuccess = operationResult.isSuccess();
+            if(errorInfos.size()>0){
+                operationResult.setSuccess(false);
+                StringBuffer msg = new StringBuffer();
+                for (IOperateInfo errInfo: errorInfos) {
+                    msg.append(errInfo.getMessage()).append("\r\n");
+                }
+                message = message + "\r\n" + msg.toString();
+            }
+
+            this.getView().showTipNotification(message);
+        }
+        if(StringUtils.equals("pay",afterDoOperationEventArgs.getOperateKey())){
+
+        }
+        if(StringUtils.equals("cancelpay",afterDoOperationEventArgs.getOperateKey())){
+
+        }
+    }
+}

+ 14 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/operate/PrepayBillOpPlugin.java

@@ -0,0 +1,14 @@
+package nckd.jimin.jyyy.fi.plugin.operate;
+
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+
+/**
+ * 表单标识:预付单(nckd_er_prepaybill_ext)
+ * @author wanghaiwu_kd
+ * @date 2025/04/29
+ */
+public class PrepayBillOpPlugin extends AbstractOperationServicePlugIn {
+    private static final Log logger = LogFactory.getLog(PrepayBillOpPlugin.class);
+}

+ 110 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/operate/PublicReimBillReturnOpPlugin.java

@@ -0,0 +1,110 @@
+package nckd.jimin.jyyy.fi.plugin.operate;
+
+import com.kingdee.util.StringUtils;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.AddValidatorsEventArgs;
+import kd.bos.entity.plugin.PreparePropertysEventArgs;
+import kd.bos.entity.plugin.args.AfterOperationArgs;
+import kd.bos.entity.plugin.args.BeforeOperationArgs;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.bos.entity.plugin.args.EndOperationTransactionArgs;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 表单标识:对公报销单(nckd_er_publicreimbur_ext)
+ * @author wanghaiwu_kd
+ * @date 2025/05/12
+ */
+public class PublicReimBillReturnOpPlugin extends AbstractOperationServicePlugIn {
+    private static final Log logger = LogFactory.getLog(PublicReimBillReturnOpPlugin.class);
+
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        super.onPreparePropertys(e);
+
+        List<String> fieldKeys = e.getFieldKeys();
+
+        fieldKeys.add("billno");
+        fieldKeys.add("billstatus");
+        fieldKeys.add("nckd_srmbillno");
+        fieldKeys.add("nckd_srmurl");
+    }
+
+    public void onAddValidators(AddValidatorsEventArgs e) {
+        super.onAddValidators(e);
+    }
+
+    @Override
+    public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
+        super.beforeExecuteOperationTransaction(e);
+
+        StringBuffer errMessage = new StringBuffer();
+
+        DynamicObject[] billEntities = e.getDataEntities();
+        String operationKey = e.getOperationKey();
+
+        logger.info("开始执行操作:{},实体:{}", operationKey, billEntities);
+
+        if (StringUtils.equals( "nckd_returnsrm", operationKey)){//退回
+            for (DynamicObject billInfo : billEntities) {
+                String billno = billInfo.getString("billno");
+                String billstatus = billInfo.getString("billstatus");
+                String smrBillNo = billInfo.getString("nckd_srmbillno");
+
+                if(!"A".equals(billstatus) || StringUtils.isEmpty(smrBillNo)){
+                    if(errMessage.length() > 0){
+                        errMessage.append(",");
+                    }
+                    errMessage.append(billno);
+                }
+
+                Map<String, String> returnMap = SRMHelperUtils.writeBackSRMWfStatus(billInfo.getDataEntityType().getName(), billInfo.getLong("id"), operationKey);
+            }
+
+//            if(errMessage.length() > 0) {
+                ////将错误信息返回到前端
+                String msg = "以下单据状态不是暂存状态或不是SRM生成的单据,不允许退回!" + errMessage.toString();
+                e.setCancelMessage(msg);
+                e.setCancel(true);
+
+                logger.info("PublicReimBillReturnOpPlugin 退回失败:" + msg);
+//            }
+        }
+    }
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        super.beginOperationTransaction(e);
+    }
+
+    @Override
+    public void endOperationTransaction(EndOperationTransactionArgs e) {
+        super.endOperationTransaction(e);
+    }
+
+    @Override
+    public void afterExecuteOperationTransaction(AfterOperationArgs args) {
+        super.afterExecuteOperationTransaction(args);
+
+        String operationKey = args.getOperationKey();
+        DynamicObject[] billEntities = args.getDataEntities();
+        logger.info("开始执行操作:{},实体:{}", operationKey, billEntities);
+
+        switch (operationKey) {
+            case "nckd_return"://退回,只有保存状态的单据可以退回
+                for (DynamicObject billInfo : billEntities) {
+                    if(!"A".equals(billInfo.getString("billstatus"))){
+                        String billno = billInfo.getString("billno");
+                    }
+
+                    billInfo.set("billstatus", "H");
+                }
+                break;
+            default:
+                break;
+        }
+    }
+}

+ 715 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/operate/SRMBase64Util.java

@@ -0,0 +1,715 @@
+package nckd.jimin.jyyy.fi.plugin.operate;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+//import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+public class SRMBase64Util
+{
+    private static class DecInputStream extends InputStream
+    {
+
+        public int read()
+            throws IOException
+        {
+            return read(sbBuf, 0, 1) != -1 ? sbBuf[0] & 255 : -1;
+        }
+
+        public int read(byte abyte0[], int i, int j)
+            throws IOException
+        {
+            if(closed)
+                throw new IOException("Stream is closed");
+            if(eof && nextout < 0)
+                return -1;
+            if(i < 0 || j < 0 || j > abyte0.length - i)
+                throw new IndexOutOfBoundsException();
+            int k = i;
+            if(nextout >= 0)
+            {
+                do
+                {
+                    if(j == 0)
+                        return i - k;
+                    abyte0[i++] = (byte)(bits >> nextout);
+                    j--;
+                    nextout -= 8;
+                } while(nextout >= 0);
+                bits = 0;
+            }
+            do
+            {
+                if(j <= 0)
+                    break;
+                int l = is.read();
+                if(l == -1)
+                {
+                    eof = true;
+                    if(nextin != 18)
+                    {
+                        if(nextin == 12)
+                            throw new IOException("Base64 stream has one un-decoded dangling byte.");
+                        abyte0[i++] = (byte)(bits >> 16);
+                        j--;
+                        if(nextin == 0)
+                            if(j == 0)
+                            {
+                                bits >>= 8;
+                                nextout = 0;
+                            } else
+                            {
+                                abyte0[i++] = (byte)(bits >> 8);
+                            }
+                    }
+                    if(i == k)
+                        return -1;
+                    else
+                        return i - k;
+                }
+                if(l == 61)
+                {
+                    if(nextin == 18 || nextin == 12 || nextin == 6 && is.read() != 61)
+                        throw new IOException((new StringBuilder()).append("Illegal base64 ending sequence:").append(nextin).toString());
+                    abyte0[i++] = (byte)(bits >> 16);
+                    j--;
+                    if(nextin == 0)
+                        if(j == 0)
+                        {
+                            bits >>= 8;
+                            nextout = 0;
+                        } else
+                        {
+                            abyte0[i++] = (byte)(bits >> 8);
+                        }
+                    eof = true;
+                    break;
+                }
+                if((l = base64[l]) == -1)
+                {
+                    if(!isMIME)
+                        throw new IOException((new StringBuilder()).append("Illegal base64 character ").append(Integer.toString(l, 16)).toString());
+                    continue;
+                }
+                bits |= l << nextin;
+                if(nextin == 0)
+                {
+                    nextin = 18;
+                    for(nextout = 16; nextout >= 0;)
+                    {
+                        abyte0[i++] = (byte)(bits >> nextout);
+                        j--;
+                        nextout -= 8;
+                        if(j == 0 && nextout >= 0)
+                            return i - k;
+                    }
+
+                    bits = 0;
+                    continue;
+                }
+                nextin -= 6;
+            } while(true);
+            return i - k;
+        }
+
+        public int available()
+            throws IOException
+        {
+            if(closed)
+                throw new IOException("Stream is closed");
+            else
+                return is.available();
+        }
+
+        public void close()
+            throws IOException
+        {
+            if(!closed)
+            {
+                closed = true;
+                is.close();
+            }
+        }
+
+        private final InputStream is;
+        private final boolean isMIME;
+        private final int base64[];
+        private int bits;
+        private int nextin;
+        private int nextout;
+        private boolean eof;
+        private boolean closed;
+        private byte sbBuf[];
+
+        DecInputStream(InputStream inputstream, int ai[], boolean flag)
+        {
+            bits = 0;
+            nextin = 18;
+            nextout = -8;
+            eof = false;
+            closed = false;
+            sbBuf = new byte[1];
+            is = inputstream;
+            base64 = ai;
+            isMIME = flag;
+        }
+    }
+
+    public static class Decoder
+    {
+
+        public byte[] decode(byte abyte0[])
+        {
+            byte abyte1[] = new byte[outLength(abyte0, 0, abyte0.length)];
+            int i = decode0(abyte0, 0, abyte0.length, abyte1);
+            if(i != abyte1.length)
+                abyte1 = Arrays.copyOf(abyte1, i);
+            return abyte1;
+        }
+
+        public byte[] decode(String s) throws UnsupportedEncodingException
+        {
+            return decode(s.getBytes("ISO-8859-1"));//StandardCharsets.ISO_8859_1
+        }
+
+        public int decode(byte abyte0[], byte abyte1[])
+        {
+            int i = outLength(abyte0, 0, abyte0.length);
+            if(abyte1.length < i)
+                throw new IllegalArgumentException("Output byte array is too small for decoding all input bytes");
+            else
+                return decode0(abyte0, 0, abyte0.length, abyte1);
+        }
+
+        public ByteBuffer decode(ByteBuffer bytebuffer)
+        {
+            int i = bytebuffer.position();
+            try
+            {
+                byte abyte0[];
+                int j;
+                int k;
+                if(bytebuffer.hasArray())
+                {
+                    abyte0 = bytebuffer.array();
+                    j = bytebuffer.arrayOffset() + bytebuffer.position();
+                    k = bytebuffer.arrayOffset() + bytebuffer.limit();
+                    bytebuffer.position(bytebuffer.limit());
+                } else
+                {
+                    abyte0 = new byte[bytebuffer.remaining()];
+                    bytebuffer.get(abyte0);
+                    j = 0;
+                    k = abyte0.length;
+                }
+                byte abyte1[] = new byte[outLength(abyte0, j, k)];
+                return ByteBuffer.wrap(abyte1, 0, decode0(abyte0, j, k, abyte1));
+            }
+            catch(IllegalArgumentException illegalargumentexception)
+            {
+                bytebuffer.position(i);
+                throw illegalargumentexception;
+            }
+        }
+
+        public InputStream wrap(InputStream inputstream)
+        {
+            requireNonNull(inputstream);
+            return new DecInputStream(inputstream, isURL ? fromBase64URL : fromBase64, isMIME);
+        }
+
+        private int outLength(byte abyte0[], int i, int j)
+        {
+            int ai[] = isURL ? fromBase64URL : fromBase64;
+            int k = 0;
+            int l = j - i;
+            if(l == 0)
+                return 0;
+            if(l < 2)
+                if(isMIME && ai[0] == -1)
+                    return 0;
+                else
+                    throw new IllegalArgumentException("Input byte[] should at least have 2 bytes for base64 bytes");
+            if(isMIME)
+            {
+                int i1 = 0;
+                do
+                {
+                    if(i >= j)
+                        break;
+                    int j1 = abyte0[i++] & 255;
+                    if(j1 == 61)
+                    {
+                        l -= (j - i) + 1;
+                        break;
+                    }
+                    if((j1 = ai[j1]) == -1)
+                        i1++;
+                } while(true);
+                l -= i1;
+            } else
+            if(abyte0[j - 1] == 61)
+            {
+                k++;
+                if(abyte0[j - 2] == 61)
+                    k++;
+            }
+            if(k == 0 && (l & 3) != 0)
+                k = 4 - (l & 3);
+            return 3 * ((l + 3) / 4) - k;
+        }
+
+        private int decode0(byte abyte0[], int i, int j, byte abyte1[])
+        {
+            int ai[] = isURL ? fromBase64URL : fromBase64;
+            int k = 0;
+            int l = 0;
+            int i1 = 18;
+            do
+            {
+                if(i >= j)
+                    break;
+                int j1 = abyte0[i++] & 255;
+                if((j1 = ai[j1]) < 0)
+                {
+                    if(j1 == -2)
+                    {
+                        if(i1 == 6 && (i == j || abyte0[i++] != 61) || i1 == 18)
+                            throw new IllegalArgumentException("Input byte array has wrong 4-byte ending unit");
+                        break;
+                    }
+                    if(!isMIME)
+                        throw new IllegalArgumentException((new StringBuilder()).append("Illegal base64 character ").append(Integer.toString(abyte0[i - 1], 16)).toString());
+                } else
+                {
+                    l |= j1 << i1;
+                    if((i1 -= 6) < 0)
+                    {
+                        abyte1[k++] = (byte)(l >> 16);
+                        abyte1[k++] = (byte)(l >> 8);
+                        abyte1[k++] = (byte)l;
+                        i1 = 18;
+                        l = 0;
+                    }
+                }
+            } while(true);
+            if(i1 == 6)
+                abyte1[k++] = (byte)(l >> 16);
+            else
+            if(i1 == 0)
+            {
+                abyte1[k++] = (byte)(l >> 16);
+                abyte1[k++] = (byte)(l >> 8);
+            } else
+            if(i1 == 12)
+                throw new IllegalArgumentException("Last unit does not have enough valid bits");
+            while(i < j) 
+                if(!isMIME || ai[abyte0[i++]] >= 0)
+                    throw new IllegalArgumentException((new StringBuilder()).append("Input byte array has incorrect ending byte at ").append(i).toString());
+            return k;
+        }
+
+        private final boolean isURL;
+        private final boolean isMIME;
+        private static final int fromBase64[];
+        private static final int fromBase64URL[];
+        static final Decoder RFC4648 = new Decoder(false, false);
+        static final Decoder RFC4648_URLSAFE = new Decoder(true, false);
+        static final Decoder RFC2045 = new Decoder(false, true);
+
+        static 
+        {
+            fromBase64 = new int[256];
+            Arrays.fill(fromBase64, -1);
+            for(int i = 0; i < Encoder.toBase64.length; i++)
+                fromBase64[Encoder.toBase64[i]] = i;
+
+            fromBase64[61] = -2;
+            fromBase64URL = new int[256];
+            Arrays.fill(fromBase64URL, -1);
+            for(int j = 0; j < Encoder.toBase64URL.length; j++)
+                fromBase64URL[Encoder.toBase64URL[j]] = j;
+
+            fromBase64URL[61] = -2;
+        }
+
+
+        private Decoder(boolean flag, boolean flag1)
+        {
+            isURL = flag;
+            isMIME = flag1;
+        }
+    }
+
+    private static class EncOutputStream extends FilterOutputStream
+    {
+
+        public void write(int i)
+            throws IOException
+        {
+            byte abyte0[] = new byte[1];
+            abyte0[0] = (byte)(i & 255);
+            write(abyte0, 0, 1);
+        }
+
+        private void checkNewline()
+            throws IOException
+        {
+            if(linepos == linemax)
+            {
+                out.write(newline);
+                linepos = 0;
+            }
+        }
+
+        public void write(byte abyte0[], int i, int j)
+            throws IOException
+        {
+            if(closed)
+                throw new IOException("Stream is closed");
+            if(i < 0 || j < 0 || j > abyte0.length - i)
+                throw new ArrayIndexOutOfBoundsException();
+            if(j == 0)
+                return;
+            if(leftover != 0)
+            {
+                if(leftover == 1)
+                {
+                    b1 = abyte0[i++] & 255;
+                    if(--j == 0)
+                    {
+                        leftover++;
+                        return;
+                    }
+                }
+                b2 = abyte0[i++] & 255;
+                j--;
+                checkNewline();
+                out.write(base64[b0 >> 2]);
+                out.write(base64[b0 << 4 & 63 | b1 >> 4]);
+                out.write(base64[b1 << 2 & 63 | b2 >> 6]);
+                out.write(base64[b2 & 63]);
+                linepos += 4;
+            }
+            int k = j / 3;
+            leftover = j - k * 3;
+            while(k-- > 0) 
+            {
+                checkNewline();
+                int l = (abyte0[i++] & 255) << 16 | (abyte0[i++] & 255) << 8 | abyte0[i++] & 255;
+                out.write(base64[l >>> 18 & 63]);
+                out.write(base64[l >>> 12 & 63]);
+                out.write(base64[l >>> 6 & 63]);
+                out.write(base64[l & 63]);
+                linepos += 4;
+            }
+            if(leftover == 1)
+                b0 = abyte0[i++] & 255;
+            else
+            if(leftover == 2)
+            {
+                b0 = abyte0[i++] & 255;
+                b1 = abyte0[i++] & 255;
+            }
+        }
+
+        public void close()
+            throws IOException
+        {
+            if(!closed)
+            {
+                closed = true;
+                if(leftover == 1)
+                {
+                    checkNewline();
+                    out.write(base64[b0 >> 2]);
+                    out.write(base64[b0 << 4 & 63]);
+                    if(doPadding)
+                    {
+                        out.write(61);
+                        out.write(61);
+                    }
+                } else
+                if(leftover == 2)
+                {
+                    checkNewline();
+                    out.write(base64[b0 >> 2]);
+                    out.write(base64[b0 << 4 & 63 | b1 >> 4]);
+                    out.write(base64[b1 << 2 & 63]);
+                    if(doPadding)
+                        out.write(61);
+                }
+                leftover = 0;
+                out.close();
+            }
+        }
+
+        private int leftover;
+        private int b0;
+        private int b1;
+        private int b2;
+        private boolean closed;
+        private final char base64[];
+        private final byte newline[];
+        private final int linemax;
+        private final boolean doPadding;
+        private int linepos;
+
+        EncOutputStream(OutputStream outputstream, char ac[], byte abyte0[], int i, boolean flag)
+        {
+            super(outputstream);
+            leftover = 0;
+            closed = false;
+            linepos = 0;
+            base64 = ac;
+            newline = abyte0;
+            linemax = i;
+            doPadding = flag;
+        }
+    }
+
+    public static class Encoder
+    {
+
+        private final int outLength(int i)
+        {
+            int j = 0;
+            if(doPadding)
+            {
+                j = 4 * ((i + 2) / 3);
+            } else
+            {
+                int k = i % 3;
+                j = 4 * (i / 3) + (k != 0 ? k + 1 : 0);
+            }
+            if(linemax > 0)
+                j += ((j - 1) / linemax) * newline.length;
+            return j;
+        }
+
+        public byte[] encode(byte abyte0[])
+        {
+            int i = outLength(abyte0.length);
+            byte abyte1[] = new byte[i];
+            int j = encode0(abyte0, 0, abyte0.length, abyte1);
+            if(j != abyte1.length)
+                return Arrays.copyOf(abyte1, j);
+            else
+                return abyte1;
+        }
+
+        public int encode(byte abyte0[], byte abyte1[])
+        {
+            int i = outLength(abyte0.length);
+            if(abyte1.length < i)
+                throw new IllegalArgumentException("Output byte array is too small for encoding all input bytes");
+            else
+                return encode0(abyte0, 0, abyte0.length, abyte1);
+        }
+
+        public String encodeToString(byte abyte0[])
+        {
+            byte abyte1[] = encode(abyte0);
+            return new String(abyte1, 0, 0, abyte1.length);
+        }
+
+        public ByteBuffer encode(ByteBuffer bytebuffer)
+        {
+            int i = outLength(bytebuffer.remaining());
+            byte abyte0[] = new byte[i];
+            int j = 0;
+            if(bytebuffer.hasArray())
+            {
+                j = encode0(bytebuffer.array(), bytebuffer.arrayOffset() + bytebuffer.position(), bytebuffer.arrayOffset() + bytebuffer.limit(), abyte0);
+                bytebuffer.position(bytebuffer.limit());
+            } else
+            {
+                byte abyte1[] = new byte[bytebuffer.remaining()];
+                bytebuffer.get(abyte1);
+                j = encode0(abyte1, 0, abyte1.length, abyte0);
+            }
+            if(j != abyte0.length)
+                abyte0 = Arrays.copyOf(abyte0, j);
+            return ByteBuffer.wrap(abyte0);
+        }
+
+        public OutputStream wrap(OutputStream outputstream)
+        {
+            requireNonNull(outputstream);
+            return new EncOutputStream(outputstream, isURL ? toBase64URL : toBase64, newline, linemax, doPadding);
+        }
+
+        public Encoder withoutPadding()
+        {
+            if(!doPadding)
+                return this;
+            else
+                return new Encoder(isURL, newline, linemax, false);
+        }
+
+        private int encode0(byte abyte0[], int i, int j, byte abyte1[])
+        {
+            char ac[] = isURL ? toBase64URL : toBase64;
+            int k = i;
+            int l = ((j - i) / 3) * 3;
+            int i1 = i + l;
+            if(linemax > 0 && l > (linemax / 4) * 3)
+                l = (linemax / 4) * 3;
+            int j1 = 0;
+            do
+            {
+                if(k >= i1)
+                    break;
+                int k1 = Math.min(k + l, i1);
+                int i2 = k;
+                int k2 = j1;
+                while(i2 < k1) 
+                {
+                    int l2 = (abyte0[i2++] & 255) << 16 | (abyte0[i2++] & 255) << 8 | abyte0[i2++] & 255;
+                    abyte1[k2++] = (byte)ac[l2 >>> 18 & 63];
+                    abyte1[k2++] = (byte)ac[l2 >>> 12 & 63];
+                    abyte1[k2++] = (byte)ac[l2 >>> 6 & 63];
+                    abyte1[k2++] = (byte)ac[l2 & 63];
+                }
+                i2 = ((k1 - k) / 3) * 4;
+                j1 += i2;
+                k = k1;
+                if(i2 == linemax && k < j)
+                {
+                    byte abyte2[] = newline;
+                    int i3 = abyte2.length;
+                    int j3 = 0;
+                    while(j3 < i3) 
+                    {
+                        byte byte0 = abyte2[j3];
+                        abyte1[j1++] = byte0;
+                        j3++;
+                    }
+                }
+            } while(true);
+            if(k < j)
+            {
+                int l1 = abyte0[k++] & 255;
+                abyte1[j1++] = (byte)ac[l1 >> 2];
+                if(k == j)
+                {
+                    abyte1[j1++] = (byte)ac[l1 << 4 & 63];
+                    if(doPadding)
+                    {
+                        abyte1[j1++] = 61;
+                        abyte1[j1++] = 61;
+                    }
+                } else
+                {
+                    int j2 = abyte0[k++] & 255;
+                    abyte1[j1++] = (byte)ac[l1 << 4 & 63 | j2 >> 4];
+                    abyte1[j1++] = (byte)ac[j2 << 2 & 63];
+                    if(doPadding)
+                        abyte1[j1++] = 61;
+                }
+            }
+            return j1;
+        }
+
+        private final byte newline[];
+        private final int linemax;
+        private final boolean isURL;
+        private final boolean doPadding;
+        private static final char toBase64[] = {
+            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 
+            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 
+            'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 
+            'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
+            'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 
+            'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', 
+            '8', '9', '+', '/'
+        };
+        private static final char toBase64URL[] = {
+            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 
+            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 
+            'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 
+            'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
+            'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 
+            'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', 
+            '8', '9', '-', '_'
+        };
+        private static final int MIMELINEMAX = 76;
+        private static final byte CRLF[] = {
+            13, 10
+        };
+        static final Encoder RFC4648 = new Encoder(false, null, -1, true);
+        static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
+        static final Encoder RFC2045 = new Encoder(false, CRLF, 76, true);
+
+
+
+
+        private Encoder(boolean flag, byte abyte0[], int i, boolean flag1)
+        {
+            isURL = flag;
+            newline = abyte0;
+            linemax = i;
+            doPadding = flag1;
+        }
+
+    }
+
+
+    private SRMBase64Util()
+    {
+    }
+
+    public static Encoder getEncoder()
+    {
+        return Encoder.RFC4648;
+    }
+
+    public static Encoder getUrlEncoder()
+    {
+        return Encoder.RFC4648_URLSAFE;
+    }
+
+    public static Encoder getMimeEncoder()
+    {
+        return Encoder.RFC2045;
+    }
+
+    public static Encoder getMimeEncoder(int i, byte abyte0[])
+    {
+        requireNonNull(abyte0);
+        int ai[] = Decoder.fromBase64;
+        byte abyte1[] = abyte0;
+        int j = abyte1.length;
+        for(int k = 0; k < j; k++)
+        {
+            byte byte0 = abyte1[k];
+            if(ai[byte0 & 255] != -1)
+                throw new IllegalArgumentException((new StringBuilder()).append("Illegal base64 line separator character 0x").append(Integer.toString(byte0, 16)).toString());
+        }
+
+        if(i <= 0)
+            return Encoder.RFC4648;
+        else
+            return new Encoder(false, abyte0, (i >> 2) << 2, true);
+    }
+    public static Object requireNonNull(Object obj)
+    {
+        if(obj == null)
+            throw new NullPointerException();
+        else
+            return obj;
+    }
+    public static Decoder getDecoder()
+    {
+        return Decoder.RFC4648;
+    }
+
+    public static Decoder getUrlDecoder()
+    {
+        return Decoder.RFC4648_URLSAFE;
+    }
+
+    public static Decoder getMimeDecoder()
+    {
+        return Decoder.RFC2045;
+    }
+}

+ 396 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/operate/SRMHelperUtils.java

@@ -0,0 +1,396 @@
+package nckd.jimin.jyyy.fi.plugin.operate;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.sdk.util.KHttpClientUtils;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.bos.util.StringUtils;
+import nckd.base.helper.CommonHelperUtils;
+import okhttp3.*;
+import sun.misc.BASE64Encoder;
+
+import javax.crypto.Cipher;
+import java.io.IOException;
+import java.security.KeyFactory;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 插件说明:SRM系统帮助类
+ * @author wanghaiwu_kd
+ * @date 2025/05/12
+ */
+public class SRMHelperUtils {
+    private static final Log logger = LogFactory.getLog(SRMHelperUtils.class);
+
+    private static int timeout = 180;//超时时间(次/秒)
+
+    /**
+     *
+     * @param entityName
+     * @param billId
+     * @param operatorType
+     * @return
+     */
+    public static Map<String, String> writeBackSRMWfStatus(String entityName, Long billId, String operatorType){
+        Map<String, String> returnMap = new HashMap<>();
+        if(StringUtils.isEmpty(entityName) || billId == null || billId == 0L){
+            returnMap.put("code", "1");
+            returnMap.put("msg", "回写失败,参数为空");
+
+            return returnMap;
+        }
+
+        String selectProperties = "id, billno, billstatus, nckd_srmbillno, nckd_srmurl";
+        DynamicObject billInfo = BusinessDataServiceHelper.loadSingle(billId, entityName, selectProperties);
+
+        billInfo.set("billstatus", "A");
+
+        SaveServiceHelper.update(new DynamicObject[]{billInfo});
+
+//        if(true) {
+//            return returnMap;
+//        }
+
+        //退回
+        if("nckd_returnsrm".equals(operatorType)){
+            //判断单据状态是否等于暂存
+            String billStatus = billInfo.getString("billstatus");
+            if(!"A".equals(billStatus)){
+                returnMap.put("code", "1");
+                returnMap.put("msg", "退回失败,单据不是暂存状态");
+
+                return returnMap;
+            }
+            String srmBillNo = billInfo.getString("nckd_srmbillno");
+            if(StringUtils.isEmpty(srmBillNo)){
+                returnMap.put("code", "1");
+                returnMap.put("msg", "退回失败,单据(" + billInfo.getString("billno") + ")不是由SRM系统生成");
+
+                return returnMap;
+            }
+
+            String srmurl = billInfo.getString("nckd_srmurl");
+            if(StringUtils.isEmpty(srmurl)){
+                returnMap.put("code", "1");
+                returnMap.put("msg", "退回失败,单据(" + billInfo.getString("billno") + ")的srm单点url为空");
+
+                return returnMap;
+            }
+
+            Map<String,String> doReturnMap = doSRMreturnDeal(srmBillNo);
+
+            //退回成功后单点到srm
+            if (!"0".equals(doReturnMap.get("code"))) {//调接口异常
+                return doReturnMap;
+            } else {//调接口成功后,获取单点登录链接
+                Map<String,String> ssomap = buildSSOUrl(srmurl);
+
+                return ssomap;
+            }
+        }
+
+        returnMap.put("code", "1");
+        returnMap.put("msg", "回写失败");
+
+        return returnMap;
+    }
+
+    /**
+     * 退回处理
+     * @param srmBillNo
+     * @return
+     */
+    public static Map<String,String> doSRMreturnDeal(String srmBillNo) {
+        Map<String,String> returnMap = new HashMap<>();
+        //获取token
+        Map<String,String> tokenMap = getSRMToken();
+        if (!"0".equals(tokenMap.get("code"))) {//未获取到token
+            returnMap.put("code", "1");
+            returnMap.put("msg", tokenMap.get("msg"));
+
+            return returnMap;
+        }
+        String token = tokenMap.get("msg").toString();
+
+        Map<String, String> mapentity = CommonHelperUtils.getCommonParams("SRM");
+        if(mapentity == null ){
+            logger.info("SRMHelperUtils:nckd_entryentity is null");
+
+            returnMap.put("code", "1");
+            returnMap.put("msg", "未配置SRM系统对接参数!");
+            return returnMap;
+        }
+
+        String serverUrlApi = mapentity.get("apiserverurl");
+        String writeBillStatusApi = mapentity.get("apiwritebillstatus");
+        String userName = mapentity.get("username");//用户名(srm提供)
+        String interfaceCode = mapentity.get("interfacecodewritebillstatus");//接口编码(srm提供,不同接口编码不同)
+        String externalSysCode = mapentity.get("externalsyscode");//外部系统(srm提供)
+        String applicationCode = mapentity.get("applicationcode");//应用(srm提供)
+        String applicationGroupCode = mapentity.get("applicationgroupcode");//应用组(srm提供)
+
+        if(StringUtils.isEmpty(serverUrlApi) || StringUtils.isEmpty(writeBillStatusApi) || StringUtils.isEmpty(userName)
+                || StringUtils.isEmpty(interfaceCode) ||StringUtils.isEmpty(externalSysCode)
+                || StringUtils.isEmpty(applicationCode) || StringUtils.isEmpty(applicationGroupCode)){
+            returnMap.put("code", "1");
+            returnMap.put("msg", "请检查是否配置SRM参数:服务地址、回写审批状态接口、用户名、接口编码、外部系统、应用编码、应用组");
+            return returnMap;
+        }
+
+        String url = serverUrlApi + writeBillStatusApi;
+        String timestamp = String.valueOf(System.currentTimeMillis());
+
+        JSONObject headerObj = new JSONObject();
+        headerObj.put("applicationCode", applicationCode);
+        headerObj.put("applicationGroupCode", applicationGroupCode);
+        headerObj.put("batchNum", timestamp);
+        headerObj.put("externalSystemCode", externalSysCode);
+        headerObj.put("interfaceCode", interfaceCode);
+        headerObj.put("userName", userName);
+
+        JSONArray bodyArr = new JSONArray();
+        JSONObject bodyObj = new JSONObject();
+
+        bodyObj.put("settleNum", srmBillNo);//SRM单据编号
+        bodyObj.put("settleStatus", "REJECTED");//APPROVED 已审批 / REJECTED 已拒绝
+        bodyObj.put("documentType", "PAYMENT");//PAYMENT 付款   PREPAYMENT 预付款
+        bodyObj.put("approvedRemark", "退回");//审批意见
+        bodyObj.put("settleReversalLines","[{\"lineNum\":1}]");//结算单行
+        bodyArr.add(bodyObj);
+
+        JSONObject body=new JSONObject();
+        body.put("header", headerObj);
+        body.put("body", bodyArr);
+
+        try {
+            String response = doHttpClientPost(url,  body.toJSONString(), token);
+            if(StringUtils.isEmpty(response)){
+                returnMap.put("code", "1");
+                returnMap.put("msg", "审批回传失败, 返回为空");
+
+                return returnMap;
+            }
+
+            JSONObject resultJSON = JSONObject.parseObject(response);
+
+            if (resultJSON !=null && "SUCCESS".equals(resultJSON.get("responseStatus").toString())
+                    &&"SUCCESS".equals(resultJSON.get("executeResult").toString())) {
+                returnMap.put("code","0");
+            } else{
+                returnMap.put("code","1");
+            }
+
+            returnMap.put("msg", response);
+
+            return returnMap;
+        } catch(IOException e){
+            returnMap.put("code", "1");
+            returnMap.put("msg", "获取token失败, " + e.getMessage());
+
+            return returnMap;
+        } catch(Exception e){
+            returnMap.put("code", "1");
+            returnMap.put("msg", "获取token失败, " + e.getMessage());
+
+            return returnMap;
+        }
+    }
+
+    /**
+     * 构造单点url
+     * @param srmUrl
+     * @return
+     */
+    public static Map<String,String> buildSSOUrl(String srmUrl) {
+        Map<String,String> returnMap = new HashMap<>();
+
+        Map<String, String> mapentity = CommonHelperUtils.getCommonParams("SRM");
+        if(mapentity == null ){
+            logger.info("SRMHelperUtils:nckd_entryentity is null");
+
+            returnMap.put("code", "1");
+            returnMap.put("msg", "未配置SRM系统对接参数!");
+            return returnMap;
+        }
+
+        String serverUrl = mapentity.get("serverurl");//服务地址
+        String ssoUrl = mapentity.get("ssourl");//单点地址
+        String privateKey = mapentity.get("ssoprivate");//单点私钥
+
+        if(StringUtils.isEmpty(serverUrl) || StringUtils.isEmpty(ssoUrl) || StringUtils.isEmpty(privateKey)){
+            returnMap.put("code", "1");
+            returnMap.put("msg", "请检查是否配置SRM参数:服务地址、单点地址、单点私钥");
+            return returnMap;
+        }
+
+        DynamicObject  userInfo =  UserServiceHelper.getUserInfoByID(UserServiceHelper.getCurrentUserId(),"number,username");
+        String userNumber = userInfo.getString("number");
+
+        try {
+            String userName = encrypt(userNumber, privateKey);
+            String url = serverUrl + ssoUrl + "?username=" + userName + "&redirectUri=" + srmUrl;
+
+            returnMap.put("code", "0");
+            returnMap.put("msg", url);
+
+            return returnMap;
+        } catch(Exception e){
+            returnMap.put("code", "1");
+            returnMap.put("msg", e.getMessage());
+            return returnMap;
+        }
+    }
+
+    /**
+     * 对用户加密
+     * @param content
+     * @param keyStr
+     * @return
+     * @throws Exception
+     */
+    public static String encrypt(String content, String keyStr) throws Exception {
+        long nowTime = System.currentTimeMillis(); //获取当前时间戳拼接加密,加密内容使用有效期3分钟
+        String contentWithTime = content + "|" + String.valueOf(nowTime);
+        Cipher cipher = Cipher.getInstance("RSA");
+        RSAPrivateKey privateKey = getPrivateKey(keyStr);
+        cipher.init(1, privateKey);
+        return SRMBase64Util.getUrlEncoder().encodeToString(cipher.doFinal(contentWithTime.getBytes("UTF-8")));//StandardCharsets.UTF_8
+
+    }
+
+    /**
+     * 获取私钥
+     * @param privateKey
+     * @return
+     * @throws Exception
+     */
+    private static RSAPrivateKey getPrivateKey(String privateKey) throws  Exception {
+        try {
+            BASE64Encoder encoder = new BASE64Encoder();
+            return (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(SRMBase64Util.getDecoder().decode(privateKey)));
+        } catch (Exception var2) {
+            System.out.println("RSA get Private Key failed!"+var2.toString());
+            throw new Exception(var2);
+        }
+    }
+
+    /**
+     * 获取SRM token
+     * @return
+     */
+    public static Map<String,String> getSRMToken() {
+        Map<String,String> returnMap = new HashMap<>();
+
+        Map<String, String> mapentity = CommonHelperUtils.getCommonParams("SRM");
+        if(mapentity == null ){
+            logger.info("SRMHelperUtils:nckd_entryentity is null");
+
+            returnMap.put("code", "1");
+            returnMap.put("msg", "未配置SRM系统对接参数!");
+            return returnMap;
+        }
+
+        String serverUrlApi = mapentity.get("apiserverurl");
+        String getTokenApi = mapentity.get("apigettoken");
+        String grantType = mapentity.get("granttype");
+        String clientId = mapentity.get("clientid");
+        String clientSecret = mapentity.get("clientsecret");
+        String scope = mapentity.get("scope");
+
+
+        if(StringUtils.isEmpty(serverUrlApi) || StringUtils.isEmpty(getTokenApi) || StringUtils.isEmpty(grantType)
+                || StringUtils.isEmpty(clientId) ||StringUtils.isEmpty(clientSecret) || StringUtils.isEmpty(scope) ){
+            returnMap.put("code", "1");
+            returnMap.put("msg", "请检查是否配置SRM参数:服务地址、授权模式、客户端ID、客户端密钥、作用域、获取token接口");
+            return returnMap;
+        }
+
+        String tokenUrl = serverUrlApi + getTokenApi + "?grant_type=" + grantType + "&client_id=" + clientId + "&client_secret=" + clientSecret + "&scope=" + scope;
+
+        try {
+            JSONObject jsonData = new JSONObject();
+            String response = KHttpClientUtils.postjson(tokenUrl, null, jsonData.toJSONString());
+            if(StringUtils.isEmpty(response)){
+                returnMap.put("code", "1");
+                returnMap.put("msg", "获取token失败, getToken返回为空");
+
+                return returnMap;
+            }
+
+            JSONObject resultJSON = JSONObject.parseObject(response);
+            if (resultJSON !=null &&!StringUtils.isEmpty(resultJSON.get("access_token").toString())) {
+                returnMap.put("code", "0");
+                returnMap.put("msg", resultJSON.get("access_token").toString());
+            } else{
+                returnMap.put("code","1");
+                returnMap.put("msg", response);
+            }
+
+            return returnMap;
+        } catch(IOException e){
+            returnMap.put("code", "1");
+            returnMap.put("msg", "获取token失败, " + e.getMessage());
+
+            return returnMap;
+        } catch(Exception e){
+            returnMap.put("code", "1");
+            returnMap.put("msg", "获取token失败, " + e.getMessage());
+
+            return returnMap;
+        }
+    }
+
+    /**
+     * http post
+     * @param url
+     * @return
+     * @throws IOException
+     */
+    public static String doHttpClientPost(String url) throws IOException{
+        OkHttpClient client = new OkHttpClient().newBuilder()
+//	    	.connectTimeout(timeout, TimeUnit.SECONDS)
+//            .readTimeout(timeout, TimeUnit.SECONDS)
+//            .writeTimeout(timeout, TimeUnit.SECONDS)
+                .build();
+        MediaType mediaType = MediaType.parse("text/plain");
+        RequestBody body = RequestBody.create(mediaType, "");
+        Request request = new Request.Builder().url(url).method("POST", body).addHeader("User-Agent", "apifox/1.0.0 (https://www.apifox.cn)").build();
+        Response response = client.newCall(request).execute();
+
+        if(response == null){
+            return null;
+        }
+        return response.body().toString();
+    }
+
+    public static String doHttpClientPost(String url, String bodyString, String token) throws IOException{
+//	    	int maxRentry=2;//请求重试次数(本次+ maxRentry),注意:BOS开发的jdk是1.6,我们本地测试用低版本的okhttp-3.2.0.jar和okio-1.9.0.jar,服务器还是用okhttp-3.12.13.jar和okio-1.15.0.jar
+        OkHttpClient client = new OkHttpClient().newBuilder()
+                .connectTimeout(timeout, TimeUnit.SECONDS)
+                .readTimeout(timeout, TimeUnit.SECONDS)
+                .writeTimeout(timeout, TimeUnit.SECONDS)
+//            .addInterceptor(new HttpResponseConnectTimeout.OkhttpInterceptor(maxRentry)) //过滤器,设置最大重试次数
+                .build();
+        MediaType mediaType = MediaType.parse("application/json");
+        RequestBody body = RequestBody.create(mediaType, bodyString);
+        Request request = new Request.Builder().url(url).method("POST", body)
+                .addHeader("User-Agent", "apifox/1.0.0 (https://www.apifox.cn)")
+                .addHeader("Content-Type", "application/json")
+                .addHeader("Authorization", "Bearer " + token).build();
+
+        Response response = client.newCall(request).execute();
+        return response.body().string();
+    }
+}