Merge tag '1.0.12' into develop
农商行接口修改,消费接口新加支付场景
diff --git a/.gitignore b/.gitignore
index 470effd..7499f48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,3 +49,4 @@
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
+/log
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 83b1ec8..fe5a7de 100644
--- a/build.gradle
+++ b/build.gradle
@@ -101,7 +101,6 @@
implementation "org.slf4j:slf4j-api:${slf4jVersion}"
implementation "org.postgresql:postgresql:${postgresVersion}"
implementation "io.github.microutils:kotlin-logging:${kotlnLogVersion}"
- implementation "org.slf4j:slf4j-parent:${slf4jVersion}"
implementation "com.google.code.gson:gson:${gsonVersion}"
implementation "commons-dbcp:commons-dbcp:${dbcpVersion}"
implementation "commons-codec:commons-codec:${commonCodecVersion}"
diff --git a/bus-qrcode/build.gradle b/bus-qrcode/build.gradle
new file mode 100644
index 0000000..8f2e1f1
--- /dev/null
+++ b/bus-qrcode/build.gradle
@@ -0,0 +1,54 @@
+plugins {
+ id 'java'
+ id 'maven-publish'
+ id "com.palantir.git-version"
+}
+
+group = rootProject.group
+
+def sdkVersion = gitVersion()
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+
+publishing {
+ publications {
+ mavenJava(MavenPublication) {
+ groupId = project.group
+ artifactId = 'bus-qrcode'
+ version = sdkVersion
+ from components.java
+ }
+ }
+ repositories {
+ maven {
+ // change URLs to point to your repos, e.g. http://my.org/repo
+ def releasesRepoUrl = "http://ykt-nx.supwisdom.com/repository/ecard-repo/"
+ def snapshotsRepoUrl = "http://ykt-nx.supwisdom.com/repository/ecard-repo/snapshot/"
+ url = version.endsWith('dirty') ? snapshotsRepoUrl : releasesRepoUrl
+ credentials(PasswordCredentials) {
+ username = nxUser
+ password = nxPassword
+ }
+ }
+ }
+}
+
+dependencies {
+ implementation "org.apache.commons:commons-lang3:3.7"
+ implementation 'com.eatthepath:java-otp:0.1.0'
+ implementation 'org.slf4j:slf4j-api:1.7.25'
+ implementation 'commons-codec:commons-codec:1.9'
+ runtime 'org.slf4j:slf4j-parent:1.7.25'
+ runtime 'org.slf4j:slf4j-simple:1.7.25'
+ testImplementation 'junit:junit:4.12'
+}
+
+jar {
+ enabled = true
+ baseName = "bus-qrcode"
+ manifest {
+ attributes('Bus-QRcode-Version': sdkVersion)
+ }
+}
+
+publish.dependsOn(jar)
diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/BinUtil.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/BinUtil.java
new file mode 100644
index 0000000..ebb4f05
--- /dev/null
+++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/BinUtil.java
@@ -0,0 +1,67 @@
+package com.supwisdom.dlpay.busqrcode;
+
+import org.apache.commons.codec.binary.Base64;
+
+public class BinUtil {
+ public static byte[] decodeHex(String data) {
+ if (data == null || data.length() % 2 != 0) {
+ throw new RuntimeException("decodeHex data length must be divided by 2");
+ }
+ byte[] result = new byte[data.length() / 2];
+ for (int i = 0; i < data.length(); i += 2) {
+ result[i / 2] = (byte) Integer.parseInt(data.substring(i, i + 2), 16);
+ }
+ return result;
+ }
+
+ public static String encodeHex(byte[] data) {
+ if (data == null) {
+ throw new RuntimeException("encodeHex data must be not null");
+ }
+ StringBuilder sb = new StringBuilder();
+ for (byte datum : data) {
+ sb.append(String.format("%02x", datum & 0xFF));
+ }
+ return sb.toString();
+ }
+
+ public static byte[] encodeBCD(String data) {
+ if (data == null || data.length() % 2 != 0) {
+ throw new RuntimeException("encodeBCD data length must be divided by 2");
+ }
+ byte[] result = new byte[data.length() / 2];
+ int base = (int)'0';
+ for (int i = 0; i < data.length(); i += 2) {
+ int t = (int)data.charAt(i) - base;
+ int t1 = (int)data.charAt(i + 1) - base;
+ if (t < 0 || t > 9 || t1 < 0 || t1 > 9) {
+ throw new RuntimeException("encodeBCD char must be '0'~'9'");
+ }
+ result[i / 2] = (byte) (((t << 4) | t1) & 0xFF);
+ }
+ return result;
+ }
+
+ public static String decodeBCD(byte[] data) {
+ if (data == null) {
+ throw new RuntimeException("decodeBCD data must be not null");
+ }
+ int base = (int)'0';
+ StringBuilder sb = new StringBuilder();
+ for (byte datum : data) {
+ int t1 = ((datum >> 4) & 0x0F) + base;
+ int t2 = (datum & 0x0F) + base;
+ sb.append((char) t1)
+ .append((char) t2);
+ }
+ return sb.toString();
+ }
+
+ public static String encodeBase64(byte[] data) {
+ return Base64.encodeBase64String(data);
+ }
+
+ public static byte[] decodeBase64(String data) {
+ return Base64.decodeBase64(data);
+ }
+}
diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/CryptoUtil.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/CryptoUtil.java
new file mode 100644
index 0000000..839e3c0
--- /dev/null
+++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/CryptoUtil.java
@@ -0,0 +1,117 @@
+package com.supwisdom.dlpay.busqrcode;
+
+import javax.crypto.*;
+import javax.crypto.spec.DESKeySpec;
+import javax.crypto.spec.DESedeKeySpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.Arrays;
+
+public class CryptoUtil {
+
+ private static byte[] doCipher(Cipher cipher, SecretKey key, int mode, byte[] data) {
+ try {
+ cipher.init(mode, key);
+ return cipher.doFinal(data);
+ } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private static byte[] doDesCipher(byte[] key, int cipherMode, byte[] data) {
+ try {
+ String mode;
+ KeySpec keySpec;
+ if (key.length == 8) {
+ mode = "DES";
+ keySpec = new DESKeySpec(key);
+ } else if (key.length == 16) {
+ mode = "DESede";
+ byte[] keyEDE = new byte[24];
+ System.arraycopy(key, 0, keyEDE, 0, 16);
+ System.arraycopy(key, 0, keyEDE, 16, 8);
+ keySpec = new DESedeKeySpec(keyEDE);
+ } else {
+ throw new RuntimeException("DES Key length must be 8 or 16 bytes");
+ }
+ Cipher keyCipher = Cipher.getInstance(mode + "/ECB/NoPadding");
+ SecretKey secret = SecretKeyFactory.getInstance(mode).generateSecret(keySpec);
+ return doCipher(keyCipher, secret, cipherMode, data);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeySpecException | InvalidKeyException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static byte[] desEncryptECB(byte[] key, byte[] plain) {
+ return doDesCipher(key, Cipher.ENCRYPT_MODE, plain);
+ }
+
+ public static byte[] desDecryptECB(byte[] key, byte[] cipher) {
+ return doDesCipher(key, Cipher.DECRYPT_MODE, cipher);
+ }
+
+ private static byte[] messageDigest(String alg, byte[] data) {
+ try {
+ MessageDigest md = MessageDigest.getInstance(alg);
+ md.update(data);
+ return md.digest();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static byte[] md5(byte[] data) {
+ return messageDigest("MD5", data);
+ }
+
+ public static byte[] sha256(byte[] data) {
+ return messageDigest("SHA-256", data);
+ }
+
+ public static byte[] sha1(byte[] data) {
+ return messageDigest("SHA-1", data);
+ }
+
+ private static byte[] doAESCipher(byte[] key, byte[] iv, byte[] data, int mode) {
+ try {
+ Cipher keyCipher = Cipher.getInstance("AES/CFB/NoPadding");
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+ SecretKeySpec secret = new SecretKeySpec(key, "AES");
+ keyCipher.init(mode, secret, ivSpec);
+ return keyCipher.doFinal(data);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+ | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static byte[] aesEncryptCFB(byte[] key, byte[] plain) {
+ byte[] iv = new byte[16];
+ Arrays.fill(iv, (byte) 0);
+ return aesEncryptCFB(key, plain, iv);
+ }
+
+ public static byte[] aesEncryptCFB(byte[] key, byte[] plain, byte[] iv) {
+ return doAESCipher(key, iv, plain, Cipher.ENCRYPT_MODE);
+ }
+
+ public static byte[] aesDecryptCFB(byte[] key, byte[] cipher, byte[] iv) {
+ return doAESCipher(key, iv, cipher, Cipher.DECRYPT_MODE);
+ }
+
+ public static byte[] aesDecryptCFB(byte[] key, byte[] cipher) {
+ byte[] iv = new byte[16];
+ Arrays.fill(iv, (byte) 0);
+ return aesDecryptCFB(key, cipher, iv);
+ }
+}
diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/PbocAlgorithem.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/PbocAlgorithem.java
new file mode 100644
index 0000000..3c26f8b
--- /dev/null
+++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/PbocAlgorithem.java
@@ -0,0 +1,93 @@
+package com.supwisdom.dlpay.busqrcode;
+
+import java.util.Arrays;
+
+import static com.supwisdom.dlpay.busqrcode.CryptoUtil.desDecryptECB;
+import static com.supwisdom.dlpay.busqrcode.CryptoUtil.desEncryptECB;
+
+
+public class PbocAlgorithem {
+ private static final byte[] PADDING = new byte[]{(byte) 0x80, 0, 0, 0, 0, 0, 0, 0};
+
+ private static byte[] doDeliveryKey(byte[] key, byte[] factor) {
+ if (key.length == 16) {
+ byte[] factor1 = new byte[8];
+ for (int i = 0; i < 8; i++) {
+ factor1[i] = (byte) ((~((int) factor[i] & 0xFF)) & 0xFF);
+ }
+ byte[] d1 = desEncryptECB(key, factor);
+ byte[] d2 = desEncryptECB(key, factor1);
+ byte[] result = new byte[16];
+ System.arraycopy(d1, 0, result, 0, 8);
+ System.arraycopy(d2, 0, result, 8, 8);
+ return result;
+ } else if (key.length == 8) {
+ return desEncryptECB(key, factor);
+ } else {
+ throw new RuntimeException("Des key length must be 8 or 16");
+ }
+ }
+
+ public static byte[] deliveryKey(byte[] rootKey, byte[] factory) {
+ if (factory.length % 8 != 0) {
+ throw new RuntimeException("delivery key factory length must be divived by 8");
+ }
+ if (factory.length / 8 > 3) {
+ throw new RuntimeException("delivery key factor length must be less or equal 24");
+ }
+
+ byte[] deliveryKey = rootKey;
+
+ for (int i = 0; i < factory.length; i += 8) {
+ deliveryKey = doDeliveryKey(deliveryKey, Arrays.copyOfRange(factory, i, i + 8));
+ }
+ return deliveryKey;
+ }
+
+ private static byte[] doDesMac(byte[] key, byte[] init, byte[] data) {
+ byte[] temp = Arrays.copyOf(init, 8);
+ for (int offset = 0; offset < data.length; offset += 8) {
+ int i;
+ for (i = 0; i < 8 && i + offset < data.length; ++i) {
+ temp[i] = (byte) (((int) temp[i] & 0xFF) ^ ((int) data[i + offset] & 0xFF));
+ }
+ for (int j = i; i < 8; ++i) {
+ temp[i] = (byte) (((int) temp[i] & 0xFF) ^ ((int) PADDING[i - j] & 0xFF));
+ }
+ temp = desEncryptECB(key, temp);
+ }
+ return temp;
+ }
+
+ public static byte[] desMac(byte[] key, byte[] init, byte[] data) {
+ byte[] buffer = Arrays.copyOfRange(doDesMac(key, init, data), 0, 4);
+ if (data.length % 8 == 0) {
+ buffer = doDesMac(key, buffer, PADDING);
+ }
+ return Arrays.copyOfRange(buffer, 0, 4);
+ }
+
+ public static byte[] desMac(byte[] key, byte[] data) {
+ byte[] init = new byte[8];
+ Arrays.fill(init, (byte) 0);
+ return desMac(key, init, data);
+ }
+
+ public static byte[] des3Mac(byte[] key, byte[] init, byte[] data) {
+ byte[] k1 = Arrays.copyOfRange(key, 0, 8);
+ byte[] k2 = Arrays.copyOfRange(key, 8, 16);
+ byte[] buffer = doDesMac(k1, init, data);
+ if (data.length % 8 == 0) {
+ buffer = doDesMac(key, buffer, PADDING);
+ }
+ buffer = desDecryptECB(k2, buffer);
+ buffer = desEncryptECB(k1, buffer);
+ return Arrays.copyOfRange(buffer, 0, 4);
+ }
+
+ public static byte[] des3Mac(byte[] key, byte[] data) {
+ byte[] init = new byte[8];
+ Arrays.fill(init, (byte) 0);
+ return des3Mac(key, init, data);
+ }
+}
diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/QrCode.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/QrCode.java
new file mode 100644
index 0000000..93f6b60
--- /dev/null
+++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/QrCode.java
@@ -0,0 +1,279 @@
+package com.supwisdom.dlpay.busqrcode;
+
+import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+import static com.supwisdom.dlpay.busqrcode.BinUtil.*;
+import static com.supwisdom.dlpay.busqrcode.CryptoUtil.*;
+
+public class QrCode {
+ private static Logger logger = LoggerFactory.getLogger(QrCode.class);
+
+ public static final String DELIMITER = ":";
+ public static final String SCOPE_DELIMITER = ",";
+ public static final String DEFAULT_SCOPE = "1"; //默认仅支持 1-大理车载消费场景
+ public static final int TOTP_OFFSET = 10; //TODO: 前后偏移10步,30*10=300s=5m 约前后5分钟内totp有效
+
+// static final ArrayList<String> DEFAULT_SCOPES = new ArrayList<>();
+// static {
+// DEFAULT_SCOPES.add("1"); //默认仅支持 1-大理车载消费场景
+// }
+
+ private Builder qrBuilder;
+
+ private QrCode(Builder b) {
+ this.qrBuilder = b;
+ }
+
+ public static class Builder {
+ private String appid;
+ private String uid;
+ private String scope;
+ private String seed = "a8aae1a41ffab089428e8e21381cc34af3b614ac7b7cba50f58470b8e50c6072";
+ private boolean resetSeed = false;
+ private byte[] iv = decodeHex("55b6f5b3287c535f8274a99354676d0b");
+ private boolean resetIv = false;
+ private byte[] rootKey;
+ private byte[] sKey;
+ private byte[] des3Key;
+ private String prefix;
+ private boolean debug;
+
+ public QrCode create() {
+ return new QrCode(this);
+ }
+
+ public Builder appid(String appid) {
+ if (StringUtils.isBlank(appid) || !StringUtils.isAsciiPrintable(appid)) {
+ throw new RuntimeException("appid is invalid");
+ }
+ this.appid = appid;
+ this.des3Key = md5(appid.getBytes());
+ return this;
+ }
+
+ public Builder uid(String uid) {
+ if (StringUtils.isBlank(uid) || !StringUtils.isAsciiPrintable(uid)) {
+ throw new RuntimeException("uid is invalid");
+ }
+ this.uid = uid;
+ return this;
+ }
+
+ public Builder scope(String scope) {
+ if (StringUtils.isBlank(scope)) {
+ throw new RuntimeException("scope is empty");
+ }
+ this.scope = scope;
+ return this;
+ }
+
+ public Builder rootkey(String key) {
+ this.rootKey = decodeHex(key);
+ return this;
+ }
+
+ public Builder rootkey(String appid, String skey){
+ if (StringUtils.isBlank(appid) || !StringUtils.isAsciiPrintable(appid)) {
+ throw new RuntimeException("appid is invalid");
+ }
+ if (StringUtils.isBlank(skey)) {
+ throw new RuntimeException("sKey is empty");
+ }
+ this.appid = appid;
+ this.sKey = decodeHex(skey);
+ this.des3Key = md5(appid.getBytes());
+ this.rootKey = desEncryptECB(this.des3Key, this.sKey);
+ return this;
+ }
+
+ public Builder seed(String s) {
+ if (StringUtils.isBlank(s)) {
+ throw new RuntimeException("seed is empty");
+ }
+ this.seed = s;
+ this.resetSeed = true;
+ return this;
+ }
+
+ public Builder ivHex(String v) {
+ this.iv = decodeHex(v);
+ this.resetIv = true;
+ return this;
+ }
+
+ public Builder prefix(String p) {
+ if (p == null) {
+ throw new RuntimeException("prefix must not be null");
+ }
+ this.prefix = p;
+ return this;
+ }
+
+ public Builder debug(boolean on) {
+ this.debug = on;
+ return this;
+ }
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ private static String getRandomString(int length) {
+ String base = "abcdefghijklmnopqrstuvwxyz234567";
+ Random random = new Random(System.currentTimeMillis());
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ int number = random.nextInt(base.length());
+ sb.append(base.charAt(number));
+ }
+ return sb.toString();
+ }
+
+ private String genTOTPWithSeed(String seed, int pl) {
+ final TimeBasedOneTimePasswordGenerator alg = totpGenerator(pl);
+ final SecretKey secretKey = new SecretKeySpec(decodeHex(seed),
+ TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA256);
+ try {
+ Date now = Calendar.getInstance().getTime();
+ return StringUtils.leftPad(String.format("%d", alg.generateOneTimePassword(secretKey, now)),
+ pl, '0');
+ } catch (InvalidKeyException e) {
+ throw new RuntimeException("TOTP Error", e);
+ }
+ }
+
+ private TimeBasedOneTimePasswordGenerator totpGenerator(int passwordLength) {
+ try {
+ return new TimeBasedOneTimePasswordGenerator(30, TimeUnit.SECONDS, passwordLength,
+ TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA256);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("TOTP Error", e);
+ }
+ }
+
+ private boolean checkScope(List<String> scopes) {
+ return scopes.contains(DEFAULT_SCOPE);
+ }
+
+ private boolean verifyTotp(String totp, String secret, int offset) {
+ String second = "30";
+ String seed = secret;
+ long T0 = 0;
+ String[] keys = new String[offset * 2 + 1];
+ long time = Calendar.getInstance().getTime().getTime() / 1000;
+ for (int i = 0; i < keys.length; i++) {
+ String steps = "0";
+ try {
+ long T = (time - T0) / Long.parseLong(second);
+ steps = Long.toHexString(T + (i - offset)).toUpperCase();
+ while (steps.length() < 16) {
+ steps = "0" + steps;
+ }
+ String key = TOTP.generateTOTP(secret, steps, "8", TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA256);
+ keys[i] = key;
+ } catch (final Exception e) {
+ System.out.println("Error : " + e);
+ }
+ }
+ for (String key : keys) {
+ if (key.equals(totp)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String qrcode() {
+ if (qrBuilder.debug) {
+ logger.info("appid=" + qrBuilder.appid);
+ logger.info("uid=" + qrBuilder.uid);
+ logger.info("scope=" + qrBuilder.scope);
+ if (qrBuilder.resetSeed) logger.info("seed=" + qrBuilder.seed);
+ if (qrBuilder.resetIv) logger.info("iv=" + encodeHex(qrBuilder.iv));
+ if (null != qrBuilder.sKey) logger.info("sKey=" + encodeHex(qrBuilder.sKey).toUpperCase());
+ if (null != qrBuilder.prefix) logger.info("prefix=" + qrBuilder.prefix);
+
+ logger.info("rootkey=" + encodeHex(qrBuilder.rootKey).toUpperCase());
+ logger.info("=======================================================");
+ }
+
+ final String totp = genTOTPWithSeed(qrBuilder.seed, 8);
+ final String encDataPlain = new StringJoin(DELIMITER)
+ .add(qrBuilder.uid)
+ .add(qrBuilder.scope)
+ .add(totp).toString();
+
+ final byte[] encData = aesEncryptCFB(qrBuilder.rootKey, encDataPlain.getBytes(), qrBuilder.iv);
+ final String code = encodeBase64(encData);
+ if (qrBuilder.debug) {
+ logger.info("encdata plain= " + encDataPlain);
+ }
+
+ if (qrBuilder.prefix != null) {
+ return qrBuilder.prefix + code;
+ }
+ return code;
+ }
+
+ public String decodeUid(String qrcode) {
+ if (qrBuilder.debug) {
+ logger.info("appid=" + qrBuilder.appid);
+ if (qrBuilder.resetSeed) logger.info("seed=" + qrBuilder.seed);
+ if (qrBuilder.resetIv) logger.info("iv=" + encodeHex(qrBuilder.iv));
+ if (null != qrBuilder.sKey) logger.info("sKey=" + encodeHex(qrBuilder.sKey).toUpperCase());
+ if (null != qrBuilder.prefix) logger.info("prefix=" + qrBuilder.prefix);
+ logger.info("=======================================================");
+ }
+ if (null == qrBuilder.rootKey) {
+ throw new RuntimeException("qr decode root key must not be null");
+ }
+ if (qrBuilder.prefix != null) {
+ qrcode = qrcode.substring(qrBuilder.prefix.length());
+ }
+ final String encDataPlain = new String(aesDecryptCFB(qrBuilder.rootKey, decodeBase64(qrcode), qrBuilder.iv));
+ if (qrBuilder.debug) {
+ logger.info("Decode data : <" + encDataPlain + ">");
+ }
+ String[] fields = encDataPlain.split(DELIMITER);
+ if (fields.length < 3) {
+ throw new RuntimeException("qrcode plain text format error!");
+ }
+ String uid = fields[0];
+ String[] scopes = fields[1].split(SCOPE_DELIMITER);
+ String totp = fields[2];
+
+ if (!verifyTotp(totp, qrBuilder.seed, TOTP_OFFSET)) {
+ throw new RuntimeException("qrcode is invalid!"); //TOTP校验
+ }
+
+ if (!checkScope(Arrays.asList(scopes))) {
+ throw new RuntimeException("qrcode is not supported!"); //应用场景校验
+ }
+
+ return uid;
+ }
+
+ public String tac(String uid, Integer amount, String transdate, String transtime) {
+ if (null == qrBuilder.appid) {
+ throw new RuntimeException("appid is null");
+ }
+ if (null == qrBuilder.sKey) {
+ throw new RuntimeException("sKey is null");
+ }
+ final String encdata = uid + qrBuilder.appid + amount + transdate + transtime + "{" + encodeHex(qrBuilder.sKey).toUpperCase() + "}";
+ return encodeHex(sha256(encdata.getBytes())).toUpperCase();
+ }
+
+}
diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/StringJoin.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/StringJoin.java
new file mode 100644
index 0000000..ae3b0c7
--- /dev/null
+++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/StringJoin.java
@@ -0,0 +1,30 @@
+package com.supwisdom.dlpay.busqrcode;
+
+public class StringJoin {
+ private String delimiter;
+ private StringBuilder builder;
+
+ public StringJoin(String delimiter) {
+ if(delimiter == null) {
+ throw new RuntimeException("Delimiter must not be null");
+ }
+ this.delimiter = delimiter;
+ builder = new StringBuilder();
+ }
+
+ public StringJoin add(String field) {
+ if(field == null) {
+ throw new RuntimeException("added value must not be null");
+ }
+ if(builder.length() > 0) {
+ builder.append(this.delimiter);
+ }
+ builder.append(field);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return builder.toString();
+ }
+}
diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/TOTP.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/TOTP.java
new file mode 100644
index 0000000..450ca25
--- /dev/null
+++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/TOTP.java
@@ -0,0 +1,264 @@
+package com.supwisdom.dlpay.busqrcode;
+
+/**
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, is permitted pursuant to, and subject to the license
+ terms contained in, the Simplified BSD License set forth in Section
+ 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+ */
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+
+/**
+ * This is an example implementation of the OATH TOTP algorithm. Visit
+ * www.openauthentication.org for more information.
+ *
+ * @author Johan Rydell, PortWise, Inc.
+ */
+public class TOTP {
+ private static final int[] DIGITS_POWER
+ // 0 1 2 3 4 5 6 7 8
+ = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
+
+ private TOTP() {
+ }
+
+ /**
+ * This method uses the JCE to provide the crypto algorithm. HMAC computes a
+ * Hashed Message Authentication Code with the crypto hash algorithm as a
+ * parameter.
+ *
+ * @param crypto
+ * : the crypto algorithm (HmacSHA1, HmacSHA256, HmacSHA512)
+ * @param keyBytes
+ * : the bytes to use for the HMAC key
+ * @param text
+ * : the message or text to be authenticated
+ */
+ private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) {
+ try {
+ Mac hmac;
+ hmac = Mac.getInstance(crypto);
+
+ SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
+ hmac.init(macKey);
+
+ return hmac.doFinal(text);
+ } catch (GeneralSecurityException gse) {
+ throw new UndeclaredThrowableException(gse);
+ }
+ }
+
+ /**
+ * This method converts a HEX string to Byte[]
+ *
+ * @param hex
+ * : the HEX string
+ *
+ * @return: a byte array
+ */
+ private static byte[] hexStr2Bytes(String hex) {
+ // Adding one byte to get the right conversion
+ // Values starting with "0" can be converted
+ byte[] bArray = new BigInteger("10" + hex, 16).toByteArray();
+
+ // Copy all the REAL bytes, not the "first"
+ byte[] ret = new byte[bArray.length - 1];
+
+ for (int i = 0; i < ret.length; i++)
+ ret[i] = bArray[i + 1];
+
+ return ret;
+ }
+
+ /**
+ * This method generates a TOTP value for the given set of parameters.
+ *
+ * @param key
+ * : the shared secret, HEX encoded
+ * @param time
+ * : a value that reflects a time
+ * @param returnDigits
+ * : number of digits to return
+ *
+ * @return: a numeric String in base 10 that includes
+ * {@link truncationDigits} digits
+ */
+ public static String generateTOTP(String key, String time,
+ String returnDigits) {
+ return generateTOTP(key, time, returnDigits, "HmacSHA1");
+ }
+
+ /**
+ * This method generates a TOTP value for the given set of parameters.
+ *
+ * @param key
+ * : the shared secret, HEX encoded
+ * @param time
+ * : a value that reflects a time
+ * @param returnDigits
+ * : number of digits to return
+ *
+ * @return: a numeric String in base 10 that includes
+ * {@link truncationDigits} digits
+ */
+ public static String generateTOTP256(String key, String time,
+ String returnDigits) {
+ return generateTOTP(key, time, returnDigits, "HmacSHA256");
+ }
+
+ /**
+ * This method generates a TOTP value for the given set of parameters.
+ *
+ * @param key
+ * : the shared secret, HEX encoded
+ * @param time
+ * : a value that reflects a time
+ * @param returnDigits
+ * : number of digits to return
+ *
+ * @return: a numeric String in base 10 that includes
+ * {@link truncationDigits} digits
+ */
+ public static String generateTOTP512(String key, String time,
+ String returnDigits) {
+ return generateTOTP(key, time, returnDigits, "HmacSHA512");
+ }
+
+ /**
+ * This method generates a TOTP value for the given set of parameters.
+ *
+ * @param key
+ * : the shared secret, HEX encoded
+ * @param time
+ * : a value that reflects a time
+ * @param returnDigits
+ * : number of digits to return
+ * @param crypto
+ * : the crypto function to use
+ *
+ * @return: a numeric String in base 10 that includes
+ * {@link truncationDigits} digits
+ */
+ public static String generateTOTP(String key, String time,
+ String returnDigits, String crypto) {
+ int codeDigits = Integer.decode(returnDigits).intValue();
+ String result = null;
+
+ // Using the counter
+ // First 8 bytes are for the movingFactor
+ // Compliant with base RFC 4226 (HOTP)
+ while (time.length() < 16)
+ time = "0" + time;
+
+ // Get the HEX in a Byte[]
+ byte[] msg = hexStr2Bytes(time);
+ byte[] k = hexStr2Bytes(key);
+ byte[] hash = hmac_sha(crypto, k, msg);
+
+ // put selected bytes into result int
+ int offset = hash[hash.length - 1] & 0xf;
+
+ int binary = ((hash[offset] & 0x7f) << 24)
+ | ((hash[offset + 1] & 0xff) << 16)
+ | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);
+
+ int otp = binary % DIGITS_POWER[codeDigits];
+
+ result = Integer.toString(otp);
+
+ while (result.length() < codeDigits) {
+ result = "0" + result;
+ }
+
+ return result;
+ }
+
+ public static void main(String[] args) {
+ long X = 30;
+ long T0 = 0;
+ String steps = "0";
+ long time = System.currentTimeMillis() / 1000;
+ System.out.println(time);
+ long T = (time - T0) / X;
+ steps = Long.toHexString(T).toUpperCase();
+ while (steps.length() < 16) {
+ steps = "0" + steps;
+ }
+ String seed = "9e8bb3d2df73741c041aef39e37ee015fc98233720a1350fcd67d5c3027896ac";
+ String totp = TOTP.generateTOTP(seed, steps, "8", "HmacSHA256");
+ System.out.println(totp);
+
+ /*
+
+
+ SecureRandom a = new SecureRandom();
+ byte[] b = a.generateSeed(8);
+ // Seed for HMAC-SHA1 - 20 bytes
+ String seed = "3132333435363738393031323334353637383930";
+
+ // Seed for HMAC-SHA256 - 32 bytes
+ //String seed32 = "3132333435363738393031323334353637383930313233343536373839303132";
+ String seed32 = "e5b72370f61687c8075b8383266f4fbcbb3b55da178eed76329666600c134093";
+
+ // Seed for HMAC-SHA512 - 64 bytes
+ String seed64 = "3132333435363738393031323334353637383930"
+ + "3132333435363738393031323334353637383930"
+ + "3132333435363738393031323334353637383930" + "31323334";
+ long T0 = 0;
+ long X = 30;
+ long[] testTime = { 59L, 1111111109L, 1111111111L, 1234567890L,
+ 2000000000L, 20000000000L };
+
+ String steps = "0";
+ DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ df.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ try {
+ System.out.println("+---------------+-----------------------+"
+ + "------------------+--------+--------+");
+ System.out.println("| Time(sec) | Time (UTC format) "
+ + "| Value of T(Hex) | TOTP | Mode |");
+ System.out.println("+---------------+-----------------------+"
+ + "------------------+--------+--------+");
+
+ for (int i = 0; i < testTime.length; i++) {
+ long T = (testTime[i] - T0) / X;
+ steps = Long.toHexString(T).toUpperCase();
+
+ while (steps.length() < 16)
+ steps = "0" + steps;
+
+ String fmtTime = String.format("%1$-11s", testTime[i]);
+ String utcTime = df.format(new Date(testTime[i] * 1000));
+ System.out.print("| " + fmtTime + " | " + utcTime + " | "
+ + steps + " |");
+ System.out.println(generateTOTP(seed, steps, "8", "HmacSHA1")
+ + "| SHA1 |");
+ System.out.print("| " + fmtTime + " | " + utcTime + " | "
+ + steps + " |");
+ System.out.println(generateTOTP(seed32, steps, "8",
+ "HmacSHA256") + "| SHA256 |");
+ System.out.print("| " + fmtTime + " | " + utcTime + " | "
+ + steps + " |");
+ System.out.println(generateTOTP(seed64, steps, "8",
+ "HmacSHA512") + "| SHA512 |");
+
+ System.out.println("+---------------+-----------------------+"
+ + "------------------+--------+--------+");
+ }
+ } catch (final Exception e) {
+ System.out.println("Error : " + e);
+ }
+
+ */
+ }
+}
\ No newline at end of file
diff --git a/bus-qrcode/src/test/java/com/supwisdom/dlpay/busqrcode/QrcodeTest.java b/bus-qrcode/src/test/java/com/supwisdom/dlpay/busqrcode/QrcodeTest.java
new file mode 100644
index 0000000..9e8bae1
--- /dev/null
+++ b/bus-qrcode/src/test/java/com/supwisdom/dlpay/busqrcode/QrcodeTest.java
@@ -0,0 +1,61 @@
+package com.supwisdom.dlpay.busqrcode;
+
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.supwisdom.dlpay.busqrcode.BinUtil.*;
+import static com.supwisdom.dlpay.busqrcode.CryptoUtil.*;
+import static org.junit.Assert.*;
+
+public class QrcodeTest {
+ private Logger logger = LoggerFactory.getLogger(QrcodeTest.class);
+
+ @Test
+ public void testBusQrcode() {
+ String prefix = "dlsmk_";
+ String appid = "10001";
+ String uid = "ff8080816cf13e79016cf15d63f40011";
+ String scope = "1,2";
+ String rkey = "0A5DE6CE985D43989B7EBE64AD8EB9C3";
+ Integer amount = 100;
+ String transdate = "20200220";
+ String transtime = "090921";
+
+ String skey = encodeHex(desDecryptECB(md5(appid.getBytes()), decodeHex(rkey))).toUpperCase();
+ logger.info("skey=[" + skey + "]");
+ String rootKey = encodeHex(desEncryptECB(md5(appid.getBytes()), decodeHex(skey))).toUpperCase();
+ assertEquals("rootkey与skey互为3des加密关系错误!", rkey, rootKey);
+
+ QrCode encQr = QrCode.builder()
+ .appid(appid)
+ .rootkey(rkey)
+ .uid(uid)
+ .scope(scope)
+ .prefix(prefix)
+ .debug(true)
+ .create();
+ String qrcode = encQr.qrcode();
+ logger.info("qrcode=[" + qrcode + "]");
+
+ QrCode decQr = QrCode.builder()
+ .rootkey(appid, skey)
+ .prefix(prefix)
+ .debug(true)
+ .create();
+
+// try {
+// Thread.sleep(6*60*1000); //totp测试
+// } catch (InterruptedException e) {
+// e.printStackTrace();
+// }
+
+ String decUid = decQr.decodeUid(qrcode);
+ assertEquals("解码uid错误!", uid, decUid);
+
+ String tac = decQr.tac(uid, amount, transdate, transtime);
+ String tacData = uid + appid + amount + transdate + transtime + "{" + skey + "}";
+ String testTac = encodeHex(sha256(tacData.getBytes())).toUpperCase();
+ assertEquals("tac计算错误", tac, testTac);
+ }
+}
\ No newline at end of file
diff --git a/payapi/build.gradle b/payapi/build.gradle
index 184dbb5..2938317 100644
--- a/payapi/build.gradle
+++ b/payapi/build.gradle
@@ -82,6 +82,15 @@
runtime("org.springframework.boot:spring-boot-devtools")
implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5"
+ implementation 'org.bitbucket.b_c:jose4j:0.6.5'
+ implementation 'com.github.penggle:kaptcha:2.3.2'
+ implementation group: 'com.sun.jersey', name: 'jersey-client', version: '1.19'
+ implementation group: 'javax.servlet', name: 'jstl', version: '1.2'
+ implementation group: 'taglibs', name: 'standard', version: '1.1.2'
+ implementation files('libs/masmgc.sdk.sms-0.0.1-SNAPSHOT.jar')
+ implementation 'commons-beanutils:commons-beanutils:1.9.3'
+ implementation 'com.eatthepath:java-otp:0.1.0'
+ implementation project(':payapi-common')
implementation "org.apache.commons:commons-lang3:${lang3Version}"
implementation "net.javacrumbs.shedlock:shedlock-spring:${shedlockVersion}"
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/bean/KafkaXgMessage.java b/payapi/src/main/java/com/supwisdom/dlpay/api/bean/KafkaXgMessage.java
index 9bc836a..1378bcf 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/bean/KafkaXgMessage.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/bean/KafkaXgMessage.java
@@ -6,18 +6,20 @@
private String content;//提醒消息内容
private String gids;//gid发送列表,逗号隔开
private Boolean alltarget;//是否平台全发送
- private String platform;//android,ios
+ private String platform;//android,ios,all
private int type;//TYPE_NOTIFICATION = 1,TYPE_MESSAGE = 2
- private String url;//跳转url
private String expiretime;//过期时间
private Boolean callback;//是否回调返回
- private String msg_type;//提醒类型 字典 epay_pos_scan_code_pay、
- private String refno;//流水参考号
private int retries;//最多重试次数
- private String amount;//消费金额,元,小数点后两位
private String custom; //扩展参数,原样传递
+ //TODO: 下面的参数在epaymessager中已不用,要赋值请放入custom的json串中
+// private String url;//跳转url
+// private String msg_type;//提醒类型 字典 epay_pos_scan_code_pay、
+// private String refno;//流水参考号
+// private String amount;//消费金额,元,小数点后两位
+
public String getTitle() {
return title;
@@ -67,14 +69,6 @@
this.type = type;
}
- public String getUrl() {
- return url;
- }
-
- public void setUrl(String url) {
- this.url = url;
- }
-
public String getExpiretime() {
return expiretime;
}
@@ -91,22 +85,6 @@
this.callback = callback;
}
- public String getMsg_type() {
- return msg_type;
- }
-
- public void setMsg_type(String msg_type) {
- this.msg_type = msg_type;
- }
-
- public String getRefno() {
- return refno;
- }
-
- public void setRefno(String refno) {
- this.refno = refno;
- }
-
public int getRetries() {
return retries;
}
@@ -115,14 +93,6 @@
this.retries = retries;
}
- public String getAmount() {
- return amount;
- }
-
- public void setAmount(String amount) {
- this.amount = amount;
- }
-
public String getCustom() {
return custom;
}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/NoticeDao.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/NoticeDao.java
new file mode 100644
index 0000000..800c10e
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/NoticeDao.java
@@ -0,0 +1,19 @@
+package com.supwisdom.dlpay.framework.dao;
+
+import com.supwisdom.dlpay.framework.domain.TNotice;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import javax.persistence.LockModeType;
+
+@Repository
+public interface NoticeDao extends JpaRepository<TNotice, String>, JpaSpecificationExecutor<TNotice> {
+ TNotice getById(String id);
+
+ @Lock(LockModeType.PESSIMISTIC_WRITE)
+ @Query("from TNotice t where t.id=?1 ")
+ TNotice getByIdForUpdate(String id);
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/NoticeMsgDao.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/NoticeMsgDao.java
new file mode 100644
index 0000000..35a6dd4
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/NoticeMsgDao.java
@@ -0,0 +1,30 @@
+package com.supwisdom.dlpay.framework.dao;
+
+import com.supwisdom.dlpay.framework.domain.TNoticeMsg;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.jpa.repository.QueryHints;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.LockModeType;
+import javax.persistence.QueryHint;
+import java.util.List;
+
+@Repository
+public interface NoticeMsgDao extends JpaRepository<TNoticeMsg, String> {
+ TNoticeMsg getByMsgid(String msgid);
+
+ @Transactional
+ @Lock(LockModeType.PESSIMISTIC_WRITE)
+ @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "0")})
+ @Query("from TNoticeMsg where msgid=?1")
+ TNoticeMsg getByMsgidForUpdateNowait(String msgid);
+
+ @Query("from TNoticeMsg where noticeId=?1 order by createtime desc")
+ List<TNoticeMsg> findAllByNoticeId(String noticeid);
+
+ @Query("from TNoticeMsg t where t.status='normal' and t.pushmode='delay' and t.pushSettime<=?1 and t.sendkafka=false order by t.createtime")
+ List<TNoticeMsg> findAllDelayNoticeByDatetime(String datetime);
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/RoleFunctionDao.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/RoleFunctionDao.java
index e125f7a..945f510 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/RoleFunctionDao.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/RoleFunctionDao.java
@@ -19,11 +19,11 @@
List<TRoleFunction> findByRoleId(String roleId);
@Query(value = "select tt.id,tt.pid,tt.name,tt.checked,tt.open from " +
- " ( select f.id||'' as id ,f.parentid||'' as pid,f.name,case when rf.id is null then 0 else 1 end as checked,case when f.parentid=-1 then 1 else 0 end as open from tb_function f " +
+ " ( select f.id||'' as id ,f.parentid||'' as pid,f.name,case when rf.id is null then 0 else 1 end as checked,case when f.parentid=-1 then 1 else 0 end as open,f.ordernum from tb_function f " +
" left join tb_role_function rf on rf.functionid = f.id and rf.roleid=?1 " +
" union all " +
- " select r.id||'_res' as id,r.function_id||'' as pid,r.name,case when p.id is null then 0 else 1 end as checked,0 as open from tb_resource r " +
- " left join tb_permission p on p.resid = r.id and p.roleid=?1 ) tt order by tt.id " , nativeQuery = true)
+ " select r.id||'_res' as id,r.function_id||'' as pid,r.name,case when p.id is null then 0 else 1 end as checked,0 as open,999999 as ordernum from tb_resource r " +
+ " left join tb_permission p on p.resid = r.id and p.roleid=?1 ) tt order by tt.pid,tt.ordernum" , nativeQuery = true)
List<NodeData> findByRoleIdNative(String roleId);
void deleteByRoleId(String roleId);
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/TNotice.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/TNotice.java
new file mode 100644
index 0000000..08e645d
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/TNotice.java
@@ -0,0 +1,149 @@
+package com.supwisdom.dlpay.framework.domain;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 通知公告表(全体通知)
+ * */
+@Entity
+@Table(name = "TB_NOTICE")
+public class TNotice {
+ @Id
+ @GenericGenerator(name = "nidGenerator", strategy = "uuid")
+ @GeneratedValue(generator = "nidGenerator")
+ @Column(name = "ID", nullable = false, length = 32)
+ private String id;
+
+ @Column(name = "TITLE", nullable = false, length = 200)
+ private String title; //标题
+
+ @Column(name = "CONTENT", nullable = false, length = 2000)
+ private String content; //内容
+
+ @Column(name = "PLATFORM", nullable = false, length = 10)
+ private String platform; //all-全部;ios-苹果;android-安卓
+
+ @Column(name = "CREATEDATE", nullable = false, length = 8)
+ private String createdate; //创建日期yyyyMMdd
+
+ @Column(name = "CREATETIME", length = 6)
+ private String createtime; //创建日期hh24miss
+
+ @Column(name = "OPERID", length = 32)
+ private String operid; //创建者的operid
+
+ @Column(name = "CREATOR", length = 200)
+ private String creator; //创建者名称
+
+ @Column(name = "SENDCNT", nullable = false, precision = 9)
+ private Integer sendcnt = 0;
+
+ @Column(name = "LINKURL", length = 1000)
+ private String linkurl;
+
+ @Column(name = "LASTSAVED", length = 14)
+ private String lastsaved; //最后更新时间
+
+ @Column(name = "tenantid", length = 20)
+ @NotNull
+ private String tenantId;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public String getPlatform() {
+ return platform;
+ }
+
+ public void setPlatform(String platform) {
+ this.platform = platform;
+ }
+
+ public String getCreatedate() {
+ return createdate;
+ }
+
+ public void setCreatedate(String createdate) {
+ this.createdate = createdate;
+ }
+
+ public String getCreatetime() {
+ return createtime;
+ }
+
+ public void setCreatetime(String createtime) {
+ this.createtime = createtime;
+ }
+
+ public String getOperid() {
+ return operid;
+ }
+
+ public void setOperid(String operid) {
+ this.operid = operid;
+ }
+
+ public String getCreator() {
+ return creator;
+ }
+
+ public void setCreator(String creator) {
+ this.creator = creator;
+ }
+
+ public Integer getSendcnt() {
+ return sendcnt;
+ }
+
+ public void setSendcnt(Integer sendcnt) {
+ this.sendcnt = sendcnt;
+ }
+
+ public String getLinkurl() {
+ return linkurl;
+ }
+
+ public void setLinkurl(String linkurl) {
+ this.linkurl = linkurl;
+ }
+
+ public String getLastsaved() {
+ return lastsaved;
+ }
+
+ public void setLastsaved(String lastsaved) {
+ this.lastsaved = lastsaved;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/TNoticeMsg.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/TNoticeMsg.java
new file mode 100644
index 0000000..2a417e0
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/TNoticeMsg.java
@@ -0,0 +1,160 @@
+package com.supwisdom.dlpay.framework.domain;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 通知公告发布明细表(全体通知)
+ * */
+@Entity
+@Table(name = "TB_NOTICE_MSG")
+public class TNoticeMsg {
+ @Id
+ @GenericGenerator(name = "midGenerator", strategy = "uuid")
+ @GeneratedValue(generator = "midGenerator")
+ @Column(name = "msgid", nullable = false, length = 32)
+ private String msgid;
+
+ @Column(name = "notice_id", nullable = false, length = 32)
+ private String noticeId;
+
+ @Column(name = "alltarget", nullable = false, length = 10)
+ private Boolean alltarget = true; //是否全体通知公告
+
+ @Column(name = "pushmode", nullable = false, length = 10)
+ private String pushmode = "atonce"; //atonce-立即发布,delay-延迟定时发布
+
+ @Column(name = "push_settime", length = 14)
+ private String pushSettime; //定时发布的设定时间
+
+ @Column(name = "publisher", length = 200)
+ private String publisher; //发布者
+
+ @Column(name = "operid", length = 32)
+ private String operid; //发布者operid
+
+ @Column(name = "status", nullable = false, length = 10)
+ private String status;
+
+ @Column(name = "sendkafka", nullable = false, length = 10)
+ private Boolean sendkafka=false;
+
+ @Column(name = "pushtime", length = 14)
+ private String pushtime;
+
+ @Column(name = "createtime", length = 14)
+ private String createtime;
+
+ @Column(name = "pushresult", length = 600)
+ private String pushresult;
+
+ @Column(name = "tenantid", length = 20)
+ @NotNull
+ private String tenantId;
+
+ public String getMsgid() {
+ return msgid;
+ }
+
+ public void setMsgid(String msgid) {
+ this.msgid = msgid;
+ }
+
+ public String getNoticeId() {
+ return noticeId;
+ }
+
+ public void setNoticeId(String noticeId) {
+ this.noticeId = noticeId;
+ }
+
+ public Boolean getAlltarget() {
+ return alltarget;
+ }
+
+ public void setAlltarget(Boolean alltarget) {
+ this.alltarget = alltarget;
+ }
+
+ public String getPushmode() {
+ return pushmode;
+ }
+
+ public void setPushmode(String pushmode) {
+ this.pushmode = pushmode;
+ }
+
+ public String getPushSettime() {
+ return pushSettime;
+ }
+
+ public void setPushSettime(String pushSettime) {
+ this.pushSettime = pushSettime;
+ }
+
+ public String getPublisher() {
+ return publisher;
+ }
+
+ public void setPublisher(String publisher) {
+ this.publisher = publisher;
+ }
+
+ public String getOperid() {
+ return operid;
+ }
+
+ public void setOperid(String operid) {
+ this.operid = operid;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public Boolean getSendkafka() {
+ return sendkafka;
+ }
+
+ public void setSendkafka(Boolean sendkafka) {
+ this.sendkafka = sendkafka;
+ }
+
+ public String getPushtime() {
+ return pushtime;
+ }
+
+ public void setPushtime(String pushtime) {
+ this.pushtime = pushtime;
+ }
+
+ public String getCreatetime() {
+ return createtime;
+ }
+
+ public void setCreatetime(String createtime) {
+ this.createtime = createtime;
+ }
+
+ public String getPushresult() {
+ return pushresult;
+ }
+
+ public void setPushresult(String pushresult) {
+ this.pushresult = pushresult;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/tenant/HibernateConfig.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/tenant/HibernateConfig.java
deleted file mode 100644
index bff7098..0000000
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/tenant/HibernateConfig.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.supwisdom.dlpay.framework.tenant;
-
-import java.util.HashMap;
-import java.util.Map;
-import javax.sql.DataSource;
-
-import lombok.extern.slf4j.Slf4j;
-import org.hibernate.MultiTenancyStrategy;
-import org.hibernate.cfg.Environment;
-import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
-import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
-import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
-import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.orm.jpa.JpaVendorAdapter;
-import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
-import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
-
-
-@Configuration
-@Slf4j
-public class HibernateConfig {
-
- private final JpaProperties jpaProperties;
-
- private final HibernateProperties hibernateProperties;
-
- public HibernateConfig(@Autowired JpaProperties jpaProperties,
- HibernateProperties hibernateProperties) {
- this.jpaProperties = jpaProperties;
- this.hibernateProperties = hibernateProperties;
- }
-
- @Bean
- public JpaVendorAdapter getJpaVendorAdapter() {
- HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
- adapter.setGenerateDdl(true);
- return adapter;
- }
-
- @Bean("entityManagerFactory")
- public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource,
- MultiTenantConnectionProvider multiTenantConnectionProvider,
- CurrentTenantIdentifierResolver currentTenantIdentifierResolver) {
- Map<String, Object> properties = new HashMap<>();
- properties.putAll(hibernateProperties
- .determineHibernateProperties(jpaProperties.getProperties(),
- new HibernateSettings()));
- properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
- properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProvider);
- properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver);
-
- LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
- em.setDataSource(dataSource);
- em.setPackagesToScan("com.supwisdom");
- em.setJpaPropertyMap(properties);
- em.setJpaVendorAdapter(getJpaVendorAdapter());
- log.info("setup multi-tenant entityManagerFactor");
- return em;
- }
-
-}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/system/controller/NoticeController.java b/payapi/src/main/java/com/supwisdom/dlpay/system/controller/NoticeController.java
new file mode 100644
index 0000000..71bfae7
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/system/controller/NoticeController.java
@@ -0,0 +1,192 @@
+package com.supwisdom.dlpay.system.controller;
+
+import com.supwisdom.dlpay.api.bean.JsonResult;
+import com.supwisdom.dlpay.api.service.KafkaSendMsgService;
+import com.supwisdom.dlpay.framework.data.SystemDateTime;
+import com.supwisdom.dlpay.framework.domain.TNotice;
+import com.supwisdom.dlpay.framework.domain.TNoticeMsg;
+import com.supwisdom.dlpay.framework.domain.TOperator;
+import com.supwisdom.dlpay.framework.service.SystemUtilService;
+import com.supwisdom.dlpay.framework.util.DateUtil;
+import com.supwisdom.dlpay.framework.util.PageResult;
+import com.supwisdom.dlpay.framework.util.StringUtil;
+import com.supwisdom.dlpay.framework.util.WebConstant;
+import com.supwisdom.dlpay.system.service.NoticeService;
+import com.supwisdom.dlpay.util.ConstantUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+@Controller
+@RequestMapping("/notice")
+public class NoticeController {
+ @Autowired
+ private NoticeService noticeService;
+ @Autowired
+ private SystemUtilService systemUtilService;
+ @Autowired
+ private KafkaSendMsgService kafkaSendMsgService;
+
+ @GetMapping("/index")
+ public String noticeIndex() {
+ return "system/notice/index";
+ }
+
+ @GetMapping("/list")
+ @PreAuthorize("hasPermission('/notice/index','')")
+ @ResponseBody
+ public PageResult<TNotice> searchNotice(@RequestParam("page") Integer pageNo,
+ @RequestParam("limit") Integer pageSize,
+ @RequestParam(value = "startdate", required = false) String startdate,
+ @RequestParam(value = "enddate", required = false) String enddate,
+ @RequestParam(value = "searchkey", required = false) String searchkey) {
+ try {
+ if (null == pageNo || pageNo < 1) pageNo = WebConstant.PAGENO_DEFAULT;
+ if (null == pageSize || pageSize < 1) pageSize = WebConstant.PAGESIZE_DEFAULT;
+ return noticeService.getNotice(startdate, enddate, searchkey, pageNo, pageSize);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return new PageResult<>(99, "系统查询错误");
+ }
+ }
+
+ @GetMapping("/load4add")
+ @PreAuthorize("hasPermission('/notice/load4add','')")
+ public String load4Add() {
+ return "system/notice/add";
+ }
+
+ @PostMapping("/edit")
+ @PreAuthorize("hasPermission('/notice/edit','')")
+ @ResponseBody
+ public JsonResult edit(@RequestParam("noticeId") String noticeId,
+ @RequestParam("title") String title,
+ @RequestParam("content") String content,
+ @RequestParam("platform") String platform,
+ @RequestParam(value = "linkurl", required = false) String linkurl,
+ @AuthenticationPrincipal UserDetails operUser) {
+ try {
+ TOperator oper = (TOperator) operUser;
+ String prefix = "";
+ TNotice notice = null;
+ if (StringUtil.isEmpty(noticeId)) {
+ prefix = "新增";
+ notice = new TNotice();
+ SystemDateTime dt = systemUtilService.getSysdatetime();
+ notice.setCreatedate(dt.getHostdate());
+ notice.setCreatetime(dt.getHosttime());
+ notice.setOperid(oper.getOperid());
+ notice.setCreator(oper.getOpername());
+ notice.setTenantId(oper.getTenantId());
+ } else {
+ prefix = "修改";
+ notice = noticeService.getById(noticeId);
+ if (null == notice) return JsonResult.error("未找到原始记录,请重新查询后在操作。");
+ if (notice.getSendcnt() > 0) return JsonResult.error("公告已发布,不能修改");
+ }
+ if (StringUtil.isEmpty(title)) return JsonResult.error("公告标题不能为空");
+ if (StringUtil.isEmpty(content)) return JsonResult.error("公告内容不能为空");
+ if (!ConstantUtil.PHONE_PLATFORM_ALL.equals(platform) && !ConstantUtil.PHONE_PLATFORM_ANDROID.equals(platform) && !ConstantUtil.PHONE_PLATFORM_IOS.equals(platform))
+ return JsonResult.error("请选择推送终端");
+ notice.setPlatform(platform);
+ notice.setTitle(title.trim());
+ notice.setContent(content.trim());
+ if (StringUtil.isEmpty(linkurl)) {
+ notice.setLinkurl(null);
+ } else {
+ notice.setLinkurl(linkurl.trim());
+ }
+
+ if (noticeService.doSaveOrUpdateNotice(notice)) {
+ return JsonResult.ok(prefix + "成功!");
+ } else {
+ return JsonResult.error(prefix + "失败!");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ if (StringUtil.isEmpty(noticeId)) {
+ return JsonResult.error("新增失败,业务异常!").put("exception", e);
+ } else {
+ return JsonResult.error("修改失败,业务异常!").put("exception", e);
+ }
+ }
+ }
+
+ @PostMapping("/delete")
+ @PreAuthorize("hasPermission('/notice/delete','')")
+ @ResponseBody
+ public JsonResult delete(@RequestParam("noticeId") String noticeId) {
+ try {
+ TNotice notice = noticeService.getById(noticeId);
+ if (null == notice) return JsonResult.error("未找到原始记录,请重新查询后在操作。");
+ if (notice.getSendcnt() > 0) return JsonResult.error("公告已发布,不能删除!");
+ if (noticeService.deleteNotice(notice)) {
+ return JsonResult.ok("删除成功!");
+ } else {
+ return JsonResult.error("删除失败!");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return JsonResult.error("删除失败,业务异常!").put("exception", e);
+ }
+ }
+
+ @GetMapping("/load4publish")
+ @PreAuthorize("hasPermission('/notice/load4publish','')")
+ public String load4Publish() {
+ return "system/notice/publish";
+ }
+
+ @PostMapping("/publish")
+ @PreAuthorize("hasPermission('/notice/publish','')")
+ @ResponseBody
+ public JsonResult publish(@RequestParam("noticeId") String noticeId,
+ @RequestParam("pushmode") String pushmode,
+ @RequestParam(value = "settime", required = false) String settime,
+ @AuthenticationPrincipal UserDetails operUser) {
+ try {
+ TOperator oper = (TOperator) operUser;
+ TNotice notice = noticeService.getById(noticeId);
+ if (null == notice) return JsonResult.error("未找到原始记录,请重新查询后在操作。");
+ if (!ConstantUtil.PHONE_NOTICE_PUSHMODE_ATONCE.equals(pushmode) && !ConstantUtil.PHONE_NOTICE_PUSHMODE_DELAY.equals(pushmode))
+ return JsonResult.error("请选择是立即发布还是定时发布");
+ if (!ConstantUtil.PHONE_NOTICE_PUSHMODE_ATONCE.equals(pushmode) && !DateUtil.checkDatetimeValid(settime, "yyyy-MM-dd HH:mm:ss"))
+ return JsonResult.error("定时发布请设定正确的发布时间");
+ if (!ConstantUtil.PHONE_NOTICE_PUSHMODE_ATONCE.equals(pushmode) && DateUtil.compareDatetime(DateUtil.unParseToDateFormat(settime), systemUtilService.getSysdatetime().getHostdatetime(), DateUtil.DATETIME_FMT) <= 0)
+ return JsonResult.error("设定的发布时间必须比当前时间大");
+
+ TNoticeMsg msg = noticeService.doPublishNotice(notice.getId(), pushmode, settime, oper);
+ if (ConstantUtil.PHONE_NOTICE_PUSHMODE_ATONCE.equals(pushmode)) {
+ kafkaSendMsgService.sendNoticeMessage(msg.getMsgid()); //异步立即推送
+ }
+ return JsonResult.ok("发布成功!");
+ } catch (Exception e) {
+ e.printStackTrace();
+ return JsonResult.error("发布失败,业务异常!").put("exception", e);
+ }
+ }
+
+ @GetMapping("/load4detail")
+ @PreAuthorize("hasPermission('/notice/load4detail','')")
+ public String load4PublishDetails() {
+ return "system/notice/details";
+ }
+
+ @GetMapping("/detaillist")
+ @PreAuthorize("hasPermission('/notice/load4detail','')")
+ @ResponseBody
+ public PageResult<TNoticeMsg> searchNoticePublishDetails(@RequestParam("noticeId") String noticeId) {
+ try {
+ return noticeService.getNoticePublishDetails(noticeId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return new PageResult<>(99, "系统查询错误");
+ }
+ }
+
+
+
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/system/service/NoticeService.java b/payapi/src/main/java/com/supwisdom/dlpay/system/service/NoticeService.java
new file mode 100644
index 0000000..1bb9425
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/system/service/NoticeService.java
@@ -0,0 +1,34 @@
+package com.supwisdom.dlpay.system.service;
+
+import com.supwisdom.dlpay.framework.domain.TNotice;
+import com.supwisdom.dlpay.framework.domain.TNoticeMsg;
+import com.supwisdom.dlpay.framework.domain.TOperator;
+import com.supwisdom.dlpay.framework.util.PageResult;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+public interface NoticeService {
+ @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = true)
+ PageResult<TNotice> getNotice(String startdate, String enddate, String searchkey, int pageNo, int pageSize);
+
+ @Transactional(rollbackFor = Exception.class, readOnly = true)
+ TNotice getById(String id);
+
+ @Transactional(rollbackFor = Exception.class)
+ boolean doSaveOrUpdateNotice(TNotice notice);
+
+ @Transactional(rollbackFor = Exception.class)
+ boolean deleteNotice(TNotice notice);
+
+ @Transactional(rollbackFor = Exception.class)
+ TNoticeMsg doPublishNotice(String noticeId, String pushmode, String settime, TOperator oper)throws Exception;
+
+ @Transactional(rollbackFor = Exception.class, readOnly = true)
+ PageResult<TNoticeMsg> getNoticePublishDetails(String noticeId);
+
+ @Transactional(rollbackFor = Exception.class, readOnly = true)
+ List<TNoticeMsg> getDelayNoticeByDatetime(String datetime);
+
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/system/service/impl/NoticeServiceImpl.java b/payapi/src/main/java/com/supwisdom/dlpay/system/service/impl/NoticeServiceImpl.java
new file mode 100644
index 0000000..7f26776
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/system/service/impl/NoticeServiceImpl.java
@@ -0,0 +1,128 @@
+package com.supwisdom.dlpay.system.service.impl;
+
+import com.supwisdom.dlpay.framework.dao.NoticeDao;
+import com.supwisdom.dlpay.framework.dao.NoticeMsgDao;
+import com.supwisdom.dlpay.framework.domain.TNotice;
+import com.supwisdom.dlpay.framework.domain.TNoticeMsg;
+import com.supwisdom.dlpay.framework.domain.TOperator;
+import com.supwisdom.dlpay.framework.service.SystemUtilService;
+import com.supwisdom.dlpay.framework.util.DateUtil;
+import com.supwisdom.dlpay.framework.util.PageResult;
+import com.supwisdom.dlpay.framework.util.StringUtil;
+import com.supwisdom.dlpay.framework.util.TradeDict;
+import com.supwisdom.dlpay.system.service.NoticeService;
+import com.supwisdom.dlpay.util.ConstantUtil;
+import com.supwisdom.dlpay.util.WebCheckException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class NoticeServiceImpl implements NoticeService {
+ @Autowired
+ private NoticeDao noticeDao;
+ @Autowired
+ private NoticeMsgDao noticeMsgDao;
+ @Autowired
+ private SystemUtilService systemUtilService;
+
+ @Override
+ public PageResult<TNotice> getNotice(String startdate, String enddate, String searchkey, int pageNo, int pageSize) {
+ Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Direction.DESC, "createdate"));
+ Page<TNotice> page = noticeDao.findAll(new Specification<TNotice>() {
+ @Override
+ public Predicate toPredicate(Root<TNotice> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+ List<Predicate> predicates = new ArrayList<>();
+ if (!StringUtil.isEmpty(startdate)) {
+ predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createdate").as(String.class), DateUtil.unParseToDateFormat(startdate)));
+ }
+ if (!StringUtil.isEmpty(enddate)) {
+ predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("createdate").as(String.class), DateUtil.unParseToDateFormat(enddate)));
+ }
+ if (!StringUtil.isEmpty(searchkey)) {
+ predicates.add(criteriaBuilder.like(root.get("title").as(String.class), "%" + searchkey.trim() + "%"));
+ }
+ return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
+ }
+ }, pageable);
+ return new PageResult<>(page);
+ }
+
+ @Override
+ public TNotice getById(String id) {
+ if (!StringUtil.isEmpty(id)) return noticeDao.getById(id.trim());
+ return null;
+ }
+
+ @Override
+ public boolean doSaveOrUpdateNotice(TNotice notice) {
+ if (null != notice) {
+ notice.setLastsaved(systemUtilService.getSysdatetime().getHostdatetime());
+ noticeDao.save(notice);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean deleteNotice(TNotice notice) {
+ if (null != notice) {
+ noticeDao.delete(notice);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public TNoticeMsg doPublishNotice(String noticeId, String pushmode, String settime, TOperator oper) throws Exception {
+ TNotice notice = noticeDao.getByIdForUpdate(noticeId);
+ if (null == notice) throw new WebCheckException("未找到原始记录,请重新查询后在操作。");
+
+ TNoticeMsg msg = new TNoticeMsg();
+ msg.setNoticeId(notice.getId());
+ msg.setAlltarget(true);
+ msg.setPushmode(pushmode);
+ if (!ConstantUtil.PHONE_NOTICE_PUSHMODE_ATONCE.equals(pushmode)) {
+ msg.setPushSettime(DateUtil.unParseToDateFormat(settime));
+ }
+ msg.setPublisher(oper.getOpername());
+ msg.setOperid(oper.getOperid());
+ msg.setStatus(TradeDict.STATUS_NORMAL);
+ msg.setSendkafka(false);
+ msg.setCreatetime(systemUtilService.getSysdatetime().getHostdatetime());
+ msg.setTenantId(notice.getTenantId());
+ msg.setPushresult("待推送");
+
+ notice.setSendcnt(notice.getSendcnt() + 1);
+ notice.setLastsaved(systemUtilService.getSysdatetime().getHostdatetime());
+ noticeDao.save(notice);
+ return noticeMsgDao.save(msg);
+ }
+
+ @Override
+ public PageResult<TNoticeMsg> getNoticePublishDetails(String noticeId) {
+ if (!StringUtil.isEmpty(noticeId)) {
+ List<TNoticeMsg> list = noticeMsgDao.findAllByNoticeId(noticeId);
+ return new PageResult<>(list);
+ }
+ return new PageResult<>(99, "无数据");
+ }
+
+ @Override
+ public List<TNoticeMsg> getDelayNoticeByDatetime(String datetime) {
+ List<TNoticeMsg> list = noticeMsgDao.findAllDelayNoticeByDatetime(datetime);
+ if (!StringUtil.isEmpty(list)) return list;
+ return new ArrayList<>(0);
+ }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/util/ConstantUtil.java b/payapi/src/main/java/com/supwisdom/dlpay/util/ConstantUtil.java
index ba129e6..4f6d410 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/util/ConstantUtil.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/util/ConstantUtil.java
@@ -91,4 +91,17 @@
public static final String OPERCHK_CHKMODE_DELETE = "删除";
public static final String CHKFILE_DELIMITER = "|";
+
+ public static final String PHONE_PLATFORM_ALL = "all";
+ public static final String PHONE_PLATFORM_ANDROID = "android";
+ public static final String PHONE_PLATFORM_IOS = "ios";
+
+ public static final String PHONE_NOTICE_PUSHMODE_ATONCE = "atonce"; //立即发布
+ public static final String PHONE_NOTICE_PUSHMODE_DELAY = "delay"; //延时发布
+
+ /**
+ * kafka消息类型
+ * */
+ public static final String KAFKA_MAGTYPE_NOTICE = "dlsmk_phone_notice";
+ public static final String KAFKA_MAGTYPE_CONSUME = "dlsmk_card_consume";
}
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
index d993aea..f489996 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
@@ -4,6 +4,7 @@
import io.lettuce.core.ReadFrom
import net.javacrumbs.shedlock.core.LockProvider
import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider
+import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.SpringApplication
@@ -148,7 +149,7 @@
}
}
-
+@EnableSchedulerLock(defaultLockAtMostFor = "PT30M")
@SpringBootApplication
@EnableDiscoveryClient
@EnableScheduling
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_service.kt
index 4278bf0..b601151 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_service.kt
@@ -14,11 +14,12 @@
import org.springframework.stereotype.Component
interface CitizencardPayService {
- fun bindCard(bankcardno: String, username: String, idtype: String, idno: String, phone: String): DlpayResp
+ fun bindCard(bankcardno: String, username: String, idtype: String, idno: String,
+ phone: String): DlpayResp
- fun signCard(bankcardno: String, username: String, idtype: String, idno: String, phone: String, transtype: String): DlpayResp
+ fun signCard(bankcardno: String, username: String, idtype: String, idno: String, phone: String, transtype: String,captcha:String): DlpayResp
- fun cardPay(shopaccno: String, userid: String, accdate: String, amount: Int, refno: String): DlpayResp
+ fun cardPay(shopaccno: String, userid: String, accdate: String, amount: Int, scenario: String, refno: String): DlpayResp
fun cardPayRefund(refno: String, accdate: String, orignRefno: String, amount: Int): DlpayResp
@@ -52,7 +53,7 @@
override fun pay(transaction: TTransactionMain): AgentResponse<DtlStatus> {
val resp = citizencardPayService.cardPay(transaction.shopDtl.shopaccno, transaction.personDtl.userid, transaction.accdate,
- Math.abs(MoneyUtil.YuanToFen(transaction.personDtl.amount)), transaction.refno)
+ Math.abs(MoneyUtil.YuanToFen(transaction.personDtl.amount)), transaction.dtltype, transaction.refno)
return AgentResponse<DtlStatus>().also {
val code = agentCode(resp.code, resp.message)
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/citizencard_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/citizencard_service_impl.kt
index 0601599..aa5c0ae 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/citizencard_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/citizencard_service_impl.kt
@@ -101,7 +101,7 @@
}
}
- override fun signCard(bankcardno: String, username: String, idtype: String, idno: String, phone: String, transtype: String): DlpayResp {
+ override fun signCard(bankcardno: String, username: String, idtype: String, idno: String, phone: String, transtype: String,captcha:String): DlpayResp {
var resp = DlpayResp()
val config = sourceTypeService.getChargePaytypeConfig(TradeDict.PAYTYPE_CITIZEN_CARD, true)
if (!checkCitizencardConfig(config, resp)) {
@@ -135,6 +135,7 @@
params["idtype"] = idType.toString()
params["idno"] = idno
params["phone"] = phone
+ params["captcha"] = captcha
params["sign_type"] = "MD5"
val sign = MD5.encodeByMD5(StringUtil.createLinkString(StringUtil.paraFilter(params)) + config[YnrccUtil.YNRCC_SIGNKEY]!!.trim())
params["sign"] = sign
@@ -161,7 +162,7 @@
}
}
- override fun cardPay(shopaccno: String, userid: String, accdate: String, amount: Int, refno: String): DlpayResp {
+ override fun cardPay(shopaccno: String, userid: String, accdate: String, amount: Int, scenario: String, refno: String): DlpayResp {
var resp = DlpayResp()
val config = sourceTypeService.getConsumePaytypeConfig(TradeDict.PAYTYPE_CITIZEN_CARD, shopaccno, false, false)
if (!checkCitizencardConfig(config, resp)) {
@@ -216,6 +217,7 @@
params["merchant_bankcardno"] = merchantBankcardno!!
params["merchant_bankaccname"] = merchantBankaccname!!
params["amount"] = amount.toString()
+ params["scenario"] = scenario
params["description"] = "市民卡代扣消费"
params["sign_type"] = "MD5"
val sign = MD5.encodeByMD5(StringUtil.createLinkString(StringUtil.paraFilter(params)) + config[YnrccUtil.YNRCC_SIGNKEY]!!.trim())
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/async_tasks.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/async_tasks.kt
index e771add..f01d870 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/async_tasks.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/async_tasks.kt
@@ -71,6 +71,15 @@
}
}
+ @Bean(name = ["kafkaSendMessageAsyncTask"])
+ fun kafkaSendMessageAsyncTaskExecutor(): Executor {
+ return ThreadPoolTaskExecutor().apply {
+ corePoolSize = 20
+ maxPoolSize = 100
+ setWaitForTasksToCompleteOnShutdown(true)
+ }
+ }
+
override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler? {
return MyAsyncUncaughtExceptionHandler()
}
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_task.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_task.kt
index 1d8e2fc..e7caa77 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_task.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_task.kt
@@ -7,10 +7,12 @@
import com.supwisdom.dlpay.api.repositories.ShopaccService
import com.supwisdom.dlpay.api.service.ConsumePayService
import com.supwisdom.dlpay.api.service.DtlQueryResultService
+import com.supwisdom.dlpay.api.service.KafkaSendMsgService
import com.supwisdom.dlpay.api.service.TransactionServiceProxy
import com.supwisdom.dlpay.framework.service.SystemUtilService
import com.supwisdom.dlpay.framework.util.ApplicationUtil
import com.supwisdom.dlpay.framework.util.TradeDict
+import com.supwisdom.dlpay.system.service.NoticeService
import com.supwisdom.dlpay.util.ConstantUtil
import net.javacrumbs.shedlock.core.SchedulerLock
import org.springframework.beans.factory.annotation.Autowired
@@ -150,4 +152,34 @@
}
}
}
+}
+
+/**
+ * 定时通知公告的推送任务
+ * */
+@Component
+class NoticePushMessageTask {
+ @Autowired
+ private lateinit var noticeService: NoticeService
+ @Autowired
+ private lateinit var systemUtilService: SystemUtilService
+ @Autowired
+ private lateinit var kafkaSendMsgService: KafkaSendMsgService
+
+ @Scheduled(cron = "\${send.delay.notice.task.cron:-}")
+ @SchedulerLock(name = "SendDelayNoticeTask", lockAtMostForString = "PT10M")
+ fun doSendDelayNotice() {
+ try {
+ val hostdatetime = systemUtilService.sysdatetime.hostdatetime
+ noticeService.getDelayNoticeByDatetime(hostdatetime).forEach {
+ try {
+ kafkaSendMsgService.sendNoticeMessage(it.msgid)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ } catch (ex: Exception) {
+ ex.printStackTrace()
+ }
+ }
}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/kafka_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/kafka_service_impl.kt
index 290bfaa..c913448 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/kafka_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/kafka_service_impl.kt
@@ -3,11 +3,17 @@
import com.google.gson.Gson
import com.supwisdom.dlpay.api.bean.KafkaXgMessage
import com.supwisdom.dlpay.api.service.KafkaSendMsgService
+import com.supwisdom.dlpay.framework.dao.NoticeDao
+import com.supwisdom.dlpay.framework.dao.NoticeMsgDao
+import com.supwisdom.dlpay.framework.service.SystemUtilService
import com.supwisdom.dlpay.framework.util.DateUtil
+import com.supwisdom.dlpay.framework.util.StringUtil
import com.supwisdom.dlpay.framework.util.TradeDict
import com.supwisdom.dlpay.mobile.dao.MsgDao
import com.supwisdom.dlpay.mobile.domain.TBMsg
import com.supwisdom.dlpay.mobile.service.MobileApiService
+import com.supwisdom.dlpay.util.ConstantUtil
+import com.supwisdom.dlpay.util.WebCheckException
import mu.KotlinLogging
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.kafka.core.KafkaTemplate
@@ -15,19 +21,26 @@
import org.springframework.stereotype.Service
@Service
-class KafkaSendMsgServiceImpl:KafkaSendMsgService{
+class KafkaSendMsgServiceImpl : KafkaSendMsgService {
val logger = KotlinLogging.logger { }
@Autowired
private lateinit var kafkaTemplate: KafkaTemplate<String, String>
@Autowired
private lateinit var msgDao: MsgDao
@Autowired
+ private lateinit var noticeMsgDao: NoticeMsgDao
+ @Autowired
+ private lateinit var noticeDao: NoticeDao
+ @Autowired
private lateinit var mobileApiService: MobileApiService
+ @Autowired
+ private lateinit var systemUtilService: SystemUtilService
+
val gson = Gson()
val topic = "jpush-messages"
- @Async
+ @Async("kafkaSendMessageAsyncTask")
override fun sendJpushMessage(userid: String, title: String, content: String, refno: String, extras: MutableMap<String, String>, tenantId: String?) {
val musers = mobileApiService.findByUseridAndStatus(userid, TradeDict.STATUS_NORMAL)
var msg = TBMsg().apply {
@@ -63,16 +76,60 @@
message.custom = gson.toJson(extras)
message.expiretime = DateUtil.getNewTime(DateUtil.getNow(), 300)
message.gids = it.uid
- if(it.lastloginplatform.isNullOrEmpty()){
- message.platform="ios"
+ if (it.lastloginplatform.isNullOrEmpty()) {
+ message.platform = "all"
kafkaTemplate.send(topic, msg.msgid, gson.toJson(message))
- message.platform="android"
- kafkaTemplate.send(topic, msg.msgid, gson.toJson(message))
- }else{
+// message.platform="ios"
+// kafkaTemplate.send(topic, msg.msgid, gson.toJson(message))
+// message.platform="android"
+// kafkaTemplate.send(topic, msg.msgid, gson.toJson(message))
+ } else {
kafkaTemplate.send(topic, msg.msgid, gson.toJson(message))
}
}
msg.pusheduids = uids
msgDao.save(msg)
}
+
+ @Async("kafkaSendMessageAsyncTask")
+ override fun sendNoticeMessage(noticeMsgid: String) {
+ val noticeMsg = noticeMsgDao.getByMsgidForUpdateNowait(noticeMsgid) ?: return
+ try {
+ if (noticeMsg.sendkafka) return
+ val notice = noticeDao.getById(noticeMsg.noticeId) ?: throw WebCheckException("未找到对应的通知公告")
+
+ val extras = mutableMapOf<String, String>("msgid" to noticeMsg.msgid, "msg_type" to ConstantUtil.KAFKA_MAGTYPE_NOTICE)
+ if (!StringUtil.isEmpty(notice.linkurl)) extras["url"] = notice.linkurl
+ val message = KafkaXgMessage().apply {
+ this.title = notice.title
+ this.content = notice.content
+ this.alltarget = true //gids不需要传递
+ this.platform = notice.platform
+ this.type = 1 //TYPE_NOTIFICATION = 1,TYPE_MESSAGE = 2
+ this.callback = true
+ this.retries = 3
+ this.custom = gson.toJson(extras)
+ }
+ println(gson.toJson(message))
+ kafkaTemplate.send(topic, noticeMsg.msgid, gson.toJson(message))
+
+ noticeMsg.sendkafka = true
+ noticeMsg.pushtime = systemUtilService.sysdatetime.hostdatetime
+ noticeMsg.pushresult = "已推送"
+ noticeMsgDao.save(noticeMsg)
+ } catch (wex: WebCheckException) {
+ noticeMsg.sendkafka = false
+ noticeMsg.pushtime = systemUtilService.sysdatetime.hostdatetime
+ noticeMsg.pushresult = "推送失败! ${wex.message}"
+ noticeMsgDao.save(noticeMsg)
+ return
+ } catch (e: Exception) {
+ e.printStackTrace()
+ noticeMsg.sendkafka = false
+ noticeMsg.pushtime = systemUtilService.sysdatetime.hostdatetime
+ noticeMsg.pushresult = "推送失败"
+ noticeMsgDao.save(noticeMsg)
+ return
+ }
+ }
}
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/kafka_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/kafka_service.kt
index a5cc0e8..efd45f7 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/kafka_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/kafka_service.kt
@@ -1,5 +1,6 @@
package com.supwisdom.dlpay.api.service
+import com.supwisdom.dlpay.framework.dao.NoticeMsgDao
import com.supwisdom.dlpay.mobile.dao.MsgDao
import mu.KotlinLogging
import org.apache.kafka.clients.consumer.ConsumerRecord
@@ -9,22 +10,38 @@
interface KafkaSendMsgService {
fun sendJpushMessage(userid: String, title: String, content: String, refno: String, extras: MutableMap<String, String>, tenantId: String?)
+
+ fun sendNoticeMessage(noticeMsgid: String)
}
+
@Component
-class KafkaMsgListener{
+class KafkaMsgListener {
val logger = KotlinLogging.logger { }
@Autowired
private lateinit var msgDao: MsgDao
+ @Autowired
+ private lateinit var noticeMsgDao: NoticeMsgDao
- @KafkaListener(topics = ["jpush-messages-result"],autoStartup = "\${spring.kafka.listen.auto.start:true}")
- fun listen(record :ConsumerRecord<String, String>) {
+ @KafkaListener(topics = ["jpush-messages-result"],
+ autoStartup = "\${spring.kafka.listen.auto.start:true}",
+ groupId = "0")
+ fun listen(record: ConsumerRecord<String, String>) {
logger.debug { "${record.key()},${record.value()}" }
- if(!record.key().isNullOrEmpty()){
- val opt = msgDao.findById(record.key())
- if(opt.isPresent){
+ if (!record.key().isNullOrEmpty()) {
+ val opt = msgDao.findById(record.key())
+ if (opt.isPresent) {
var msg = opt.get()
msg.pushresult = record.value()
msgDao.save(msg)
+ return
+ }
+
+ val noticemsg = noticeMsgDao.findById(record.key())
+ if (noticemsg.isPresent) {
+ var nmsg = noticemsg.get()
+ nmsg.pushresult = record.value()
+ noticeMsgDao.save(nmsg)
+ return
}
}
}
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
index 0f6f5d6..8854b19 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
@@ -36,6 +36,7 @@
import org.springframework.web.multipart.MultipartFile
import java.time.Duration
import java.util.*
+import kotlin.collections.HashMap
@RestController
@@ -236,6 +237,14 @@
?.put("signed", signed)
?.put("tenantid", "mobile")!!
}
+
+ /**
+ *
+ * 小程序认证接口
+ *
+ * */
+
+
}
@@ -403,27 +412,32 @@
if (exsitUser != null) {
return JsonResult.error("该银行卡号已被绑定,若您本人绑定,请先解除绑定,若非本人,请联系客服")
}
+ var signed = ""
//call api
var resp = citizencardPayService.bindCard(cardno, name, idtype, idno, phone)
if (resp.code != "0000") {
return JsonResult.error(resp.message)
}
- var signed = ""
+ var needupdate = false
if (resp.sinstatus == "1") {
signed = TradeDict.STATUS_YES
- card.signed = true
+ if(!card.signed){
+ card.signed = true
+ mobileApiService.saveCard(card)
+ }
user.signedtime = DateUtil.getNow();
- mobileApiService.saveCard(card)
+ mobileApiService.saveUser(user)
+ needupdate = true;
+
+ }
+ if( user.userid.isNullOrEmpty()){
+ user.userid = person.userid
+ user.bindtime = DateUtil.getNow()
+ needupdate=true
+ }
+ if(needupdate){
mobileApiService.saveUser(user)
}
- logger.error { resp.captcha }
- var code = if (resp.captcha.isNullOrEmpty()) {
- RandomUtils.randomNumber(6)
- } else {
- resp.captcha
- }
- logger.error { code }
- redisTemplate.opsForValue().set(phone, code, Duration.ofMinutes(10))
var payseted = false
if (!user.paypwd.isNullOrEmpty()) {
payseted = true
@@ -436,6 +450,42 @@
}
+ /**
+ * 绑卡
+ * */
+ @RequestMapping("/bindcardcode")
+ fun bindcardcode(): JsonResult {
+ val p = SecurityContextHolder.getContext().authentication
+ val user = mobileApiService.findUserById(p.name)
+ ?: return JsonResult.error("用户不存在,请注册")
+ if (user.phone.isNullOrEmpty()) {
+ return JsonResult.error("手机号不存在,请注册")
+ }
+ var card = mobileApiService.findCardByUserid(user.userid!!)
+ ?: return JsonResult.error("卡片不存在,请重新绑定")
+ //call sign api
+ val person = userService.findOnePersonByUserid(card.userid)
+ var signed=""
+ //call api
+ var resp = citizencardPayService.bindCard(card.cardno, person.name, person.idtype, person.idno, user.phone!!)
+ if (resp.code != "0000") {
+ return JsonResult.error(resp.message)
+ }
+
+ if (resp.sinstatus == "1") {
+ signed = TradeDict.STATUS_YES
+ if(!card.signed){
+ card.signed = true
+ mobileApiService.saveCard(card)
+ }
+ user.signedtime = DateUtil.getNow();
+ mobileApiService.saveUser(user)
+ }
+
+ return JsonResult.ok("OK")
+ ?.put("signed", signed)!!
+
+ }
/**
* 支付密码
@@ -550,7 +600,8 @@
?: return JsonResult.error("卡片不存在,请重新绑定")
//call sign api
val person = userService.findOnePersonByUserid(card.userid)
- var resp = citizencardPayService.signCard(card.cardno, person.name, person.idtype, person.idno, user.phone!!, YnrccUtil.TRANSTYPE_SIGNCARD)
+ val captcha = agree//此处为验证码,暂由此参数代替
+ var resp = citizencardPayService.signCard(card.cardno, person.name, person.idtype, person.idno, user.phone!!, YnrccUtil.TRANSTYPE_SIGNCARD,captcha)
if (resp.code != "0000") {
return JsonResult.error(resp.message)
}
@@ -570,7 +621,7 @@
* 查询账单
* */
@RequestMapping("/bills")
- fun bills(pageno: Int): JsonResult {
+ fun bills(pageno: Int,platform: String?): JsonResult {
val p = SecurityContextHolder.getContext().authentication
val user = mobileApiService.findUserById(p.name)
?: return JsonResult.error("用户不存在,请注册")
@@ -615,6 +666,46 @@
signed = TradeDict.STATUS_YES
}
}
+ var version:String?=""
+ var minversion:String?=""
+ var versionmsg:String?=""
+ var versionurl:String?=""
+ if(!platform.isNullOrEmpty()){
+ var map= mutableMapOf<String,Any>()
+ if(platform.toLowerCase().contains("android")){
+ dictionaryProxy.refreshDictionary("androidapp")
+ map = dictionaryProxy.getDictionaryAsMap("androidapp")
+ if(map["androidversion"]!=null){
+ version = map["androidversion"] as String
+ }
+ if(map["androidminversion"]!=null){
+ minversion = map["androidminversion"] as String
+ }
+ if(map["androidversionmsg"]!=null){
+ versionmsg = map["androidversionmsg"] as String
+ }
+ if(map["androidversionurl"]!=null){
+ versionurl = map["androidversionurl"] as String
+ }
+
+ }else if(platform.toLowerCase().contains("iphone")){
+ map = dictionaryProxy.getDictionaryAsMap("iosapp")
+ if(map["iosversion"]!=null){
+ version = map["iosversion"] as String
+ }
+ if(map["iosminversion"]!=null){
+ minversion = map["iosminversion"] as String
+ }
+ if(map["iosversionmsg"]!=null){
+ versionmsg = map["iosversionmsg"] as String
+ }
+ if(map["iosversionurl"]!=null){
+ versionurl = map["iosversionurl"] as String
+ }
+ }
+
+ }
+
var name = person.name
val page = userService.findPersondtlByUserid(user.userid!!, no)
return JsonResult.ok("OK").put("page", page)
@@ -626,9 +717,10 @@
?.put("name", name)
?.put("needrebind", needrebind)
?.put("signed", signed)
- ?.put("version","1")
- ?.put("minversion","1")
- ?.put("versionmsg","1")
+ ?.put("version",version)
+ ?.put("minversion",minversion)
+ ?.put("versionmsg",versionmsg)
+ ?.put("versionurl",versionurl)
?.put("userid", if (user.userid.isNullOrEmpty()) "" else user.userid)!!.put("t", t)!!
}
@@ -783,7 +875,8 @@
?: return JsonResult.error("银行卡不存在,不能解除代扣协议")
//call sign api
val person = userService.findOnePersonByUserid(card.userid)
- var resp = citizencardPayService.signCard(card.cardno, person.name, person.idtype, person.idno, user.phone!!, YnrccUtil.TRANSTYPE_UNSIGNCARD)
+ val captcha = ""//此处为验证码,暂由此参数代替
+ var resp = citizencardPayService.signCard(card.cardno, person.name, person.idtype, person.idno, user.phone!!, YnrccUtil.TRANSTYPE_UNSIGNCARD,captcha)
if (resp.code != "0000") {
return JsonResult.error(resp.message)
}
diff --git a/payapi/src/main/resources/application.properties b/payapi/src/main/resources/application.properties
index 0a2b535..091b5cf 100644
--- a/payapi/src/main/resources/application.properties
+++ b/payapi/src/main/resources/application.properties
@@ -35,8 +35,10 @@
query.third.transdtl.result.cron=7 0/1 * * * ?
payapi.sourcetype.checker.scheduler=7 3/10 * * * ?
citizencard.dolosstask.cron=-
+send.delay.notice.task.cron=29 0/1 * * * ?
################################################
# user password
auth.password.bcrypt.length=10
###################################################
-spring.redis.database=0
\ No newline at end of file
+spring.redis.database=0
+multi-tenant.datasource.base-package=com.supwisdom.dlpay
\ No newline at end of file
diff --git a/payapi/src/main/resources/data.sql b/payapi/src/main/resources/data.sql
index 7244bf3..e2c3fac 100644
--- a/payapi/src/main/resources/data.sql
+++ b/payapi/src/main/resources/data.sql
@@ -77,6 +77,8 @@
VALUES (37, NULL, 1, NULL, '', '/user/card', '市民卡查询', 1, 19, '{tenantid}');
INSERT INTO "tb_function" ("id", "createtime", "isleaf", "lastsaved", "menuicon", "menuurl", "name", "ordernum", "parentid", tenantid)
VALUES (38, NULL, 1, NULL, '', '/report/shoptodaybusiness', '商户当天统计表', 4, 20, '{tenantid}');
+INSERT INTO "tb_function" ("id", "createtime", "isleaf", "lastsaved", "menuicon", "menuurl", "name", "ordernum", "parentid", tenantid)
+VALUES (39, NULL, 1, NULL, '', '/notice/index', '手机通知公告', 6, 3, '{tenantid}');
INSERT INTO "tb_role_function" ("id", "functionid", "roleid", tenantid)
@@ -145,6 +147,8 @@
VALUES ('ff8080816db87e27016db88be41f0015', 37, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
INSERT INTO "tb_role_function" ("id", "functionid", "roleid", tenantid)
VALUES ('4028ee9f6e5d95d8016e5d99e8d50012', 38, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
+INSERT INTO "tb_role_function" ("id", "functionid", "roleid", tenantid)
+VALUES ('ff8080816f8d8258016f8d85e4d70005', 39, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
INSERT INTO "tb_resource" ("id", "code", "function_id", "name", "uri", tenantid)
@@ -311,6 +315,21 @@
VALUES (94, '', 37, '修改跳转', '/user/load4modifycard', '{tenantid}');
INSERT INTO "tb_resource" ("id", "code", "function_id", "name", "uri", tenantid)
VALUES (95, '', 37, '修改', '/user/cardupdate', '{tenantid}');
+INSERT INTO "tb_resource" ("id", "code", "function_id", "name", "uri", tenantid)
+VALUES (96, '', 39, '查询', '/notice/index', '{tenantid}');
+INSERT INTO "tb_resource" ("id", "code", "function_id", "name", "uri", tenantid)
+VALUES (97, '', 39, '新增修改跳转', '/notice/load4add', '{tenantid}');
+INSERT INTO "tb_resource" ("id", "code", "function_id", "name", "uri", tenantid)
+VALUES (98, '', 39, '新增修改', '/notice/edit', '{tenantid}');
+INSERT INTO "tb_resource" ("id", "code", "function_id", "name", "uri", tenantid)
+VALUES (99, '', 39, '删除', '/notice/delete', '{tenantid}');
+INSERT INTO "tb_resource" ("id", "code", "function_id", "name", "uri", tenantid)
+VALUES (100, '', 39, '发布跳转', '/notice/load4publish', '{tenantid}');
+INSERT INTO "tb_resource" ("id", "code", "function_id", "name", "uri", tenantid)
+VALUES (101, '', 39, '发布', '/notice/publish', '{tenantid}');
+INSERT INTO "tb_resource" ("id", "code", "function_id", "name", "uri", tenantid)
+VALUES (102, '', 39, '发布明细', '/notice/load4detail', '{tenantid}');
+
INSERT INTO "tb_permission" ("id", "resid", "role_func_id", "roleid", tenantid)
VALUES ('ff8080816b7947ed016b795577300036', 16, NULL, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
@@ -476,7 +495,20 @@
VALUES ('ff8080816ecaebf8016ecaeedcec0004', 94, NULL, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
INSERT INTO "tb_permission" ("id", "resid", "role_func_id", "roleid", "tenantid")
VALUES ('ff8080816ecafb4e016ecafd58400005', 95, NULL, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
-
+INSERT INTO "tb_permission" ("id", "resid", "role_func_id", "roleid", "tenantid")
+VALUES ('ff8080816f8d8258016f8d85e4e20006', 96, NULL, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
+INSERT INTO "tb_permission" ("id", "resid", "role_func_id", "roleid", "tenantid")
+VALUES ('ff8080816fa2c2b7016fa2d809780030', 97, NULL, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
+INSERT INTO "tb_permission" ("id", "resid", "role_func_id", "roleid", "tenantid")
+VALUES ('ff8080816fa2c2b7016fa2d80978002e', 98, NULL, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
+INSERT INTO "tb_permission" ("id", "resid", "role_func_id", "roleid", "tenantid")
+VALUES ('ff8080816fa2c2b7016fa2d809780031', 99, NULL, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
+INSERT INTO "tb_permission" ("id", "resid", "role_func_id", "roleid", "tenantid")
+VALUES ('ff8080816fa2c2b7016fa2d809780032', 100, NULL, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
+INSERT INTO "tb_permission" ("id", "resid", "role_func_id", "roleid", "tenantid")
+VALUES ('ff8080816fa2c2b7016fa2d80978002f', 101, NULL, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
+INSERT INTO "tb_permission" ("id", "resid", "role_func_id", "roleid", "tenantid")
+VALUES ('ff8080816fa2c2b7016fa2d80977002d', 102, NULL, 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '{tenantid}');
INSERT INTO "tb_subject" ("subjid","subjno", "balflag", "displayflag", "endflag", "fsubjno", "opendate", "subjlevel", "subjname", "subjtype", "tenantid")
diff --git a/payapi/src/main/resources/static/libs/custom.js b/payapi/src/main/resources/static/libs/custom.js
index d034401..0307a2d 100644
--- a/payapi/src/main/resources/static/libs/custom.js
+++ b/payapi/src/main/resources/static/libs/custom.js
@@ -104,8 +104,9 @@
}
root.isempty = function (s) {
- if (s == null || s.length == 0)
+ if (undefined == s || null == s || '' == s || s.length == 0) {
return true;
- return /\s/.test(s);
+ }
+ return false;
}
}(window));
\ No newline at end of file
diff --git a/payapi/src/main/resources/templates/system/notice/add.html b/payapi/src/main/resources/templates/system/notice/add.html
new file mode 100644
index 0000000..1247eda
--- /dev/null
+++ b/payapi/src/main/resources/templates/system/notice/add.html
@@ -0,0 +1,85 @@
+<form id="notice-add-form" lay-filter="notice-add-form" class="layui-form model-form">
+ <input name="noticeId" id="hidden-notice-add-msgid" type="hidden"/>
+ <div class="layui-form-item">
+ <label class="layui-form-label"><span style="color: red;">*</span>推送终端</label>
+ <div class="layui-input-block">
+ <input type="radio" name="platform" value="all" title="全部" checked/>
+ <input type="radio" name="platform" value="android" title="安卓手机"/>
+ <input type="radio" name="platform" value="ios" title="苹果手机"/>
+ </div>
+ </div>
+ <div class="layui-form-item">
+ <label class="layui-form-label"><span style="color: red;">*</span>公告标题</label>
+ <div class="layui-input-block">
+ <input name="title" placeholder="请填写标题,不能超过16个字" type="text" class="layui-input" maxlength="16"
+ autocomplete="off" lay-verify="required" required/>
+ </div>
+ </div>
+ <div class="layui-form-item">
+ <label class="layui-form-label"><span style="color: red;">*</span>公告内容</label>
+ <div class="layui-input-block">
+ <textarea name="content" placeholder="请输入内容,不能超过200个字" autocomplete="off" maxlength="200" class="layui-textarea" rows="6" lay-verify="required" required></textarea>
+ </div>
+ </div>
+ <div class="layui-form-item">
+ <label class="layui-form-label">链接地址</label>
+ <div class="layui-input-block">
+ <textarea name="linkurl" placeholder="https://www.baidu.com" autocomplete="off" class="layui-textarea" lay-verify="linkurl"></textarea>
+ </div>
+ </div>
+
+ <div class="layui-form-item model-form-footer">
+ <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
+ <button class="layui-btn" lay-filter="notice-add-form-submit" lay-submit id="notice-add-submit-btn">保存</button>
+ </div>
+</form>
+
+<script>
+ layui.use(['layer', 'admin', 'form'], function () {
+ var layer = layui.layer;
+ var admin = layui.admin;
+ var form = layui.form;
+
+ form.render('radio');
+ form.verify({
+ linkurl: function (value, item) {
+ if ("" != value) {
+ if (!/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/.test(value)) {
+ return "链接格式不正确";
+ }
+ }
+ }
+ });
+
+ var bean = admin.getTempData('t_noticeTmp');
+ if (bean) {
+ form.val('notice-add-form', bean);
+ }
+ // 表单提交事件
+ form.on('submit(notice-add-form-submit)', function (data) {
+ layer.load(2);
+ var token = $("meta[name='_csrf_token']").attr("value");
+ var param = data.field;
+ param["_csrf"] = token;
+ admin.go('[[@{/notice/edit}]]', param, function (result) {
+ console.log(result);
+ layer.closeAll('loading');
+ if (result.code == 200) {
+ layer.msg(result.msg, {icon: 1});
+ admin.finishPopupCenter();
+ } else if (result.code == 401) {
+ layer.msg(result.msg, {icon: 2, time: 1500}, function () {
+ location.replace('[[@{/login}]]');
+ }, 1000);
+ return;
+ } else {
+ layer.msg(result.msg, {icon: 2});
+ }
+ }, function (ret) {
+ console.log(ret);
+ admin.errorBack(ret);
+ });
+ return false;
+ });
+ });
+</script>
\ No newline at end of file
diff --git a/payapi/src/main/resources/templates/system/notice/details.html b/payapi/src/main/resources/templates/system/notice/details.html
new file mode 100644
index 0000000..e2de243
--- /dev/null
+++ b/payapi/src/main/resources/templates/system/notice/details.html
@@ -0,0 +1,86 @@
+<form id="notice-publish-details" lay-filter="notice-publish-details" class="layui-form model-form" style="padding-top: 20px;">
+ <div class="layui-form-item">
+ <label class="layui-form-label">标题</label>
+ <div class="layui-input-block">
+ <input name="title" type="text" class="layui-input" readonly="readonly"/>
+ </div>
+ </div>
+ <div class="layui-form-item">
+ <label class="layui-form-label">内容</label>
+ <div class="layui-input-block">
+ <textarea name="content" class="layui-textarea" readonly="readonly" rows="5"></textarea>
+ </div>
+ </div>
+
+ <div class="layui-form-item">
+ <input type="hidden" name="noticeId" id="notice-publish-details-noticeId"/>
+ <div style="margin-left: 45px;" id="noticePublishDetailTableDiv">
+ <table class="layui-table" id="noticePublishDetailTable"
+ lay-filter="noticePublishDetailTable-filter"></table>
+ </div>
+ </div>
+
+ <div class="layui-form-item model-form-footer">
+ <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">关闭</button>
+ </div>
+</form>
+
+<script>
+ layui.use(['layer', 'admin', 'form', 'table'], function () {
+ var layer = layui.layer;
+ var admin = layui.admin;
+ var form = layui.form;
+ var table = layui.table;
+
+ var bean = admin.getTempData('t_noticePublishDetail');
+ if (bean) {
+ if (isempty(bean.linkurl)) {
+ form.val('notice-publish-details', {
+ noticeId: bean.id,
+ title: bean.title,
+ content: bean.content
+ });
+ } else {
+ form.val('notice-publish-details', {
+ noticeId: bean.id,
+ title: bean.title,
+ content: bean.content + '\n\n链接:' + bean.linkurl
+ });
+ }
+ }
+
+ table.render({
+ elem: '#noticePublishDetailTable',
+ url: '[[@{/notice/detaillist}]]',
+ where: {
+ noticeId: $("#notice-publish-details-noticeId").val()
+ },
+ page: false,
+ size: 'sm',
+ height: 275,
+ cols: [
+ [
+ {type: 'numbers', title: '编号', align: 'center', width: 65},
+ {field: 'publisher', title: '发布者', align: 'center', width: 100},
+ {
+ field: 'createtime', title: '发布时间', align: 'right', width: 150, templet: function (e) {
+ return admin.formatDate(e.createtime);
+ }
+ },
+ {
+ field: 'pushtime', title: '推送时间', align: 'right', width: 175, templet: function (e) {
+ if (null != e.pushtime) {
+ return admin.formatDate(e.pushtime);
+ } else if (null != e.pushSettime) {
+ return '<span style=\"color:red;\">预计</span> ' + admin.formatDate(e.pushSettime);
+ } else {
+ return '-';
+ }
+ }
+ },
+ {field: 'pushresult', title: '备注', align: 'center'},
+ ]
+ ]
+ });
+ });
+</script>
\ No newline at end of file
diff --git a/payapi/src/main/resources/templates/system/notice/index.html b/payapi/src/main/resources/templates/system/notice/index.html
new file mode 100644
index 0000000..02c000c
--- /dev/null
+++ b/payapi/src/main/resources/templates/system/notice/index.html
@@ -0,0 +1,250 @@
+<div class="layui-card">
+ <div class="layui-card-header">
+ <h2 class="header-title">手机通知公告</h2>
+ <span class="layui-breadcrumb pull-right">
+ <a href="#">系统中心</a>
+ <a><cite>手机通知公告</cite></a>
+ </span>
+ </div>
+ <div class="layui-card-body">
+ <div class="layui-form" lay-filter="notice-search-form">
+ <div class="layui-form-item" style="margin-bottom: 0;">
+ <div class="layui-inline">
+ <label class="layui-form-label">创建日期</label>
+ <div class="layui-input-inline">
+ <input type="text" name="startdate" id="notice-search-startdate" placeholder="起始日期"
+ autocomplete="off" class="layui-input"/>
+ </div>
+ <div class="layui-form-mid">-</div>
+ <div class="layui-input-inline">
+ <input type="text" name="enddate" id="notice-search-enddate" placeholder="截止日期"
+ autocomplete="off" class="layui-input"/>
+ </div>
+ </div>
+ <div class="layui-inline" style="margin-right: 20px;">
+ <label class="layui-form-label">标题</label>
+ <div class="layui-input-block" style="width: 265px;">
+ <input type="text" name="searchkey" id="notice-search-searchkey" maxlength="20"
+ autocomplete="off" class="layui-input"/>
+ </div>
+ </div>
+ <div class="layui-inline" style="margin-right: 20px;margin-top: 10px;">
+ <div class="layui-input-block" style="width: 200px;margin-left: 10px;">
+ <button id="notice-search-btn" class="layui-btn icon-btn"><i class="layui-icon"></i>搜 索
+ </button>
+ <button id="notice-search-btn-reset" class="layui-btn layui-btn-primary">清 空</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="layui-card-body">
+ <div class="layui-form toolbar">
+ <button class="layui-btn layui-btn-sm" id="btn-notice-add"><i
+ class="layui-icon"></i>新 增
+ </button>
+ </div>
+ <table class="layui-table" id="noticeSearchTable" lay-filter="noticeSearchTable-filter"></table>
+ </div>
+</div>
+
+<!-- 表格操作列 -->
+<script type="text/html" id="notice-table-bar">
+ <a class="layui-btn layui-btn-xs layui-btn-warm" lay-event="publish">发布</a>
+ {{# if(d.sendcnt == 0){ }}
+ <a class="layui-btn layui-btn-xs" lay-event="modify">修改</a>
+ <a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="delete">删除</a>
+ {{# } else{ }}
+ <a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="detail">发布详情</a>
+ {{# } }}
+</script>
+
+<script>
+ layui.use(['form', 'table', 'layer', 'admin', 'laydate'], function () {
+ var form = layui.form;
+ var table = layui.table;
+ var admin = layui.admin;
+ var laydate = layui.laydate;
+
+ laydate.render({
+ elem: '#notice-search-startdate',
+ trigger: 'click'
+ });
+ laydate.render({
+ elem: '#notice-search-enddate',
+ trigger: 'click'
+ });
+
+ table.render({
+ elem: '#noticeSearchTable',
+ url: '[[@{/notice/list}]]',
+ page: true,
+ cols: [
+ [
+ {align: 'left', title: '操作', width: 170, toolbar: '#notice-table-bar', fixed: 'left'},
+ {field: 'title', title: '消息标题', align: 'left', width: 160, fixed: 'left', sort: true},
+ {field: 'content', title: '消息内容', align: 'left'},
+ {
+ field: 'platform',
+ title: '推送终端',
+ align: 'center',
+ width: 120,
+ sort: true,
+ templet: function (d) {
+ if ('all' == d.platform) {
+ return '全部手机';
+ } else if ('ios' == d.platform) {
+ return '苹果手机';
+ } else if ('android' == d.platform) {
+ return '安卓手机';
+ } else {
+ return d.platform;
+ }
+ }
+ },
+ {field: 'sendcnt', title: '发布次数', align: 'center', width: 120, sort: true},
+ {field: 'creator', title: '创建者', align: 'center', width: 140, sort: true},
+ {
+ field: 'createdate',
+ title: '创建时间',
+ align: 'center',
+ width: 170,
+ sort: true,
+ templet: function (d) {
+ return admin.formatDate(d.createdate + '' + d.createtime);
+ }
+ },
+ {
+ field: 'lastsaved',
+ title: '最后更新时间',
+ align: 'center',
+ width: 170,
+ sort: true,
+ templet: function (d) {
+ return admin.formatDate(d.lastsaved);
+ }
+ }
+ ]
+ ]
+ });
+
+ $("#notice-search-btn").click(function () {
+ table.reload('noticeSearchTable', {
+ where: {
+ startdate: $("#notice-search-startdate").val(),
+ enddate: $("#notice-search-enddate").val(),
+ searchkey: $("#notice-search-searchkey").val()
+ }, page: {curr: 1}
+ });
+ });
+
+ $("#notice-search-btn-reset").click(function () {
+ $("#notice-search-startdate").val("");
+ $("#notice-search-enddate").val("");
+ $("#notice-search-searchkey").val("");
+ });
+
+ $("#btn-notice-add").click(function () {
+ showNoticeModel("新增公告", {
+ noticeId: '',
+ platform: 'all',
+ title: '',
+ content: '',
+ linkurl: ''
+ })
+ });
+
+ function showNoticeModel(title, data) {
+ admin.putTempData('t_noticeTmp', data);
+ admin.popupCenter({
+ title: title,
+ area: '650px',
+ path: '[[@{/notice/load4add}]]',
+ finish: function () {
+ table.reload('noticeSearchTable', {});
+ }
+ });
+ }
+
+ function showNoticePublishModel(data) {
+ admin.putTempData('t_noticePubidTmp', {
+ noticeId: data.id,
+ pushmode: 'atonce',
+ settime: ''
+ });
+ admin.popupCenter({
+ title: '发布通知公告【' + data.title + '】',
+ area: '550px',
+ path: '[[@{/notice/load4publish}]]',
+ finish: function () {
+ table.reload('noticeSearchTable', {});
+ }
+ });
+ }
+
+ function deleteNotice(data) {
+ layer.confirm('确定删除公告【' + data.title + '】吗?', function (i) {
+ layer.close(i);
+ layer.load(2);
+ admin.go('[[@{/notice/delete}]]', {
+ noticeId: data.id,
+ _csrf: $("meta[name='_csrf_token']").attr("value")
+ }, function (data) {
+ layer.closeAll('loading');
+ if (data.code == 200) {
+ layer.msg(data.msg, {icon: 1});
+ } else if (data.code == 401) {
+ layer.msg(data.msg, {icon: 2, time: 1500}, function () {
+ location.replace('[[@{/login}]]');
+ }, 1000);
+ return;
+ } else {
+ layer.msg(data.msg, {icon: 2});
+ }
+ table.reload('noticeSearchTable');
+ }, function (ret) {
+ console.log(ret);
+ admin.errorBack(ret);
+ });
+ })
+ }
+
+ function showNoticePublishDetailsModel(data) {
+ admin.putTempData('t_noticePublishDetail', data);
+ admin.popupCenter({
+ title: '通知公告【' + data.title + '】发布详情',
+ area: '750px',
+ path: '[[@{/notice/load4detail}]]',
+ finish: function () {
+ table.reload('noticeSearchTable', {});
+ }
+ });
+ }
+
+
+ //监听单元格
+ table.on('tool(noticeSearchTable-filter)', function (obj) {
+ var data = obj.data;
+ switch (obj.event) {
+ case "publish":
+ showNoticePublishModel(data);
+ break;
+ case "modify":
+ showNoticeModel("修改公告", {
+ noticeId: data.id,
+ platform: data.platform,
+ title: data.title,
+ content: data.content,
+ linkurl: data.linkurl
+ });
+ break;
+ case "delete":
+ deleteNotice(data);
+ break;
+ case "detail":
+ showNoticePublishDetailsModel(data);
+ break;
+ }
+ });
+ });
+</script>
diff --git a/payapi/src/main/resources/templates/system/notice/publish.html b/payapi/src/main/resources/templates/system/notice/publish.html
new file mode 100644
index 0000000..621749b
--- /dev/null
+++ b/payapi/src/main/resources/templates/system/notice/publish.html
@@ -0,0 +1,85 @@
+<form id="notice-publish-form" lay-filter="notice-publish-form" class="layui-form model-form">
+ <input name="noticeId" id="hidden-notice-publish-noticeid" type="hidden"/>
+ <div class="layui-form-item">
+ <label class="layui-form-label"><span style="color: red;">*</span>发布时间</label>
+ <div class="layui-input-block">
+ <input type="radio" name="pushmode" value="atonce" title="立即发布" lay-filter="notice-publish-form-pushmode-filter"/>
+ <input type="radio" name="pushmode" value="delay" title="定时发布" lay-filter="notice-publish-form-pushmode-filter"/>
+ </div>
+ </div>
+ <div class="layui-form-item" id="hidden-div-set-delaytime" style="display: none;">
+ <label class="layui-form-label"><span style="color: red;">*</span>设定时间</label>
+ <div class="layui-input-block">
+ <input name="settime" type="text" id="notice-publish-form-settime" style="width: 195px;" class="layui-input" autocomplete="off" lay-verify="pushmode"/>
+ </div>
+ </div>
+
+ <div class="layui-form-item model-form-footer">
+ <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
+ <button class="layui-btn" lay-filter="notice-publish-form-submit" lay-submit id="notice-publish-submit-btn">保存</button>
+ </div>
+</form>
+
+<script>
+ layui.use(['layer', 'admin', 'form', 'laydate'], function () {
+ var layer = layui.layer;
+ var admin = layui.admin;
+ var form = layui.form;
+ var laydate = layui.laydate;
+
+ form.render('radio');
+ form.verify({
+ pushmode: function (value, item) {
+ var pushmode = $("#notice-publish-form").find("input[name='pushmode']:checked").val();
+ if('atonce' != pushmode && isempty(value)){
+ return "定时发布请设定时间";
+ }
+ }
+ });
+ form.on('radio(notice-publish-form-pushmode-filter)', function (data) {
+ if('atonce'==data.value){
+ $("#notice-publish-form-settime").val("");
+ $("#hidden-div-set-delaytime").hide();
+ }else{
+ $("#hidden-div-set-delaytime").show();
+ }
+ });
+ laydate.render({
+ elem: '#notice-publish-form-settime',
+ type: 'datetime',
+ min: moment().locale('zh-cn').format('YYYY-MM-DD HH:mm:ss'),
+ trigger: 'click'
+ });
+ var bean = admin.getTempData('t_noticePubidTmp');
+ if (bean) {
+ form.val('notice-publish-form', bean);
+ }
+
+ // 表单提交事件
+ form.on('submit(notice-publish-form-submit)', function (data) {
+ layer.load(2);
+ var param = data.field;
+ var token = $("meta[name='_csrf_token']").attr("value");
+ param["_csrf"] = token;
+ admin.go('[[@{/notice/publish}]]', param, function (result) {
+ console.log(result);
+ layer.closeAll('loading');
+ if (result.code == 200) {
+ layer.msg(result.msg, {icon: 1});
+ admin.finishPopupCenter();
+ } else if (result.code == 401) {
+ layer.msg(result.msg, {icon: 2, time: 1500}, function () {
+ location.replace('[[@{/login}]]');
+ }, 1000);
+ return;
+ } else {
+ layer.msg(result.msg, {icon: 2});
+ }
+ }, function (ret) {
+ console.log(ret);
+ admin.errorBack(ret);
+ });
+ return false;
+ });
+ });
+</script>
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 35b2274..a1a579b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,3 @@
rootProject.name = 'payapi'
-include 'payapi', 'payapi-sdk', 'payapi-common', 'ynrcc-agent','oauth'
+include 'payapi', 'payapi-sdk', 'payapi-common', 'ynrcc-agent','oauth','bus-qrcode'
diff --git a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/bean/DlpayReq.java b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/bean/DlpayReq.java
index 7fae31a..bc5954f 100644
--- a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/bean/DlpayReq.java
+++ b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/bean/DlpayReq.java
@@ -35,6 +35,8 @@
private String oriSn; //原应用系统唯一流水号
private String chkdate; //对账日期
+ private String captcha; //签约验证码
+ private String scenario;//消费场景
/**
* 市民卡绑定请求XML
@@ -49,7 +51,7 @@
.append("<CATEGORIE>").append(categorie).append("</CATEGORIE>")
.append("<SN>").append(sn).append("</SN>")
.append("<BC_NO>").append(bcNo).append("</BC_NO>")
- .append("<NAME>").append(name).append("</NAME>")
+ .append("<NAME>").append(name).append("</NAME>")
.append("<ID_TYPE>").append(idType).append("</ID_TYPE>")
.append("<ID_NO>").append(idNo).append("</ID_NO>")
.append("<PHONE>").append(phone).append("</PHONE>")
@@ -75,7 +77,8 @@
.append("<ID_NO>").append(idNo).append("</ID_NO>")
.append("<PHONE>").append(phone).append("</PHONE>")
.append("<TRANS_TYPE>").append(transType).append("</TRANS_TYPE>")
- .append("</root>");
+ .append("<CAPTCHA>").append(captcha).append("</CAPTCHA>")
+ .append("</root>");
return String.format("%08d", xml.toString().getBytes("GBK").length) + xml.toString();
}
@@ -290,4 +293,20 @@
public void setTranscode(String transcode) {
this.transcode = transcode;
}
+
+ public String getCaptcha() {
+ return captcha;
+ }
+
+ public void setCaptcha(String captcha) {
+ this.captcha = captcha;
+ }
+
+ public String getScenario() {
+ return scenario;
+ }
+
+ public void setScenario(String scenario) {
+ this.scenario = scenario;
+ }
}
diff --git a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/controller/YnrccApiController.java b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/controller/YnrccApiController.java
index 56e4d60..d7dfcb4 100644
--- a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/controller/YnrccApiController.java
+++ b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/controller/YnrccApiController.java
@@ -77,10 +77,12 @@
@FormParam("transtime") String transtime, @FormParam("categorie") String categorie,
@FormParam("refno") String refno, @FormParam("bankcardno") String bankcardno,
@FormParam("username") String username, @FormParam("idtype") String idtype,
- @FormParam("idno") String idno, @FormParam("phone") String phone,
+ @FormParam("idno") String idno,
+ @FormParam("phone") String phone,
@FormParam("sign_type") String sign_type, @FormParam("sign") String sign) {
DlpayResp resp = new DlpayResp();
- if (!ynrccParamCheckService.checkBindCardParam(transcode, transdate, transtime, refno, bankcardno, username, idtype, idno, phone, categorie, sign_type, sign, resp)) {
+ if (!ynrccParamCheckService.checkBindCardParam(transcode, transdate, transtime, refno, bankcardno, username, idtype, idno,
+ phone, categorie, sign_type, sign, resp)) {
logger.error(resp.errPrint());
return resp;
}
@@ -136,10 +138,11 @@
@FormParam("bankcardno") String bankcardno, @FormParam("username") String username,
@FormParam("idtype") String idtype, @FormParam("idno") String idno,
@FormParam("phone") String phone,
+ @FormParam("captcha")String captcha,
@FormParam("sign_type") String sign_type, @FormParam("sign") String sign) {
DlpayResp resp = new DlpayResp();
if (!ynrccParamCheckService.checkSignCardParam(transcode, transdate, transtime, refno, categorie, bankcardno,
- username, idtype, idno, phone, transtype, sign_type, sign, resp)) {
+ username, idtype, idno, phone, captcha,transtype, sign_type, sign, resp)) {
logger.error(resp.errPrint());
return resp;
}
@@ -156,6 +159,7 @@
params.put("idno", idno);
params.put("phone", phone);
params.put("transtype", transtype);
+ params.put("captcha",captcha);
params.put("sign_type", sign_type);
params.put("sign", sign);
if (!checkYnrccSign(params, resp)) {
@@ -175,6 +179,7 @@
req.setIdNo(idno);
req.setPhone(phone);
req.setTransType(transtype);
+ req.setCaptcha(captcha);
return ynrccApiService.sendToYnrcc(DlpayUtil.OPTYPE_SIGNCARD, req);
} catch (BussinessException be) {
resp.setCode(ErrorCode.BUSSINESS_ERROR);
@@ -199,11 +204,12 @@
@FormParam("merchant_bankcardno") String merchant_bankcardno,
@FormParam("merchant_bankaccname") String merchant_bankaccname,
@FormParam("amount") Integer amount,
+ @FormParam("scenario") String scenario,
@FormParam("description") String description,
@FormParam("sign_type") String sign_type, @FormParam("sign") String sign) {
DlpayResp resp = new DlpayResp();
if (!ynrccParamCheckService.checkCardPayParam(transcode, transdate, transtime, refno, categorie, bankcardno,
- username, idtype, idno, merchant_bankcardno, merchant_bankaccname, amount, description, sign_type, sign, resp)) {
+ username, idtype, idno, merchant_bankcardno, merchant_bankaccname, amount,scenario, description, sign_type, sign, resp)) {
logger.error(resp.errPrint());
return resp;
}
@@ -216,11 +222,12 @@
params.put("categorie", categorie);
params.put("bankcardno", bankcardno);
params.put("username", username);
- params.put("idtype", idtype);
+ //params.put("idtype", idtype);
params.put("idno", idno);
params.put("merchant_bankcardno", merchant_bankcardno);
params.put("merchant_bankaccname", merchant_bankaccname);
params.put("amount", String.valueOf(amount));
+ params.put("scenario",scenario);
params.put("description", description);
params.put("sign_type", sign_type);
params.put("sign", sign);
@@ -242,6 +249,7 @@
req.setMerchantBcno(merchant_bankcardno);
req.setMerchantName(merchant_bankaccname);
req.setAmount(amount);
+ req.setScenario(scenario);
req.setDescription(description);
return ynrccApiService.sendToYnrcc(DlpayUtil.OPTYPE_CARDPAY, req);
} catch (BussinessException be) {
diff --git a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/YnrccParamCheckService.java b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/YnrccParamCheckService.java
index 4eb674b..9673aff 100644
--- a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/YnrccParamCheckService.java
+++ b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/YnrccParamCheckService.java
@@ -12,10 +12,10 @@
boolean checkSign(Map<String, String> param);
boolean checkSignCardParam(String transcode, String transdate, String transtime, String refno, String categorie, String bankcardno, String username,
- String idtype, String idno, String phone, String transtype, String sign_type, String sign, DlpayResp resp);
+ String idtype, String idno, String phone, String captcha,String transtype, String sign_type, String sign, DlpayResp resp);
boolean checkCardPayParam(String transcode, String transdate, String transtime, String refno, String categorie, String bankcardno, String username,
- String idtype, String idno, String merchant_bankcardno, String merchant_bankaccname, Integer amount, String description,
+ String idtype, String idno, String merchant_bankcardno, String merchant_bankaccname, Integer amount,String scenario, String description,
String sign_type, String sign, DlpayResp resp);
boolean checkPayRefundParam(String transcode, String transdate, String transtime, String refno, String refundRefno, Integer amount, String description,
diff --git a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/impl/YnrccParamCheckServiceImpl.java b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/impl/YnrccParamCheckServiceImpl.java
index 069bf8f..bea60e8 100644
--- a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/impl/YnrccParamCheckServiceImpl.java
+++ b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/impl/YnrccParamCheckServiceImpl.java
@@ -58,7 +58,8 @@
}
@Override
- public boolean checkBindCardParam(String transcode, String transdate, String transtime, String refno, String bankcardno, String username, String idtype, String idno, String phone, String categorie, String sign_type, String sign, DlpayResp resp) {
+ public boolean checkBindCardParam(String transcode, String transdate, String transtime, String refno, String bankcardno, String username, String idtype, String idno,
+ String phone, String categorie, String sign_type, String sign, DlpayResp resp) {
if (!checkYnrccBaseParam(transcode, transdate, transtime, refno, sign_type, sign, resp)) {
return false;
}
@@ -120,8 +121,9 @@
@Override
public boolean checkSignCardParam(String transcode, String transdate, String transtime, String refno, String categorie, String bankcardno, String username,
- String idtype, String idno, String phone, String transtype, String sign_type, String sign, DlpayResp resp) {
- if (!checkBindCardParam(transcode, transdate, transtime, refno, bankcardno, username, idtype, idno, phone, categorie, sign_type, sign, resp)) {
+ String idtype, String idno, String phone, String captcha,String transtype, String sign_type, String sign, DlpayResp resp) {
+ if (!checkBindCardParam(transcode, transdate, transtime, refno, bankcardno, username, idtype, idno,
+ phone, categorie, sign_type, sign, resp)) {
return false;
}
if (StringUtil.isEmpty(transtype)) {
@@ -129,12 +131,17 @@
resp.setMessage("请求参数错误[签约标志]");
return false;
}
+ if (StringUtil.isEmpty(captcha)) {
+ resp.setCode(ErrorCode.REQ_PARAM_ERROR);
+ resp.setMessage("请求参数错误[验证码为空]");
+ return false;
+ }
return true;
}
@Override
- public boolean checkCardPayParam(String transcode, String transdate, String transtime, String refno, String categorie, String bankcardno, String username, String idtype, String idno, String merchant_bankcardno, String merchant_bankaccname, Integer amount, String description, String sign_type, String sign, DlpayResp resp) {
+ public boolean checkCardPayParam(String transcode, String transdate, String transtime, String refno, String categorie, String bankcardno, String username, String idtype, String idno, String merchant_bankcardno, String merchant_bankaccname, Integer amount, String scenario,String description, String sign_type, String sign, DlpayResp resp) {
if (!checkYnrccBaseParam(transcode, transdate, transtime, refno, sign_type, sign, resp)) {
return false;
}
@@ -179,6 +186,11 @@
resp.setMessage("请求参数错误[交易金额为空]");
return false;
}
+ if (null == scenario) {
+ resp.setCode(ErrorCode.REQ_PARAM_ERROR);
+ resp.setMessage("请求参数错误[交易场景为空]");
+ return false;
+ }
return true;
}