feiyu02
2022-10-21 f22c4b9230808fed4fec80c435eccb4c833349a0
2022.10.21
1.环境守法小程序后台功能首发版本完成
已修改30个文件
已添加30个文件
已重命名2个文件
3324 ■■■■■ 文件已修改
pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/SupervisionApplication.kt 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/net/WXHttpService.kt 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/wx/AesException.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/wx/ByteGroup.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/wx/MessageWxVo.kt 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/wx/PKCS7Encoder.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/wx/SHA1.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/wx/TemplateManager.kt 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/wx/WXBizMsgCrypt.java 289 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/wx/WxConfig.kt 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/wx/WxTokenManager.kt 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/wx/XMLParse.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/domain/entity/BaseInfo.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/domain/entity/LogMsgSubscribeWx.java 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/domain/entity/MsgSubscribeWx.java 206 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/domain/entity/PersonalInfo.java 243 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/domain/entity/RestaurantBaseInfo.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/domain/entity/UserInfoWx.java 127 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/domain/entity/Userinfo.kt 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/domain/enumeration/AuthenticationStatus.kt 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/domain/mapper/LogMsgSubscribeWxMapper.kt 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/domain/mapper/MsgSubscribeWxMapper.kt 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/domain/mapper/PersonalInfoMapper.kt 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/infrastructure/utils/PinYin.kt 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/AuthService.kt 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/AuthServiceImpl.kt 224 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/LedgerServiceImpl.kt 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/NotificationServiceImpl.kt 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/UserinfoServiceImpl.kt 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/WxUserServiceImpl.kt 313 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/LedgerService.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/NotificationService.kt 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/UserinfoService.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/WxUserService.kt 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/vo/AccessToken.kt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/vo/AuthSceneRestVo.kt 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/vo/AuthSceneVo.kt 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/vo/UserBaseInfo.kt 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/AuthController.kt 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/LedgerController.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/NotificationController.kt 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/UserinfoController.kt 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/WxUserController.kt 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/timingtask/BaseTimingTask.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/timingtask/TaskController.kt 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/timingtask/TaskFetchVOC.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/timingtask/TaskLedgerRemind.kt 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/timingtask/TaskPushFume.kt 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/generator/generatorConfig.xml 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/BaseInfoMapper.xml 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/LogMsgSubscribeWxMapper.xml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/MsgSubscribeWxMapper.xml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/PersonalInfoMapper.xml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/RestaurantBaseInfoMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/UserInfoWxMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/templates/commitment-restaurant.ftl 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/cn/flightfeather/supervision/CommonTest.kt 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/cn/flightfeather/supervision/common/wx/TemplateManagerTest.kt 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/NotificationServiceImplTest.kt 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/WxUserServiceImplTest.kt 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/cn/flightfeather/supervision/timingtask/PushFumeTest.kt 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -363,6 +363,12 @@
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.belerweb/pinyin4j -->
        <dependency>
            <groupId>com.belerweb</groupId>
            <artifactId>pinyin4j</artifactId>
            <version>2.5.1</version>
        </dependency>
    </dependencies>
src/main/kotlin/cn/flightfeather/supervision/SupervisionApplication.kt
@@ -1,5 +1,6 @@
package cn.flightfeather.supervision
import cn.flightfeather.supervision.common.wx.WxTokenManager
import cn.flightfeather.supervision.timingtask.TaskController
import cn.flightfeather.supervision.websocket.VMRoomWebSocketServer
import org.springframework.beans.factory.annotation.Autowired
@@ -18,10 +19,14 @@
    @Autowired
    lateinit var taskController: TaskController
    @Autowired
    lateinit var wxTokenManager: WxTokenManager
    @Bean
    fun runner() = ApplicationRunner {
        webSocketServer.start()
//        taskController.run()
        wxTokenManager.run()
    }
}
src/main/kotlin/cn/flightfeather/supervision/common/net/WXHttpService.kt
@@ -1,5 +1,13 @@
package cn.flightfeather.supervision.common.net
import cn.flightfeather.supervision.common.wx.MessageWxVo
import cn.flightfeather.supervision.common.wx.WxConfig
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonParser
/**
 * @author riku
 * Date: 2020/10/15
@@ -7,17 +15,87 @@
 */
object WXHttpService {
    private const val APP_ID=""
    private const val SECRET = ""
    private val httpMethod = HttpMethod("api.weixin.qq.com", 443, true)
    /**
     * å¾®ä¿¡ç™»å½•凭证校验
     */
    fun code2Session(code: String): HttpMethod.MyResponse =
        httpMethod.get(
    fun code2Session(code: String): Pair<String, String>? {
        val res =httpMethod.get(
            "/sns/jscode2session", listOf(
                Pair("appid", APP_ID), Pair("secret", SECRET), Pair("js_code", code), Pair("grant_type", "authorization_code")
                Pair("appid", WxConfig.APP_ID), Pair("secret", WxConfig.SECRET), Pair("js_code", code), Pair("grant_type", "authorization_code")
            )
        )
        return if (res.success) {
            val json = JSONObject.parseObject(res.m.responseBodyAsString)
            val errCode = json["errcode"]
            if (errCode == 0 || errCode == null) {
                val openid = json["openid"] as String
                val unionid = json["session_key"] as String
                Pair(openid, unionid)
            } else {
                null
            }
        } else {
            null
        }
    }
    /**
     * å¾®ä¿¡å‘送订阅消息
     */
    fun sendMsg(accessToken:String, msgWxV0: MessageWxVo): Boolean {
        val data = Gson().toJson(msgWxV0)
        val response = httpMethod.post("/cgi-bin/message/subscribe/send?access_token=${accessToken}", data)
        if (response.success) {
            val json = JsonParser.parseString(response.m.responseBodyAsString)
            if (json.isJsonObject) {
                val jo = json.asJsonObject
                val eMsg = jo["errmsg"]?.asString
                when (jo["errcode"]?.asInt) {
                    40003 -> {
                        throw IllegalStateException("微信订阅消息,touser字段openid为空或者不正确,错误信息:${eMsg}")
                    }
                    40037 -> {
                        throw IllegalStateException("微信订阅消息,订阅模板id为空不正确,错误信息:${eMsg}")
                    }
                    43101 -> {
                        throw IllegalStateException("微信订阅消息,用户拒绝接收消息,错误信息:${eMsg}")
                    }
                    47003 -> {
                        throw IllegalStateException("微信订阅消息,模板参数不准确,具体错误:${eMsg}")
                    }
                    41030 -> {
                        throw IllegalStateException("微信订阅消息,page路径不正确,错误信息:${eMsg}")
                    }
                }
            }
            return true
        } else {
            return false
        }
    }
    /**
     * èŽ·å–å°ç¨‹åºå…¨å±€åŽå°æŽ¥å£è°ƒç”¨å‡­æ®
     */
    fun getAccessToken(): JsonObject? {
        val res = httpMethod.get(
            "/cgi-bin/token", listOf(
                Pair("grant_type", "client_credential"),
                Pair("appid", WxConfig.APP_ID),
                Pair("secret", WxConfig.SECRET)
            )
        )
        return if (res.success) {
            val json = JsonParser.parseString(res.m.responseBodyAsString)
            if (json.isJsonObject) {
                json.asJsonObject
            } else {
                null
            }
        } else {
            null
        }
    }
}
src/main/kotlin/cn/flightfeather/supervision/common/wx/AesException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package cn.flightfeather.supervision.common.wx;
@SuppressWarnings("serial")
public class AesException extends Exception {
    public final static int OK = 0;
    public final static int ValidateSignatureError = -40001;
    public final static int ParseXmlError = -40002;
    public final static int ComputeSignatureError = -40003;
    public final static int IllegalAesKey = -40004;
    public final static int ValidateAppidError = -40005;
    public final static int EncryptAESError = -40006;
    public final static int DecryptAESError = -40007;
    public final static int IllegalBuffer = -40008;
    //public final static int EncodeBase64Error = -40009;
    //public final static int DecodeBase64Error = -40010;
    //public final static int GenReturnXmlError = -40011;
    private int code;
    private static String getMessage(int code) {
        switch (code) {
        case ValidateSignatureError:
            return "签名验证错误";
        case ParseXmlError:
            return "xml解析失败";
        case ComputeSignatureError:
            return "sha加密生成签名失败";
        case IllegalAesKey:
            return "SymmetricKey非法";
        case ValidateAppidError:
            return "appid校验失败";
        case EncryptAESError:
            return "aes加密失败";
        case DecryptAESError:
            return "aes解密失败";
        case IllegalBuffer:
            return "解密后得到的buffer非法";
//        case EncodeBase64Error:
//            return "base64加密错误";
//        case DecodeBase64Error:
//            return "base64解密错误";
//        case GenReturnXmlError:
//            return "xml生成失败";
        default:
            return null; // cannot be
        }
    }
    public int getCode() {
        return code;
    }
    AesException(int code) {
        super(getMessage(code));
        this.code = code;
    }
}
src/main/kotlin/cn/flightfeather/supervision/common/wx/ByteGroup.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package cn.flightfeather.supervision.common.wx;
import java.util.ArrayList;
class ByteGroup {
    ArrayList<Byte> byteContainer = new ArrayList<Byte>();
    public byte[] toBytes() {
        byte[] bytes = new byte[byteContainer.size()];
        for (int i = 0; i < byteContainer.size(); i++) {
            bytes[i] = byteContainer.get(i);
        }
        return bytes;
    }
    public ByteGroup addBytes(byte[] bytes) {
        for (byte b : bytes) {
            byteContainer.add(b);
        }
        return this;
    }
    public int size() {
        return byteContainer.size();
    }
}
src/main/kotlin/cn/flightfeather/supervision/common/wx/MessageWxVo.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package cn.flightfeather.supervision.common.wx
import kotlin.reflect.jvm.internal.impl.load.kotlin.JvmType
data class MessageWxVo(
    //接收者(用户)的openid
    val touser: String,
    val template_id: String,
    val page: String,
    val miniprogram_state: String,
    val lang: String,
    var data: Any? = null
)
src/main/kotlin/cn/flightfeather/supervision/common/wx/PKCS7Encoder.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
/**
 * å¯¹å…¬ä¼—平台发送给公众账号的消息加解密示例代码.
 *
 * @copyright Copyright (c) 1998-2014 Tencent Inc.
 */
// ------------------------------------------------------------------------
package cn.flightfeather.supervision.common.wx;
import java.nio.charset.Charset;
import java.util.Arrays;
/**
 * æä¾›åŸºäºŽPKCS7算法的加解密接口.
 */
class PKCS7Encoder {
    static Charset CHARSET = Charset.forName("utf-8");
    static int BLOCK_SIZE = 32;
    /**
     * èŽ·å¾—å¯¹æ˜Žæ–‡è¿›è¡Œè¡¥ä½å¡«å……çš„å­—èŠ‚.
     *
     * @param count éœ€è¦è¿›è¡Œå¡«å……补位操作的明文字节个数
     * @return è¡¥é½ç”¨çš„字节数组
     */
    static byte[] encode(int count) {
        // è®¡ç®—需要填充的位数
        int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
        if (amountToPad == 0) {
            amountToPad = BLOCK_SIZE;
        }
        // èŽ·å¾—è¡¥ä½æ‰€ç”¨çš„å­—ç¬¦
        char padChr = chr(amountToPad);
        String tmp = new String();
        for (int index = 0; index < amountToPad; index++) {
            tmp += padChr;
        }
        return tmp.getBytes(CHARSET);
    }
    /**
     * åˆ é™¤è§£å¯†åŽæ˜Žæ–‡çš„补位字符
     *
     * @param decrypted è§£å¯†åŽçš„æ˜Žæ–‡
     * @return åˆ é™¤è¡¥ä½å­—符后的明文
     */
    static byte[] decode(byte[] decrypted) {
        int pad = (int) decrypted[decrypted.length - 1];
        if (pad < 1 || pad > 32) {
            pad = 0;
        }
        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
    }
    /**
     * å°†æ•°å­—转化成ASCII码对应的字符,用于对明文进行补码
     *
     * @param a éœ€è¦è½¬åŒ–的数字
     * @return è½¬åŒ–得到的字符
     */
    static char chr(int a) {
        byte target = (byte) (a & 0xFF);
        return (char) target;
    }
}
src/main/kotlin/cn/flightfeather/supervision/common/wx/SHA1.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
/**
 * å¯¹å…¬ä¼—平台发送给公众账号的消息加解密示例代码.
 *
 * @copyright Copyright (c) 1998-2014 Tencent Inc.
 */
// ------------------------------------------------------------------------
package cn.flightfeather.supervision.common.wx;
import java.security.MessageDigest;
import java.util.Arrays;
/**
 * SHA1 class
 *
 * è®¡ç®—公众平台的消息签名接口.
 */
public class SHA1 {
    /**
     * ç”¨SHA1算法生成安全签名
     * @param token ç¥¨æ®
     * @param timestamp æ—¶é—´æˆ³
     * @param nonce éšæœºå­—符串
     * @param encrypt å¯†æ–‡
     * @return å®‰å…¨ç­¾å
     * @throws AesException
     */
    public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException
              {
        try {
            String[] array = new String[] { token, timestamp, nonce, encrypt };
            StringBuffer sb = new StringBuffer();
            // å­—符串排序
            Arrays.sort(array);
            for (int i = 0; i < 4; i++) {
                sb.append(array[i]);
            }
            String str = sb.toString();
            // SHA1签名生成
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();
            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            return hexstr.toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.ComputeSignatureError);
        }
    }
}
src/main/kotlin/cn/flightfeather/supervision/common/wx/TemplateManager.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,188 @@
package cn.flightfeather.supervision.common.wx
import cn.flightfeather.supervision.common.net.WXHttpService
import cn.flightfeather.supervision.domain.entity.LogMsgSubscribeWx
import cn.flightfeather.supervision.domain.mapper.LogMsgSubscribeWxMapper
import com.alibaba.fastjson.JSON
import com.google.gson.Gson
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import java.util.*
import javax.annotation.PostConstruct
/**
 * å¾®ä¿¡è®¢é˜…消息模板管理
 */
@Component
class TemplateManager {
    companion object {
        private lateinit var instance: TemplateManager
        //推送小程序版本
        private const val PROGRAM_STATE_1 = "developer"//开发版
        private const val PROGRAM_STATE_2 = "trial"//体验版
        private const val PROGRAM_STATE_3 = "formal"//正式版
        const val TEMPLATE_1 = "6JQFOJ12yBvKfRg_duSdwKiH5_J3LpICmz3Li-L1Cr8"//倒计时到期提醒,台账
        const val TEMPLATE_2 = "zPNMzF5WsshniJyl83DD-lDZtNvx7JyqLbKgqDl0qvU"//代办事项提醒,自测智评
        const val TEMPLATE_3 = "zPNMzF5WsshniJyl83DD-u7MyVoUozOc2kjK8dGZcSA"//代办事项提醒,重要通知,如会议、政府公告(暂定)
        const val TEMPLATE_4 = "dqREi7vAd03OOirTgBGcm5aCihZJKBjVpiA8Kbu4B8w"//备忘事项提醒,未认证提醒或其他
        //模板id和对应的跳转路径(已有顺序尽量不要修改,新模板往后顺延)
        val templateList = listOf(
            Pair(TEMPLATE_1,"pages/m_user/userlogin/userlogin"),
            Pair(TEMPLATE_2,"pages/m_user/userlogin/userlogin"),
            Pair(TEMPLATE_3,"pages/m_user/userlogin/userlogin"),
            Pair(TEMPLATE_4,"pages/m_user/userlogin/userlogin"),
        )
    }
    @Autowired
    lateinit var wxTokenManager: WxTokenManager
    @Autowired
    lateinit var logMsgSubscribeWxMapper: LogMsgSubscribeWxMapper
    @PostConstruct
    fun init() {
        instance = this
    }
    fun sendMsg(templateId: Int, openId: String, dataList: List<String>): Boolean {
        val token = wxTokenManager.getAccessToken()
        val msg = newTemplate(templateId, openId, dataList)
        val res = WXHttpService.sendMsg(token, msg)
        return if (res) {
            //发送成功,则记录相应的日志
            val log = LogMsgSubscribeWx().apply {
                lmsOpenId = msg.touser
                lmsTemplateId = msg.template_id
                lmsData = Gson().toJson(msg.data)
                lmsTime = Date()
                lmsResult = false
            }
            logMsgSubscribeWxMapper.insert(log)
            true
        } else {
            false
        }
    }
    fun newTemplate(templateId: Int, openId: String, dataList: List<String>): MessageWxVo {
        val t = templateList[templateId]
        val msg = MessageWxVo(openId, t.first, t.second, PROGRAM_STATE_3, "zh_CN")
        when (templateId) {
            0 -> {
                val temp = Template1()
                if (dataList.size == 4) {
                    temp.thing1.value = dataList[0]
                    temp.time2.value = dataList[1]
                    temp.number3.value = dataList[2]
                    temp.thing4.value = dataList[3]
                }
                msg.data = temp
            }
            1 -> {
                val temp = Template2()
                if (dataList.size == 5) {
                    temp.thing15.value = dataList[0]
                    temp.thing1.value = dataList[1]
                    temp.thing4.value = dataList[2]
                    temp.time10.value = dataList[3]
                    temp.thing12.value = dataList[4]
                }
                msg.data = temp
            }
            2 -> {
                val temp = Template3()
                if (dataList.size == 4) {
                    temp.thing1.value = dataList[0]
                    temp.thing4.value = dataList[1]
                    temp.time10.value = dataList[2]
                    temp.thing12.value = dataList[3]
                }
                msg.data = temp
            }
        }
        return msg
    }
    /**
     * å°è´¦å€’计时提醒
     */
    inner class Template1 {
        //倒计时名称
        val thing1: TValue = TValue("")
        //目标时间
        val time2: TValue = TValue("")
        //剩余天数
        val number3: TValue = TValue("")
        //备注
        val thing4: TValue = TValue("")
    }
    /**
     * è‡ªæµ‹æ™ºè¯„提醒
     */
    inner class Template2 {
        //事项类型
        val thing15: TValue = TValue("")
        //事项主题
        val thing1: TValue = TValue("")
        //事项描述
        val thing4: TValue = TValue("")
        //截止时间
        val time10: TValue = TValue("")
        //备注消息
        val thing12: TValue = TValue("")
    }
    /**
     * é‡è¦é€šçŸ¥ï¼Œå¦‚会议、政府公告(暂定)
     */
    inner class Template3 {
        //事项主题
        val thing1: TValue = TValue("")
        //事项描述
        val thing4: TValue = TValue("")
        //截止时间
        val time10: TValue = TValue("")
        //备注消息
        val thing12: TValue = TValue("")
    }
    /**
     * æœªè®¤è¯æé†’或其他
     */
    inner class Template4 {
        //活动事件
        val thing1: TValue = TValue("")
        //备忘事项
        val thing3: TValue = TValue("")
        //开始时间
        val time2: TValue = TValue("")
        //温馨提示
        val thing5: TValue = TValue("")
        //完成进度
        val thing4: TValue = TValue("")
    }
    data class TValue(var value: String)
}
src/main/kotlin/cn/flightfeather/supervision/common/wx/WXBizMsgCrypt.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,289 @@
/**
 * å¯¹å…¬ä¼—平台发送给公众账号的消息加解密示例代码.
 *
 * @copyright Copyright (c) 1998-2014 Tencent Inc.
 */
// ------------------------------------------------------------------------
/**
 * é’ˆå¯¹org.apache.commons.codec.binary.Base64,
 * éœ€è¦å¯¼å…¥æž¶åŒ…commons-codec-1.9(或commons-codec-1.8等其他版本)
 * å®˜æ–¹ä¸‹è½½åœ°å€ï¼šhttp://commons.apache.org/proper/commons-codec/download_codec.cgi
 */
package cn.flightfeather.supervision.common.wx;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
/**
 * æä¾›æŽ¥æ”¶å’ŒæŽ¨é€ç»™å…¬ä¼—平台消息的加解密接口(UTF8编码的字符串).
 * <ol>
 *     <li>第三方回复加密消息给公众平台</li>
 *     <li>第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。</li>
 * </ol>
 * è¯´æ˜Žï¼šå¼‚常java.security.InvalidKeyException:illegal Key Size的解决方案
 * <ol>
 *     <li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址:
 *      http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>
 *     <li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li>
 *     <li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件</li>
 *     <li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件</li>
 * </ol>
 */
public class WXBizMsgCrypt {
    static Charset CHARSET = Charset.forName("utf-8");
    Base64 base64 = new Base64();
    byte[] aesKey;
    String token;
    String appId;
    /**
     * æž„造函数
     * @param token å…¬ä¼—平台上,开发者设置的token
     * @param encodingAesKey å…¬ä¼—平台上,开发者设置的EncodingAESKey
     * @param appId å…¬ä¼—平台appid
     *
     * @throws AesException æ‰§è¡Œå¤±è´¥ï¼Œè¯·æŸ¥çœ‹è¯¥å¼‚常的错误码和具体的错误信息
     */
    public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throws AesException {
        if (encodingAesKey.length() != 43) {
            throw new AesException(AesException.IllegalAesKey);
        }
        this.token = token;
        this.appId = appId;
        aesKey = Base64.decodeBase64(encodingAesKey + "=");
    }
    // ç”Ÿæˆ4个字节的网络字节序
    byte[] getNetworkBytesOrder(int sourceNumber) {
        byte[] orderBytes = new byte[4];
        orderBytes[3] = (byte) (sourceNumber & 0xFF);
        orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);
        orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);
        orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);
        return orderBytes;
    }
    // è¿˜åŽŸ4个字节的网络字节序
    int recoverNetworkBytesOrder(byte[] orderBytes) {
        int sourceNumber = 0;
        for (int i = 0; i < 4; i++) {
            sourceNumber <<= 8;
            sourceNumber |= orderBytes[i] & 0xff;
        }
        return sourceNumber;
    }
    // éšæœºç”Ÿæˆ16位字符串
    String getRandomStr() {
        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 16; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
    /**
     * å¯¹æ˜Žæ–‡è¿›è¡ŒåР坆.
     *
     * @param text éœ€è¦åŠ å¯†çš„æ˜Žæ–‡
     * @return åŠ å¯†åŽbase64编码的字符串
     * @throws AesException aes加密失败
     */
    String encrypt(String randomStr, String text) throws AesException {
        ByteGroup byteCollector = new ByteGroup();
        byte[] randomStrBytes = randomStr.getBytes(CHARSET);
        byte[] textBytes = text.getBytes(CHARSET);
        byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length);
        byte[] appidBytes = appId.getBytes(CHARSET);
        // randomStr + networkBytesOrder + text + appid
        byteCollector.addBytes(randomStrBytes);
        byteCollector.addBytes(networkBytesOrder);
        byteCollector.addBytes(textBytes);
        byteCollector.addBytes(appidBytes);
        // ... + pad: ä½¿ç”¨è‡ªå®šä¹‰çš„填充方式对明文进行补位填充
        byte[] padBytes = PKCS7Encoder.encode(byteCollector.size());
        byteCollector.addBytes(padBytes);
        // èŽ·å¾—æœ€ç»ˆçš„å­—èŠ‚æµ, æœªåР坆
        byte[] unencrypted = byteCollector.toBytes();
        try {
            // è®¾ç½®åŠ å¯†æ¨¡å¼ä¸ºAES的CBC模式
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
            // åР坆
            byte[] encrypted = cipher.doFinal(unencrypted);
            // ä½¿ç”¨BASE64对加密后的字符串进行编码
            String base64Encrypted = base64.encodeToString(encrypted);
            return base64Encrypted;
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.EncryptAESError);
        }
    }
    /**
     * å¯¹å¯†æ–‡è¿›è¡Œè§£å¯†.
     *
     * @param text éœ€è¦è§£å¯†çš„密文
     * @return è§£å¯†å¾—到的明文
     * @throws AesException aes解密失败
     */
    String decrypt(String text) throws AesException {
        byte[] original;
        try {
            // è®¾ç½®è§£å¯†æ¨¡å¼ä¸ºAES的CBC模式
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
            cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);
            // ä½¿ç”¨BASE64对密文进行解码
            byte[] encrypted = Base64.decodeBase64(text);
            // è§£å¯†
            original = cipher.doFinal(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.DecryptAESError);
        }
        String xmlContent, from_appid;
        try {
            // åŽ»é™¤è¡¥ä½å­—ç¬¦
            byte[] bytes = PKCS7Encoder.decode(original);
            // åˆ†ç¦»16位随机字符串,网络字节序和AppId
            byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
            int xmlLength = recoverNetworkBytesOrder(networkOrder);
            xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);
            from_appid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length),
                    CHARSET);
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.IllegalBuffer);
        }
        // appid不相同的情况
        if (!from_appid.equals(appId)) {
            throw new AesException(AesException.ValidateAppidError);
        }
        return xmlContent;
    }
    /**
     * å°†å…¬ä¼—平台回复用户的消息加密打包.
     * <ol>
     *     <li>对要发送的消息进行AES-CBC加密</li>
     *     <li>生成安全签名</li>
     *     <li>将消息密文和安全签名打包成xml格式</li>
     * </ol>
     *
     * @param replyMsg å…¬ä¼—平台待回复用户的消息,xml格式的字符串
     * @param timeStamp æ—¶é—´æˆ³ï¼Œå¯ä»¥è‡ªå·±ç”Ÿæˆï¼Œä¹Ÿå¯ä»¥ç”¨URL参数的timestamp
     * @param nonce éšæœºä¸²ï¼Œå¯ä»¥è‡ªå·±ç”Ÿæˆï¼Œä¹Ÿå¯ä»¥ç”¨URL参数的nonce
     *
     * @return åŠ å¯†åŽçš„å¯ä»¥ç›´æŽ¥å›žå¤ç”¨æˆ·çš„å¯†æ–‡ï¼ŒåŒ…æ‹¬msg_signature, timestamp, nonce, encrypt的xml格式的字符串
     * @throws AesException æ‰§è¡Œå¤±è´¥ï¼Œè¯·æŸ¥çœ‹è¯¥å¼‚常的错误码和具体的错误信息
     */
    public String encryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException {
        // åР坆
        String encrypt = encrypt(getRandomStr(), replyMsg);
        // ç”Ÿæˆå®‰å…¨ç­¾å
        if (timeStamp == "") {
            timeStamp = Long.toString(System.currentTimeMillis());
        }
        String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt);
        // System.out.println("发送给平台的签名是: " + signature[1].toString());
        // ç”Ÿæˆå‘送的xml
        String result = XMLParse.generate(encrypt, signature, timeStamp, nonce);
        return result;
    }
    /**
     * æ£€éªŒæ¶ˆæ¯çš„真实性,并且获取解密后的明文.
     * <ol>
     *     <li>利用收到的密文生成安全签名,进行签名验证</li>
     *     <li>若验证通过,则提取xml中的加密消息</li>
     *     <li>对消息进行解密</li>
     * </ol>
     *
     * @param msgSignature ç­¾åä¸²ï¼Œå¯¹åº”URL参数的msg_signature
     * @param timeStamp æ—¶é—´æˆ³ï¼Œå¯¹åº”URL参数的timestamp
     * @param nonce éšæœºä¸²ï¼Œå¯¹åº”URL参数的nonce
     * @param postData å¯†æ–‡ï¼Œå¯¹åº”POST请求的数据
     *
     * @return è§£å¯†åŽçš„原文
     * @throws AesException æ‰§è¡Œå¤±è´¥ï¼Œè¯·æŸ¥çœ‹è¯¥å¼‚常的错误码和具体的错误信息
     */
    public String decryptMsg(String msgSignature, String timeStamp, String nonce, String postData)
            throws AesException {
        // å¯†é’¥ï¼Œå…¬ä¼—账号的app secret
        // æå–密文
        Object[] encrypt = XMLParse.extract(postData);
        // éªŒè¯å®‰å…¨ç­¾å
        String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString());
        // å’ŒURL中的签名比较是否相等
        // System.out.println("第三方收到URL中的签名:" + msg_sign);
        // System.out.println("第三方校验签名:" + signature);
        if (!signature.equals(msgSignature)) {
            throw new AesException(AesException.ValidateSignatureError);
        }
        // è§£å¯†
        String result = decrypt(encrypt[1].toString());
        return result;
    }
    /**
     * éªŒè¯URL
     * @param msgSignature ç­¾åä¸²ï¼Œå¯¹åº”URL参数的msg_signature
     * @param timeStamp æ—¶é—´æˆ³ï¼Œå¯¹åº”URL参数的timestamp
     * @param nonce éšæœºä¸²ï¼Œå¯¹åº”URL参数的nonce
     * @param echoStr éšæœºä¸²ï¼Œå¯¹åº”URL参数的echostr
     *
     * @return è§£å¯†ä¹‹åŽçš„echostr
     * @throws AesException æ‰§è¡Œå¤±è´¥ï¼Œè¯·æŸ¥çœ‹è¯¥å¼‚常的错误码和具体的错误信息
     */
    public String verifyUrl(String msgSignature, String timeStamp, String nonce, String echoStr)
            throws AesException {
        String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr);
        if (!signature.equals(msgSignature)) {
            throw new AesException(AesException.ValidateSignatureError);
        }
        String result = decrypt(echoStr);
        return result;
    }
}
src/main/kotlin/cn/flightfeather/supervision/common/wx/WxConfig.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package cn.flightfeather.supervision.common.wx
object WxConfig {
    /**小程序申请了两个公众号,选择其中一种*************************************************************/
    //小白咨询
//    const val APP_ID="wxffd1438dd373fcf6"
//    const val SECRET = "83c5ecfb9b2ae882b577a5c04ff7c3bd"
    //中小微企业生态环境守法智能服务
    const val APP_ID="wx5758efcebb0774de"
    const val SECRET = "444a906c37ae3f2eab609060d944f624"
    const val USER_NAME = "gh_c60faa57000f"
    /*********************************************************************************************/
    //小程序推送消息验证token
    const val TOKEN = "ledger"
    const val AES_KEY = "ou43VsUPRFNmEIoVXsy038z0NPLbPAiIAysGrg1YFlZ"
    //用户接收或拒收消息推送的关键词
    const val S_ACCEPT = "accept"
    const val S_REJECT = "reject"
}
src/main/kotlin/cn/flightfeather/supervision/common/wx/WxTokenManager.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,98 @@
package cn.flightfeather.supervision.common.wx
import cn.flightfeather.supervision.common.net.WXHttpService
import org.springframework.stereotype.Component
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
/**
 * å°ç¨‹åºå…¨å±€åŽå°æŽ¥å£è°ƒç”¨å‡­æ®ç®¡ç†
 */
@Component
class WxTokenManager {
    companion object {
        private const val TAG = "WxTokenManager"
    }
    private var schedule = Executors.newScheduledThreadPool(2)
    private var token = ""
    fun run(){
        if (token.isBlank()) {
            refreshToken(0)
        }
    }
    /**
     * åˆ·æ–°æŽ¥å£è°ƒç”¨å‡­è¯
     * @param delay å»¶è¿Ÿæ‰§è¡Œæ—¶é—´ï¼Œå•位:秒
     * @param force æ˜¯å¦å¼ºåˆ¶ä¸­æ–­å½“前线程,重新执行
     */
    fun refreshToken(delay:Long, force:Boolean = false) {
        if (force) {
            schedule = closeThread(schedule)
        }
        schedule.schedule({
            getTokenTask()
        }, delay, TimeUnit.SECONDS)
    }
    fun getAccessToken(): String {
        if (token.isBlank()) {
            throw IllegalStateException("[${TAG}]获取小程序接口调用凭据,当前token还未获取到")
        }
        return token
    }
    private fun getTokenTask() {
        val res = WXHttpService.getAccessToken()
        if (res == null) {
            //请求失败,10s后重试
            refreshToken(10)
        } else {
            var nextDelay = 6900L//下一次获取凭证时间间隔(秒)
            when (res["errcode"]?.asInt) {
                //请求成功
                null, 0 -> {
                    val t = res["access_token"]?.asString
                    if (t == null) {
                        refreshToken(60)
                    } else {
                        token = t
                        res["expires_in"]?.asLong?.let { nextDelay = it - 300 }
                        refreshToken(nextDelay)
                    }
                }
                //微信服务器系统繁忙,稍后重试
                -1 -> {
                    refreshToken(60)
                }
                //AppSecret错误
                40001 -> {
                    throw IllegalStateException("[${TAG}]获取小程序接口调用凭据,AppSecret[${WxConfig.SECRET}]错误")
                }
                40002 -> {
                    throw IllegalStateException("[${TAG}]获取小程序接口调用凭据,请确保grant_type字段值为client_credential")
                }
                40003 -> {
                    throw IllegalStateException("[${TAG}]获取小程序接口调用凭据,AppID[${WxConfig.APP_ID}]错误")
                }
            }
        }
    }
    private fun closeThread(s: ScheduledExecutorService): ScheduledExecutorService {
        try {
            s.shutdown()
            if (s.awaitTermination(10, TimeUnit.SECONDS)) {
                s.shutdownNow()
            }
        } catch (e: InterruptedException) {
            e.printStackTrace()
            s.shutdownNow()
        }
        return Executors.newScheduledThreadPool(2)
    }
}
src/main/kotlin/cn/flightfeather/supervision/common/wx/XMLParse.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,78 @@
/**
 * å¯¹å…¬ä¼—平台发送给公众账号的消息加解密示例代码.
 *
 * @copyright Copyright (c) 1998-2014 Tencent Inc.
 */
// ------------------------------------------------------------------------
package cn.flightfeather.supervision.common.wx;
import java.io.StringReader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
 * XMLParse class
 *
 * æä¾›æå–消息格式中的密文及生成回复消息格式的接口.
 */
class XMLParse {
    /**
     * æå–出xml数据包中的加密消息
     * @param xmltext å¾…提取的xml字符串
     * @return æå–出的加密消息字符串
     * @throws AesException
     */
    public static Object[] extract(String xmltext) throws AesException     {
        Object[] result = new Object[3];
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
            dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            dbf.setXIncludeAware(false);
            dbf.setExpandEntityReferences(false);
            DocumentBuilder db = dbf.newDocumentBuilder();
            StringReader sr = new StringReader(xmltext);
            InputSource is = new InputSource(sr);
            Document document = db.parse(is);
            Element root = document.getDocumentElement();
            NodeList nodelist1 = root.getElementsByTagName("Encrypt");
            NodeList nodelist2 = root.getElementsByTagName("ToUserName");
            result[0] = 0;
            result[1] = nodelist1.item(0).getTextContent();
            result[2] = nodelist2.item(0).getTextContent();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.ParseXmlError);
        }
    }
    /**
     * ç”Ÿæˆxml消息
     * @param encrypt åŠ å¯†åŽçš„æ¶ˆæ¯å¯†æ–‡
     * @param signature å®‰å…¨ç­¾å
     * @param timestamp æ—¶é—´æˆ³
     * @param nonce éšæœºå­—符串
     * @return ç”Ÿæˆçš„xml字符串
     */
    public static String generate(String encrypt, String signature, String timestamp, String nonce) {
        String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n"
                + "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n"
                + "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" + "</xml>";
        return String.format(format, encrypt, signature, timestamp, nonce);
    }
}
src/main/kotlin/cn/flightfeather/supervision/domain/entity/BaseInfo.java
@@ -19,6 +19,12 @@
    private String biName;
    /**
     * ç”¨æˆ·ç®€ç§°
     */
    @Column(name = "BI_Nick_Name")
    private String biNickName;
    /**
     * æ‰€å±žå…¬å¸id
     */
    @Column(name = "CI_GUID")
@@ -29,6 +35,30 @@
     */
    @Column(name = "CI_Name")
    private String ciName;
    @Column(name = "BI_Province_Code")
    private String biProvinceCode;
    @Column(name = "BI_Province_Name")
    private String biProvinceName;
    @Column(name = "BI_City_Code")
    private String biCityCode;
    @Column(name = "BI_City_Name")
    private String biCityName;
    @Column(name = "BI_District_Code")
    private String biDistrictCode;
    @Column(name = "BI_District_Name")
    private String biDistrictName;
    @Column(name = "BI_Town_Code")
    private String biTownCode;
    @Column(name = "BI_Town_Name")
    private String biTownName;
    /**
     * æ‰€å±žç‰©ä¸šå…¬å¸åç§°id
@@ -115,6 +145,24 @@
    }
    /**
     * èŽ·å–ç”¨æˆ·ç®€ç§°
     *
     * @return BI_Nick_Name - ç”¨æˆ·ç®€ç§°
     */
    public String getBiNickName() {
        return biNickName;
    }
    /**
     * è®¾ç½®ç”¨æˆ·ç®€ç§°
     *
     * @param biNickName ç”¨æˆ·ç®€ç§°
     */
    public void setBiNickName(String biNickName) {
        this.biNickName = biNickName == null ? null : biNickName.trim();
    }
    /**
     * èŽ·å–æ‰€å±žå…¬å¸id
     *
     * @return CI_GUID - æ‰€å±žå…¬å¸id
@@ -151,6 +199,118 @@
    }
    /**
     * @return BI_Province_Code
     */
    public String getBiProvinceCode() {
        return biProvinceCode;
    }
    /**
     * @param biProvinceCode
     */
    public void setBiProvinceCode(String biProvinceCode) {
        this.biProvinceCode = biProvinceCode == null ? null : biProvinceCode.trim();
    }
    /**
     * @return BI_Province_Name
     */
    public String getBiProvinceName() {
        return biProvinceName;
    }
    /**
     * @param biProvinceName
     */
    public void setBiProvinceName(String biProvinceName) {
        this.biProvinceName = biProvinceName == null ? null : biProvinceName.trim();
    }
    /**
     * @return BI_City_Code
     */
    public String getBiCityCode() {
        return biCityCode;
    }
    /**
     * @param biCityCode
     */
    public void setBiCityCode(String biCityCode) {
        this.biCityCode = biCityCode == null ? null : biCityCode.trim();
    }
    /**
     * @return BI_City_Name
     */
    public String getBiCityName() {
        return biCityName;
    }
    /**
     * @param biCityName
     */
    public void setBiCityName(String biCityName) {
        this.biCityName = biCityName == null ? null : biCityName.trim();
    }
    /**
     * @return BI_District_Code
     */
    public String getBiDistrictCode() {
        return biDistrictCode;
    }
    /**
     * @param biDistrictCode
     */
    public void setBiDistrictCode(String biDistrictCode) {
        this.biDistrictCode = biDistrictCode == null ? null : biDistrictCode.trim();
    }
    /**
     * @return BI_District_Name
     */
    public String getBiDistrictName() {
        return biDistrictName;
    }
    /**
     * @param biDistrictName
     */
    public void setBiDistrictName(String biDistrictName) {
        this.biDistrictName = biDistrictName == null ? null : biDistrictName.trim();
    }
    /**
     * @return BI_Town_Code
     */
    public String getBiTownCode() {
        return biTownCode;
    }
    /**
     * @param biTownCode
     */
    public void setBiTownCode(String biTownCode) {
        this.biTownCode = biTownCode == null ? null : biTownCode.trim();
    }
    /**
     * @return BI_Town_Name
     */
    public String getBiTownName() {
        return biTownName;
    }
    /**
     * @param biTownName
     */
    public void setBiTownName(String biTownName) {
        this.biTownName = biTownName == null ? null : biTownName.trim();
    }
    /**
     * èŽ·å–æ‰€å±žç‰©ä¸šå…¬å¸åç§°id
     *
     * @return BI_Management_Company_Id - æ‰€å±žç‰©ä¸šå…¬å¸åç§°id
src/main/kotlin/cn/flightfeather/supervision/domain/entity/LogMsgSubscribeWx.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,195 @@
package cn.flightfeather.supervision.domain.entity;
import java.util.Date;
import javax.persistence.*;
@Table(name = "sys_log_msg_subscribe_wx")
public class LogMsgSubscribeWx {
    @Id
    @Column(name = "LMS_ID")
    private Integer lmsId;
    @Column(name = "LMS_Open_Id")
    private String lmsOpenId;
    @Column(name = "LMS_Template_Id")
    private String lmsTemplateId;
    @Column(name = "LMS_Data")
    private String lmsData;
    @Column(name = "LMS_Time")
    private Date lmsTime;
    @Column(name = "LMS_Msg_Id")
    private String lmsMsgId;
    @Column(name = "LMS_Result")
    private Boolean lmsResult;
    @Column(name = "LMS_Extension1")
    private String lmsExtension1;
    @Column(name = "LMS_Extension2")
    private String lmsExtension2;
    @Column(name = "LMS_Extension3")
    private String lmsExtension3;
    @Column(name = "LMS_Remark")
    private String lmsRemark;
    /**
     * @return LMS_ID
     */
    public Integer getLmsId() {
        return lmsId;
    }
    /**
     * @param lmsId
     */
    public void setLmsId(Integer lmsId) {
        this.lmsId = lmsId;
    }
    /**
     * @return LMS_Open_Id
     */
    public String getLmsOpenId() {
        return lmsOpenId;
    }
    /**
     * @param lmsOpenId
     */
    public void setLmsOpenId(String lmsOpenId) {
        this.lmsOpenId = lmsOpenId == null ? null : lmsOpenId.trim();
    }
    /**
     * @return LMS_Template_Id
     */
    public String getLmsTemplateId() {
        return lmsTemplateId;
    }
    /**
     * @param lmsTemplateId
     */
    public void setLmsTemplateId(String lmsTemplateId) {
        this.lmsTemplateId = lmsTemplateId == null ? null : lmsTemplateId.trim();
    }
    /**
     * @return LMS_Data
     */
    public String getLmsData() {
        return lmsData;
    }
    /**
     * @param lmsData
     */
    public void setLmsData(String lmsData) {
        this.lmsData = lmsData == null ? null : lmsData.trim();
    }
    /**
     * @return LMS_Time
     */
    public Date getLmsTime() {
        return lmsTime;
    }
    /**
     * @param lmsTime
     */
    public void setLmsTime(Date lmsTime) {
        this.lmsTime = lmsTime;
    }
    /**
     * @return LMS_Msg_Id
     */
    public String getLmsMsgId() {
        return lmsMsgId;
    }
    /**
     * @param lmsMsgId
     */
    public void setLmsMsgId(String lmsMsgId) {
        this.lmsMsgId = lmsMsgId == null ? null : lmsMsgId.trim();
    }
    /**
     * @return LMS_Result
     */
    public Boolean getLmsResult() {
        return lmsResult;
    }
    /**
     * @param lmsResult
     */
    public void setLmsResult(Boolean lmsResult) {
        this.lmsResult = lmsResult;
    }
    /**
     * @return LMS_Extension1
     */
    public String getLmsExtension1() {
        return lmsExtension1;
    }
    /**
     * @param lmsExtension1
     */
    public void setLmsExtension1(String lmsExtension1) {
        this.lmsExtension1 = lmsExtension1 == null ? null : lmsExtension1.trim();
    }
    /**
     * @return LMS_Extension2
     */
    public String getLmsExtension2() {
        return lmsExtension2;
    }
    /**
     * @param lmsExtension2
     */
    public void setLmsExtension2(String lmsExtension2) {
        this.lmsExtension2 = lmsExtension2 == null ? null : lmsExtension2.trim();
    }
    /**
     * @return LMS_Extension3
     */
    public String getLmsExtension3() {
        return lmsExtension3;
    }
    /**
     * @param lmsExtension3
     */
    public void setLmsExtension3(String lmsExtension3) {
        this.lmsExtension3 = lmsExtension3 == null ? null : lmsExtension3.trim();
    }
    /**
     * @return LMS_Remark
     */
    public String getLmsRemark() {
        return lmsRemark;
    }
    /**
     * @param lmsRemark
     */
    public void setLmsRemark(String lmsRemark) {
        this.lmsRemark = lmsRemark == null ? null : lmsRemark.trim();
    }
}
src/main/kotlin/cn/flightfeather/supervision/domain/entity/MsgSubscribeWx.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,206 @@
package cn.flightfeather.supervision.domain.entity;
import java.util.Date;
import javax.persistence.*;
@Table(name = "ea_t_msg_subscribe_wx")
public class MsgSubscribeWx {
    @Id
    @Column(name = "MS_ID")
    private Integer msId;
    /**
     * è®¢é˜…的模板id
     */
    @Column(name = "MS_Template_Id")
    private String msTemplateId;
    @Column(name = "MS_Open_Id")
    private String msOpenId;
    /**
     * å‰©ä½™è®¢é˜…次数
     */
    @Column(name = "MS_Count")
    private Integer msCount;
    /**
     * æ˜¯å¦æŽ¥æ”¶æŽ¨é€
     */
    @Column(name = "MS_Accept")
    private Boolean msAccept;
    /**
     * æœ€æ–°æ“ä½œæ—¶é—´
     */
    @Column(name = "MS_Update_Time")
    private Date msUpdateTime;
    @Column(name = "MS_Extension1")
    private String msExtension1;
    @Column(name = "MS_Extension2")
    private String msExtension2;
    @Column(name = "MS_Extension3")
    private String msExtension3;
    @Column(name = "MS_Remark")
    private String msRemark;
    /**
     * @return MS_ID
     */
    public Integer getMsId() {
        return msId;
    }
    /**
     * @param msId
     */
    public void setMsId(Integer msId) {
        this.msId = msId;
    }
    /**
     * èŽ·å–è®¢é˜…çš„æ¨¡æ¿id
     *
     * @return MS_Template_Id - è®¢é˜…的模板id
     */
    public String getMsTemplateId() {
        return msTemplateId;
    }
    /**
     * è®¾ç½®è®¢é˜…的模板id
     *
     * @param msTemplateId è®¢é˜…的模板id
     */
    public void setMsTemplateId(String msTemplateId) {
        this.msTemplateId = msTemplateId == null ? null : msTemplateId.trim();
    }
    /**
     * @return MS_Open_Id
     */
    public String getMsOpenId() {
        return msOpenId;
    }
    /**
     * @param msOpenId
     */
    public void setMsOpenId(String msOpenId) {
        this.msOpenId = msOpenId == null ? null : msOpenId.trim();
    }
    /**
     * èŽ·å–å‰©ä½™è®¢é˜…æ¬¡æ•°
     *
     * @return MS_Count - å‰©ä½™è®¢é˜…次数
     */
    public Integer getMsCount() {
        return msCount;
    }
    /**
     * è®¾ç½®å‰©ä½™è®¢é˜…次数
     *
     * @param msCount å‰©ä½™è®¢é˜…次数
     */
    public void setMsCount(Integer msCount) {
        this.msCount = msCount;
    }
    /**
     * èŽ·å–æ˜¯å¦æŽ¥æ”¶æŽ¨é€
     *
     * @return MS_Accept - æ˜¯å¦æŽ¥æ”¶æŽ¨é€
     */
    public Boolean getMsAccept() {
        return msAccept;
    }
    /**
     * è®¾ç½®æ˜¯å¦æŽ¥æ”¶æŽ¨é€
     *
     * @param msAccept æ˜¯å¦æŽ¥æ”¶æŽ¨é€
     */
    public void setMsAccept(Boolean msAccept) {
        this.msAccept = msAccept;
    }
    /**
     * èŽ·å–æœ€æ–°æ“ä½œæ—¶é—´
     *
     * @return MS_Update_Time - æœ€æ–°æ“ä½œæ—¶é—´
     */
    public Date getMsUpdateTime() {
        return msUpdateTime;
    }
    /**
     * è®¾ç½®æœ€æ–°æ“ä½œæ—¶é—´
     *
     * @param msUpdateTime æœ€æ–°æ“ä½œæ—¶é—´
     */
    public void setMsUpdateTime(Date msUpdateTime) {
        this.msUpdateTime = msUpdateTime;
    }
    /**
     * @return MS_Extension1
     */
    public String getMsExtension1() {
        return msExtension1;
    }
    /**
     * @param msExtension1
     */
    public void setMsExtension1(String msExtension1) {
        this.msExtension1 = msExtension1 == null ? null : msExtension1.trim();
    }
    /**
     * @return MS_Extension2
     */
    public String getMsExtension2() {
        return msExtension2;
    }
    /**
     * @param msExtension2
     */
    public void setMsExtension2(String msExtension2) {
        this.msExtension2 = msExtension2 == null ? null : msExtension2.trim();
    }
    /**
     * @return MS_Extension3
     */
    public String getMsExtension3() {
        return msExtension3;
    }
    /**
     * @param msExtension3
     */
    public void setMsExtension3(String msExtension3) {
        this.msExtension3 = msExtension3 == null ? null : msExtension3.trim();
    }
    /**
     * @return MS_Remark
     */
    public String getMsRemark() {
        return msRemark;
    }
    /**
     * @param msRemark
     */
    public void setMsRemark(String msRemark) {
        this.msRemark = msRemark == null ? null : msRemark.trim();
    }
}
src/main/kotlin/cn/flightfeather/supervision/domain/entity/PersonalInfo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,243 @@
package cn.flightfeather.supervision.domain.entity;
import javax.persistence.*;
@Table(name = "ea_t_personal_info")
public class PersonalInfo {
    /**
     * ä¸ªäººä¿¡æ¯id
     */
    @Id
    @Column(name = "PI_GUID")
    private String piGuid;
    /**
     * å§“名
     */
    @Column(name = "PI_Name")
    private String piName;
    /**
     * ä¸ªäººè¯ä»¶ç±»åž‹id
     */
    @Column(name = "PI_ID_Type_Num")
    private Byte piIdTypeNum;
    /**
     * ä¸ªäººè¯ä»¶ç±»åž‹
     */
    @Column(name = "PI_ID_Type")
    private String piIdType;
    /**
     * è¯ä»¶ç¼–号
     */
    @Column(name = "PI_ID")
    private String piId;
    /**
     * èŒä½ç¼–号
     */
    @Column(name = "PI_Position_Num")
    private Byte piPositionNum;
    /**
     * èŒä½
     */
    @Column(name = "PI_Position")
    private String piPosition;
    @Column(name = "PI_Extension1")
    private String piExtension1;
    @Column(name = "PI_Extension2")
    private String piExtension2;
    @Column(name = "PI_Extension3")
    private String piExtension3;
    @Column(name = "PI_Remark")
    private String piRemark;
    /**
     * èŽ·å–ä¸ªäººä¿¡æ¯id
     *
     * @return PI_GUID - ä¸ªäººä¿¡æ¯id
     */
    public String getPiGuid() {
        return piGuid;
    }
    /**
     * è®¾ç½®ä¸ªäººä¿¡æ¯id
     *
     * @param piGuid ä¸ªäººä¿¡æ¯id
     */
    public void setPiGuid(String piGuid) {
        this.piGuid = piGuid == null ? null : piGuid.trim();
    }
    /**
     * èŽ·å–å§“å
     *
     * @return PI_Name - å§“名
     */
    public String getPiName() {
        return piName;
    }
    /**
     * è®¾ç½®å§“名
     *
     * @param piName å§“名
     */
    public void setPiName(String piName) {
        this.piName = piName == null ? null : piName.trim();
    }
    /**
     * èŽ·å–ä¸ªäººè¯ä»¶ç±»åž‹id
     *
     * @return PI_ID_Type_Num - ä¸ªäººè¯ä»¶ç±»åž‹id
     */
    public Byte getPiIdTypeNum() {
        return piIdTypeNum;
    }
    /**
     * è®¾ç½®ä¸ªäººè¯ä»¶ç±»åž‹id
     *
     * @param piIdTypeNum ä¸ªäººè¯ä»¶ç±»åž‹id
     */
    public void setPiIdTypeNum(Byte piIdTypeNum) {
        this.piIdTypeNum = piIdTypeNum;
    }
    /**
     * èŽ·å–ä¸ªäººè¯ä»¶ç±»åž‹
     *
     * @return PI_ID_Type - ä¸ªäººè¯ä»¶ç±»åž‹
     */
    public String getPiIdType() {
        return piIdType;
    }
    /**
     * è®¾ç½®ä¸ªäººè¯ä»¶ç±»åž‹
     *
     * @param piIdType ä¸ªäººè¯ä»¶ç±»åž‹
     */
    public void setPiIdType(String piIdType) {
        this.piIdType = piIdType == null ? null : piIdType.trim();
    }
    /**
     * èŽ·å–è¯ä»¶ç¼–å·
     *
     * @return PI_ID - è¯ä»¶ç¼–号
     */
    public String getPiId() {
        return piId;
    }
    /**
     * è®¾ç½®è¯ä»¶ç¼–号
     *
     * @param piId è¯ä»¶ç¼–号
     */
    public void setPiId(String piId) {
        this.piId = piId == null ? null : piId.trim();
    }
    /**
     * èŽ·å–èŒä½ç¼–å·
     *
     * @return PI_Position_Num - èŒä½ç¼–号
     */
    public Byte getPiPositionNum() {
        return piPositionNum;
    }
    /**
     * è®¾ç½®èŒä½ç¼–号
     *
     * @param piPositionNum èŒä½ç¼–号
     */
    public void setPiPositionNum(Byte piPositionNum) {
        this.piPositionNum = piPositionNum;
    }
    /**
     * èŽ·å–èŒä½
     *
     * @return PI_Position - èŒä½
     */
    public String getPiPosition() {
        return piPosition;
    }
    /**
     * è®¾ç½®èŒä½
     *
     * @param piPosition èŒä½
     */
    public void setPiPosition(String piPosition) {
        this.piPosition = piPosition == null ? null : piPosition.trim();
    }
    /**
     * @return PI_Extension1
     */
    public String getPiExtension1() {
        return piExtension1;
    }
    /**
     * @param piExtension1
     */
    public void setPiExtension1(String piExtension1) {
        this.piExtension1 = piExtension1 == null ? null : piExtension1.trim();
    }
    /**
     * @return PI_Extension2
     */
    public String getPiExtension2() {
        return piExtension2;
    }
    /**
     * @param piExtension2
     */
    public void setPiExtension2(String piExtension2) {
        this.piExtension2 = piExtension2 == null ? null : piExtension2.trim();
    }
    /**
     * @return PI_Extension3
     */
    public String getPiExtension3() {
        return piExtension3;
    }
    /**
     * @param piExtension3
     */
    public void setPiExtension3(String piExtension3) {
        this.piExtension3 = piExtension3 == null ? null : piExtension3.trim();
    }
    /**
     * @return PI_Remark
     */
    public String getPiRemark() {
        return piRemark;
    }
    /**
     * @param piRemark
     */
    public void setPiRemark(String piRemark) {
        this.piRemark = piRemark == null ? null : piRemark.trim();
    }
}
src/main/kotlin/cn/flightfeather/supervision/domain/entity/RestaurantBaseInfo.java
@@ -1,12 +1,9 @@
package cn.flightfeather.supervision.domain.entity;
import com.fasterxml.jackson.annotation.JsonInclude;
import javax.persistence.*;
@Table(name = "ea_t_restaurant_base_info")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RestaurantBaseInfo implements BaseSpecialInfo {
public class RestaurantBaseInfo implements BaseSpecialInfo{
    @Id
    @Column(name = "RB_GUID")
    private String rbGuid;
@@ -118,6 +115,24 @@
     */
    @Column(name = "RB_Concentration_Area")
    private String rbConcentrationArea;
    /**
     * æŽ’口数量
     */
    @Column(name = "RB_Outfall_Count")
    private Integer rbOutfallCount;
    /**
     * æŽ’口位置
     */
    @Column(name = "RB_Outfall_Location")
    private String rbOutfallLocation;
    /**
     * æŽ’口编号
     */
    @Column(name = "RB_Outfall_Num")
    private String rbOutfallNum;
    @Column(name = "RB_Extension1")
    private String rbExtension1;
@@ -470,6 +485,60 @@
    }
    /**
     * èŽ·å–æŽ’å£æ•°é‡
     *
     * @return RB_Outfall_Count - æŽ’口数量
     */
    public Integer getRbOutfallCount() {
        return rbOutfallCount;
    }
    /**
     * è®¾ç½®æŽ’口数量
     *
     * @param rbOutfallCount æŽ’口数量
     */
    public void setRbOutfallCount(Integer rbOutfallCount) {
        this.rbOutfallCount = rbOutfallCount;
    }
    /**
     * èŽ·å–æŽ’å£ä½ç½®
     *
     * @return RB_Outfall_Location - æŽ’口位置
     */
    public String getRbOutfallLocation() {
        return rbOutfallLocation;
    }
    /**
     * è®¾ç½®æŽ’口位置
     *
     * @param rbOutfallLocation æŽ’口位置
     */
    public void setRbOutfallLocation(String rbOutfallLocation) {
        this.rbOutfallLocation = rbOutfallLocation == null ? null : rbOutfallLocation.trim();
    }
    /**
     * èŽ·å–æŽ’å£ç¼–å·
     *
     * @return RB_Outfall_Num - æŽ’口编号
     */
    public String getRbOutfallNum() {
        return rbOutfallNum;
    }
    /**
     * è®¾ç½®æŽ’口编号
     *
     * @param rbOutfallNum æŽ’口编号
     */
    public void setRbOutfallNum(String rbOutfallNum) {
        this.rbOutfallNum = rbOutfallNum == null ? null : rbOutfallNum.trim();
    }
    /**
     * @return RB_Extension1
     */
    public String getRbExtension1() {
src/main/kotlin/cn/flightfeather/supervision/domain/entity/UserInfoWx.java
@@ -4,13 +4,28 @@
@Table(name = "sm_t_userinfo_wx")
public class UserInfoWx {
    @Column(name = "UI_GUID")
    private String uiGuid;
    /**
     * å¾®ä¿¡id
     */
    @Id
    @Column(name = "UI_Open_Id")
    private String uiOpenId;
    /**
     * åœºæ™¯id
     */
    @Column(name = "UI_GUID")
    private String uiGuid;
    /**
     * ä¼ä¸šid
     */
    @Column(name = "CI_GUID")
    private String ciGuid;
    /**
     * å¾®ä¿¡æ˜µç§°
     */
    @Column(name = "UI_Nick_Name")
    private String uiNickName;
@@ -26,49 +41,91 @@
    @Column(name = "UI_City")
    private String uiCity;
    /**
     * å¾®ä¿¡å¤´åƒ
     */
    @Column(name = "UI_Avatar_Url")
    private String uiAvatarUrl;
    /**
     * å¾®ä¿¡ä¼šè¯sessionId
     */
    @Column(name = "UI_UnionId")
    private String uiUnionid;
    /**
     * @return UI_GUID
     * ä¸ªäººä¿¡æ¯id
     */
    public String getUiGuid() {
        return uiGuid;
    }
    @Column(name = "PI_GUID")
    private String piGuid;
    /**
     * @param uiGuid
     */
    public void setUiGuid(String uiGuid) {
        this.uiGuid = uiGuid == null ? null : uiGuid.trim();
    }
    /**
     * @return UI_Open_Id
     * èŽ·å–å¾®ä¿¡id
     *
     * @return UI_Open_Id - å¾®ä¿¡id
     */
    public String getUiOpenId() {
        return uiOpenId;
    }
    /**
     * @param uiOpenId
     * è®¾ç½®å¾®ä¿¡id
     *
     * @param uiOpenId å¾®ä¿¡id
     */
    public void setUiOpenId(String uiOpenId) {
        this.uiOpenId = uiOpenId == null ? null : uiOpenId.trim();
    }
    /**
     * @return UI_Nick_Name
     * èŽ·å–åœºæ™¯id
     *
     * @return UI_GUID - åœºæ™¯id
     */
    public String getUiGuid() {
        return uiGuid;
    }
    /**
     * è®¾ç½®åœºæ™¯id
     *
     * @param uiGuid åœºæ™¯id
     */
    public void setUiGuid(String uiGuid) {
        this.uiGuid = uiGuid == null ? null : uiGuid.trim();
    }
    /**
     * èŽ·å–ä¼ä¸šid
     *
     * @return CI_GUID - ä¼ä¸šid
     */
    public String getCiGuid() {
        return ciGuid;
    }
    /**
     * è®¾ç½®ä¼ä¸šid
     *
     * @param ciGuid ä¼ä¸šid
     */
    public void setCiGuid(String ciGuid) {
        this.ciGuid = ciGuid == null ? null : ciGuid.trim();
    }
    /**
     * èŽ·å–å¾®ä¿¡æ˜µç§°
     *
     * @return UI_Nick_Name - å¾®ä¿¡æ˜µç§°
     */
    public String getUiNickName() {
        return uiNickName;
    }
    /**
     * @param uiNickName
     * è®¾ç½®å¾®ä¿¡æ˜µç§°
     *
     * @param uiNickName å¾®ä¿¡æ˜µç§°
     */
    public void setUiNickName(String uiNickName) {
        this.uiNickName = uiNickName == null ? null : uiNickName.trim();
@@ -131,30 +188,56 @@
    }
    /**
     * @return UI_Avatar_Url
     * èŽ·å–å¾®ä¿¡å¤´åƒ
     *
     * @return UI_Avatar_Url - å¾®ä¿¡å¤´åƒ
     */
    public String getUiAvatarUrl() {
        return uiAvatarUrl;
    }
    /**
     * @param uiAvatarUrl
     * è®¾ç½®å¾®ä¿¡å¤´åƒ
     *
     * @param uiAvatarUrl å¾®ä¿¡å¤´åƒ
     */
    public void setUiAvatarUrl(String uiAvatarUrl) {
        this.uiAvatarUrl = uiAvatarUrl == null ? null : uiAvatarUrl.trim();
    }
    /**
     * @return UI_UnionId
     * èŽ·å–å¾®ä¿¡ä¼šè¯sessionId
     *
     * @return UI_UnionId - å¾®ä¿¡ä¼šè¯sessionId
     */
    public String getUiUnionid() {
        return uiUnionid;
    }
    /**
     * @param uiUnionid
     * è®¾ç½®å¾®ä¿¡ä¼šè¯sessionId
     *
     * @param uiUnionid å¾®ä¿¡ä¼šè¯sessionId
     */
    public void setUiUnionid(String uiUnionid) {
        this.uiUnionid = uiUnionid == null ? null : uiUnionid.trim();
    }
    /**
     * èŽ·å–ä¸ªäººä¿¡æ¯id
     *
     * @return PI_GUID - ä¸ªäººä¿¡æ¯id
     */
    public String getPiGuid() {
        return piGuid;
    }
    /**
     * è®¾ç½®ä¸ªäººä¿¡æ¯id
     *
     * @param piGuid ä¸ªäººä¿¡æ¯id
     */
    public void setPiGuid(String piGuid) {
        this.piGuid = piGuid == null ? null : piGuid.trim();
    }
}
src/main/kotlin/cn/flightfeather/supervision/domain/entity/Userinfo.kt
@@ -1,13 +1,14 @@
package cn.flightfeather.supervision.domain.entity
import com.fasterxml.jackson.annotation.JsonInclude
import java.util.*
import javax.persistence.Column
import javax.persistence.Id
import javax.persistence.Table
@Table(name = "sm_t_userinfo")
@JsonInclude(JsonInclude.Include.NON_NULL)
open class  Userinfo {
open class Userinfo {
    @Id
    @Column(name = "UI_GUID")
    var guid: String? = null
@@ -28,7 +29,7 @@
    var password: String? = null
    /**
     *
     * å€¼åŸŸè¡¨ä¸­èŽ·å–ï¼ˆ1:我司用户;2:被监管对象单位用户;3:主管部门用户)
     */
    @Column(name = "UI_UserTypeID")
    var usertypeid: Byte? = null
@@ -57,6 +58,12 @@
    @Column(name = "UI_WechatID")
    var wechatid: String? = null
    @Column(name = "UI_Create_Time")
    var uiCreateTime: Date? = null
    @Column(name = "UI_Login_Time")
    var uiLoginTime: Date? = null
    @Column(name = "UI_Extension1")
    var extension1: String? = null
src/main/kotlin/cn/flightfeather/supervision/domain/enumeration/AuthenticationStatus.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package cn.flightfeather.supervision.domain.enumeration
/**
 * ç”¨æˆ·è®¤è¯çŠ¶æ€
 */
enum class AuthenticationStatus(val value: Byte, val des: String) {
    YES(1, "authenticated"),
    NO(0, "unauthorized")
}
src/main/kotlin/cn/flightfeather/supervision/domain/mapper/LogMsgSubscribeWxMapper.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
package cn.flightfeather.supervision.domain.mapper
import cn.flightfeather.supervision.domain.entity.LogMsgSubscribeWx
import cn.flightfeather.supervision.domain.util.MyMapper
import org.apache.ibatis.annotations.Mapper
@Mapper
interface LogMsgSubscribeWxMapper : MyMapper<LogMsgSubscribeWx?>
src/main/kotlin/cn/flightfeather/supervision/domain/mapper/MsgSubscribeWxMapper.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
package cn.flightfeather.supervision.domain.mapper
import cn.flightfeather.supervision.domain.entity.MsgSubscribeWx
import cn.flightfeather.supervision.domain.util.MyMapper
import org.apache.ibatis.annotations.Mapper
@Mapper
interface MsgSubscribeWxMapper : MyMapper<MsgSubscribeWx?>
src/main/kotlin/cn/flightfeather/supervision/domain/mapper/PersonalInfoMapper.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
package cn.flightfeather.supervision.domain.mapper
import cn.flightfeather.supervision.domain.entity.PersonalInfo
import cn.flightfeather.supervision.domain.util.MyMapper
import org.apache.ibatis.annotations.Mapper
@Mapper
interface PersonalInfoMapper : MyMapper<PersonalInfo?>
src/main/kotlin/cn/flightfeather/supervision/infrastructure/utils/PinYin.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package cn.flightfeather.supervision.infrastructure.utils
import net.sourceforge.pinyin4j.PinyinHelper
class PinYin {
    companion object{
        fun getPinYinHeader(str: String): String {
            val temp = str.trim()
            val result = StringBuilder()
            for (i in temp.indices) {
                if (i >= 6) {
                    break
                }
                val it = temp[i]
                if (it in 'a'..'z' || it in 'A'..'Z' || it in '0'..'9') {
                    result.append(it.toString()) //如果字符串是英文不改变
                } else {
                    PinyinHelper.toHanyuPinyinStringArray(it)?.let { pinyin ->
                        result.append(pinyin[0].elementAt(0) + "")
                    }
                }
            }
            return result.toString()
        }
        /**
         * ç¬¬ä¸€ä¸ªæ–‡å­—获取完整拼音,之后的只取首字母
         */
        fun getPinYinHeader2(str: String): Array<String>? {
            val temp = str.trim()
            val result = mutableListOf<String>()
            var i = 0
            temp.forEach {
                //                if (i >= 6) {
//                    return@forEach
//                }
                if (it in 'a'..'z' || it in 'A'..'Z' || it in '0'..'9') {
                    result.add(it.toString()) //如果字符串是英文不改变
                } else {
                    PinyinHelper.toHanyuPinyinStringArray(it)?.let { pinyin ->
                        if (i == 0) {
                            result.add(pinyin[0].dropLast(1))
                        } else {
                            result.add(pinyin[0].elementAt(0) + "")
                        }
                    }
                }
                i++
            }
            return result.toTypedArray()
        }
    }
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/AuthService.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package cn.flightfeather.supervision.lightshare.service
import cn.flightfeather.supervision.domain.entity.Company
import cn.flightfeather.supervision.domain.entity.PersonalInfo
import cn.flightfeather.supervision.domain.enumeration.SceneType
import cn.flightfeather.supervision.lightshare.vo.AuthSceneVo
import cn.flightfeather.supervision.lightshare.vo.BaseResponse
interface AuthService {
    fun authCompany(wxUserId: String, company: Company): BaseResponse<String>
    /**
     * ç”¨æˆ·åœºæ™¯è®¤è¯
     * @param wxUserId å¾®ä¿¡ç”¨æˆ·openId
     * @param sceneType åœºæ™¯ç±»åž‹ï¼Œ[SceneType.value]
     * @param sceneInfo åœºæ™¯ä¿¡æ¯ï¼Œ[AuthSceneVo]
     * @see SceneType
     * @see AuthSceneVo
     * @return è®¤è¯æˆåŠŸä¸Žå¦
     */
    fun authScene(wxUserId: String, sceneType: Int, sceneInfo: String): BaseResponse<String>
    fun authPersonal(wxUserId: String, personalInfo: PersonalInfo): BaseResponse<String>
    /**
     * èŽ·å–ç”¨æˆ·è®¤è¯çŠ¶æ€
     * @param wxUserId å¾®ä¿¡ç”¨æˆ·openId
     * @param userId åœºæ™¯ç”¨æˆ·id
     * @return è®¤è¯çŠ¶æ€ï¼Œ [企业认证状态, åœºæ™¯è®¤è¯çŠ¶æ€, ä¸ªäººè®¤è¯çŠ¶æ€]
     */
    fun authStatus(wxUserId: String?, userId: String?): BaseResponse<List<Boolean>>
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/AuthServiceImpl.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,224 @@
package cn.flightfeather.supervision.lightshare.service.Impl
import cn.flightfeather.supervision.domain.entity.*
import cn.flightfeather.supervision.domain.enumeration.AuthenticationStatus
import cn.flightfeather.supervision.domain.enumeration.SceneType
import cn.flightfeather.supervision.domain.mapper.*
import cn.flightfeather.supervision.infrastructure.utils.PinYin
import cn.flightfeather.supervision.infrastructure.utils.UUIDGenerator
import cn.flightfeather.supervision.lightshare.service.AuthService
import cn.flightfeather.supervision.lightshare.vo.AuthSceneRestVo
import cn.flightfeather.supervision.lightshare.vo.AuthSceneVo
import cn.flightfeather.supervision.lightshare.vo.BaseResponse
import com.google.gson.Gson
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import tk.mybatis.mapper.entity.Example
@Service
class AuthServiceImpl(
    private val userinfoMapper: UserinfoMapper,
    private val userInfoWxMapper: UserInfoWxMapper,
    private val companyMapper: CompanyMapper,
    private val personalInfoMapper: PersonalInfoMapper,
    private val baseInfoMapper: BaseInfoMapper,
    private val fumePurifyDeviceMapper: FumePurifyDeviceMapper,
    private val monitorDeviceMapper: MonitorDeviceMapper,
    private val restaurantBaseInfoMapper: RestaurantBaseInfoMapper,
    private val vehicleBaseInfoMapper: VehicleBaseInfoMapper
): AuthService {
    @Transactional
    override fun authCompany(wxUserId: String, company: Company): BaseResponse<String> {
        val wxUser = userInfoWxMapper.selectByPrimaryKey(wxUserId) ?: return BaseResponse(false, "用户微信id不存在")
        company.ciExtension3 = AuthenticationStatus.YES.des
        if (company.ciGuid == null) {
            company.ciGuid = UUIDGenerator.generate16ShortUUID()
            companyMapper.insert(company)
        } else {
            companyMapper.updateByPrimaryKeySelective(company)
        }
        val bInfo = if (wxUser.uiGuid == null) null else baseInfoMapper.selectByPrimaryKey(wxUser.uiGuid)
        if (bInfo != null && bInfo.ciGuid != company.ciGuid) {
            bInfo.ciGuid = company.ciGuid
            bInfo.ciName = company.ciName
            baseInfoMapper.updateByPrimaryKeySelective(bInfo)
        }
        if (wxUser.ciGuid != company.ciGuid) {
            wxUser.ciGuid = company.ciGuid
            userInfoWxMapper.updateByPrimaryKeySelective(wxUser)
        }
        return BaseResponse(true)
    }
    @Transactional
    override fun authScene(wxUserId: String, sceneType: Int, sceneInfo: String): BaseResponse<String> {
//        val user = userinfoMapper.selectByPrimaryKey(userId) ?: return BaseResponse(false, "用户场景id不存在")
        val wxUser = userInfoWxMapper.selectByPrimaryKey(wxUserId) ?: return BaseResponse(false, "用户微信id不存在")
        val gson = Gson()
        // æ‰€æœ‰åœºæ™¯ç»Ÿä¸€çš„基础信息
        val asVo = gson.fromJson(sceneInfo, AuthSceneVo::class.java)
        var bInfo = if (wxUser.uiGuid == null) null else baseInfoMapper.selectByPrimaryKey(wxUser.uiGuid)
        if (bInfo == null) {
            val cInfo = if (wxUser.ciGuid == null) null else companyMapper.selectByPrimaryKey(wxUser.ciGuid)
            val name = getUName(asVo.biName ?: "")
            //新建场景账号及场景信息
            val newUser = Userinfo().apply {
                guid = UUIDGenerator.generate16ShortUUID()
                acountname = name
                realname = asVo.biName
                password = "123456"
                usertypeid = 3
                usertype = "企业"
                isenable = true
                if (asVo.biLocation.isNotEmpty()) extension1 = asVo.biLocation[2]
                extension2 = sceneType.toString()
            }
            bInfo = asVo.toNewBaseInfo(newUser, cInfo)
            userinfoMapper.insert(newUser)
            baseInfoMapper.insert(bInfo)
        } else {
            val userInfo = userinfoMapper.selectByPrimaryKey(bInfo.biGuid)
            userInfo.apply {
                realname = asVo.biName
                if (asVo.biLocation.isNotEmpty()) extension1 = asVo.biLocation[2]
            }
            asVo.updateBaseInfo(bInfo)
            userinfoMapper.updateByPrimaryKeySelective(userInfo)
            baseInfoMapper.updateByPrimaryKeySelective(bInfo)
        }
        val userId = bInfo.biGuid
        when (sceneType) {
            SceneType.Restaurant.value -> {
                val info = gson.fromJson(sceneInfo, AuthSceneRestVo::class.java)
                //餐饮店基本信息录入
                var rbInfo = restaurantBaseInfoMapper.selectByPrimaryKey(userId)
                if (rbInfo == null) {
                    rbInfo = info.toNewRestInfo(userId)
                    restaurantBaseInfoMapper.insert(rbInfo)
                } else {
                    info.updateRestInfo(rbInfo)
                    restaurantBaseInfoMapper.updateByPrimaryKeySelective(rbInfo)
                }
                //餐饮店油烟净化装置信息录入
                var fpdInfo = fumePurifyDeviceMapper.selectByExample(Example(FumePurifyDevice::class.java).apply {
                    createCriteria().andEqualTo("fpUserId", userId)
                })?.takeIf { it.isNotEmpty() }?.get(0)
                if (fpdInfo == null) {
                    fpdInfo = info.toNewFpdInfo(bInfo)
                    fumePurifyDeviceMapper.insert(fpdInfo)
                } else {
                    info.updateFpdInfo(fpdInfo)
                    fumePurifyDeviceMapper.updateByPrimaryKeySelective(fpdInfo)
                }
                //餐饮店油烟监测设备信息录入
                var mdInfo = monitorDeviceMapper.selectByExample(Example(MonitorDevice::class.java).apply {
                    createCriteria().andEqualTo("mdUserId", userId)
                })?.takeIf { it.isNotEmpty() }?.get(0)
                if (mdInfo == null) {
                    mdInfo = info.toNewMdInfo(bInfo)
                    monitorDeviceMapper.insert(mdInfo)
                } else {
                    info.updateMdInfo(mdInfo)
                    monitorDeviceMapper.updateByPrimaryKeySelective(mdInfo)
                }
            }
            SceneType.Construction.value -> {
                val info = gson.fromJson(sceneInfo, AuthSceneVo::class.java)
            }
            SceneType.Wharf.value -> {
                val info = gson.fromJson(sceneInfo, AuthSceneVo::class.java)
            }
            SceneType.StorageYard.value -> {
                val info = gson.fromJson(sceneInfo, AuthSceneVo::class.java)
            }
            SceneType.MixingPlant.value -> {
                val info = gson.fromJson(sceneInfo, AuthSceneVo::class.java)
            }
            SceneType.Industrial.value -> {
                val info = gson.fromJson(sceneInfo, AuthSceneVo::class.java)
            }
            SceneType.VehicleRepair.value -> {
                val info = gson.fromJson(sceneInfo, AuthSceneVo::class.java)
            }
            else-> return BaseResponse(false, "用户场景类型错误")
        }
        return BaseResponse(true, "场景认证完成")
    }
    override fun authPersonal(wxUserId: String, personalInfo: PersonalInfo): BaseResponse<String> {
        val wxUser = userInfoWxMapper.selectByPrimaryKey(wxUserId) ?: return BaseResponse(false, "用户微信id不存在")
        personalInfo.piExtension3 = AuthenticationStatus.YES.des
        if (personalInfo.piGuid == null) {
            personalInfo.piGuid = UUIDGenerator.generate16ShortUUID()
            personalInfoMapper.insert(personalInfo)
        } else {
            personalInfoMapper.updateByPrimaryKeySelective(personalInfo)
        }
        if (wxUser.piGuid != personalInfo.piGuid) {
            wxUser.piGuid = personalInfo.piGuid
            userInfoWxMapper.updateByPrimaryKeySelective(wxUser)
        }
        return BaseResponse(true)
    }
    override fun authStatus(wxUserId: String?, userId: String?): BaseResponse<List<Boolean>> {
        val status = mutableListOf(false, false, false)
        if (wxUserId != null) {
            val wxUser = userInfoWxMapper.selectByPrimaryKey(wxUserId) ?: return BaseResponse(false, "该微信账户不存在")
            //判断企业信息是否认证
            if (wxUser.ciGuid != null) {
                companyMapper.selectByPrimaryKey(wxUser.ciGuid)?.let { c ->
                    if (c.ciExtension3 == AuthenticationStatus.YES.des) status[0] = true
                }
            }
            //判断场景信息是否认证
            if (wxUser.uiGuid != null) {
                baseInfoMapper.selectByPrimaryKey(wxUser.uiGuid)?.let {b ->
                    if (b.biExtension3 == AuthenticationStatus.YES.des) status[1] = true
                }
            }
            //判断个人信息是否认证
            if (wxUser.piGuid != null) {
                personalInfoMapper.selectByPrimaryKey(wxUser.piGuid)?.let { p ->
                    if (p.piExtension3 == AuthenticationStatus.YES.des) status[2] = true
                }
            }
        }else if (userId != null) {
            val user = userinfoMapper.selectByPrimaryKey(userId) ?: return BaseResponse(false, "该场景账户不存在")
            val baseInfo = baseInfoMapper.selectByPrimaryKey(userId)
            //判断企业信息是否认证
            if (baseInfo?.ciGuid != null) {
                companyMapper.selectByPrimaryKey(baseInfo.ciGuid)?.let { c ->
                    if (c.ciExtension3 == AuthenticationStatus.YES.des) status[0] = true
                }
            }
            //判断场景信息是否认证
            if (baseInfo?.biExtension3 == AuthenticationStatus.YES.des) status[1] = true
            //判断个人信息是否认证
            // TODO: 2022/10/11 æ­¤åˆ†æ”¯ä¸‹æš‚时无个人认证
        }
        return BaseResponse(true, data = status)
    }
    private fun getUName(sceneName: String): String {
        var uName = if (sceneName.isNotBlank()) PinYin.getPinYinHeader(sceneName) else UUIDGenerator.generateShortUUID()
        var repeated = false
        var i = 1
        do {
            userinfoMapper.selectByExample(Example(Userinfo::class.java).apply {
                createCriteria().andEqualTo("acountname", uName)
            }).let {
                repeated = it.isNotEmpty()
                if (repeated) {
                    uName += i
                }
            }
            i++
        } while (repeated && i < 20)
        if (repeated) uName = UUIDGenerator.generateShortUUID()
        return uName
    }
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/LedgerServiceImpl.kt
@@ -60,6 +60,7 @@
            if (sceneType != SceneType.NoType.value) {
                createCriteria().andEqualTo("lScenetype", sceneType)
            }
            orderBy("lTypeid")
        })
        val records = getLedgerRecords(userId, null, sceneType, time)
        val resultList = mutableListOf<LedgerSubTypeVo>()
@@ -170,8 +171,19 @@
    }
    override fun getLedgerDetail2(userId: String, ledgerSubTypeId: Int?, sceneType: Int, time: String): List<LedgerVo> {
        val records = getLedgerRecords(userId, ledgerSubTypeId, sceneType, time)
    override fun getLedgerDetail2(userId: String, ledgerSubTypeId: Int?, sceneType: Int, time: String?): List<LedgerVo> {
        val records = if (time != null) {
            getLedgerRecords(userId, ledgerSubTypeId, sceneType, time)
        } else {
            // FIXME: 2022/10/13 æš‚时只支持ledgerSubTypeId不为null的情况
            if (ledgerSubTypeId == null) return emptyList()
            PageHelper.startPage<LedgerRecord>(1, 1)
            ledgerRecordMapper.selectByExample(Example(LedgerRecord::class.java).apply {
                createCriteria().andEqualTo("lrSubmitid", userId)
                    .andEqualTo("lsSubtypeid", ledgerSubTypeId)
                orderBy("lrSubmitdate").desc()
            })
        }
        val result = ArrayList<LedgerVo>()
        records.forEach {
            val media = ledgerMediaFileMapper.selectByExample(
@@ -387,6 +399,7 @@
    override fun copyLedger(userId: String, time: String, copyLedgerList: List<CopyLedgerVo>): BaseResponse<String> {
        val year = time.split("-")[0]
        val month = time.split("-")[1]
        val date = DateUtil.StringToDate(time, DateUtil.DateStyle.YYYY_MM)
        copyLedgerList.forEach {
            //去重判断
            val r = ledgerRecordMapper.selectByExample(Example(LedgerRecord::class.java).apply {
@@ -419,7 +432,7 @@
                lr.lrYear = year.toInt()
                lr.lrMonth = month.toByte()
                lr.lrIssubmitontime = true
                lr.lrSubmitdate = Date()
                lr.lrSubmitdate = date
                lr.lrExtension2 = "copy"//表明是复制的台账
                ledgerRecordMapper.insert(lr)
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/NotificationServiceImpl.kt
@@ -1,12 +1,12 @@
package cn.flightfeather.supervision.lightshare.service.Impl
import cn.flightfeather.supervision.common.net.WXHttpService
import cn.flightfeather.supervision.common.wx.MessageWxVo
import cn.flightfeather.supervision.common.wx.TemplateManager
import cn.flightfeather.supervision.common.wx.WxTokenManager
import cn.flightfeather.supervision.domain.entity.*
import cn.flightfeather.supervision.domain.enumeration.DistrictType
import cn.flightfeather.supervision.domain.enumeration.UserType
import cn.flightfeather.supervision.domain.mapper.NoticeMapper
import cn.flightfeather.supervision.domain.mapper.NoticeReadStateMapper
import cn.flightfeather.supervision.domain.mapper.NotificationMapper
import cn.flightfeather.supervision.domain.mapper.UserinfoMapper
import cn.flightfeather.supervision.lightshare.repository.MeetingParticipantRepository
import cn.flightfeather.supervision.lightshare.service.NotificationService
import cn.flightfeather.supervision.lightshare.vo.NoticeReadStateVo
@@ -14,7 +14,9 @@
import cn.flightfeather.supervision.push.PushService
import cn.flightfeather.supervision.domain.enumeration.ParticipantType
import cn.flightfeather.supervision.domain.enumeration.SceneType
import cn.flightfeather.supervision.domain.mapper.*
import cn.flightfeather.supervision.infrastructure.utils.UUIDGenerator
import com.alibaba.fastjson.JSON
import com.flightfeather.taizhang.model.enumeration.NotificationType
import com.flightfeather.taizhang.model.enumeration.WorkSubType
import com.github.pagehelper.PageHelper
@@ -31,7 +33,8 @@
        val noticeMapper: NoticeMapper,
        val noticeReadStateMapper: NoticeReadStateMapper,
        val userinfoMapper: UserinfoMapper,
        val meetingParticipantRepository: MeetingParticipantRepository
        val meetingParticipantRepository: MeetingParticipantRepository,
        val templateManager: TemplateManager
) : NotificationService {
    override fun getNotificationUnRead(userId: String, page: Int, per_page: Int, response: HttpServletResponse): List<NotificationVo> {
@@ -288,4 +291,9 @@
        }
        return noticeMapper.insert(notice) == 1
    }
    override fun pushMsgWx(templateId: Int): String {
        val res = templateManager.sendMsg(templateId, "otZkc5VRlwauEMPqMluQYdVa4zuE", listOf("台账上传", "2022å¹´10月10日", "3", "请重点关注现场自寻查部分"))
        return if (res) "success" else "fail"
    }
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/UserinfoServiceImpl.kt
@@ -1,18 +1,14 @@
package cn.flightfeather.supervision.lightshare.service.Impl
import cn.flightfeather.supervision.common.net.WXHttpService
import cn.flightfeather.supervision.domain.entity.BaseInfo
import cn.flightfeather.supervision.domain.entity.Company
import cn.flightfeather.supervision.domain.entity.UserInfoWx
import cn.flightfeather.supervision.domain.entity.Userinfo
import cn.flightfeather.supervision.domain.enumeration.SceneType
import cn.flightfeather.supervision.domain.enumeration.UserType
import cn.flightfeather.supervision.domain.mapper.*
import cn.flightfeather.supervision.infrastructure.utils.FileUtil
import cn.flightfeather.supervision.infrastructure.utils.UUIDGenerator
import cn.flightfeather.supervision.lightshare.service.UserinfoService
import cn.flightfeather.supervision.lightshare.vo.*
import com.alibaba.fastjson.JSONObject
import com.github.pagehelper.PageHelper
import org.springframework.beans.BeanUtils
import org.springframework.stereotype.Service
@@ -30,6 +26,8 @@
    val restaurantBaseInfoMapper: RestaurantBaseInfoMapper,
    val vehicleBaseInfoMapper: VehicleBaseInfoMapper,
    val userMapMapper: UserMapMapper,
    val personalInfoMapper: PersonalInfoMapper,
    val userInfoWxMapper: UserInfoWxMapper
) : UserinfoService {
    //根据userinfo条件查询
@@ -45,7 +43,7 @@
        }
    }
    override fun findOne(id: String): Userinfo{
    override fun findOne(id: String): Userinfo {
        val userInfo = userinfoMapper.selectByPrimaryKey(id)
        userMapMapper.selectByPrimaryKey(id)?.let {
            userInfo?.extension3 = it.svUserId
@@ -206,16 +204,21 @@
        }
    }
    override fun changePassword(userId: String, oldPassword: String, newPassword: String): Int {
    override fun changePassword(userId: String, oldPassword: String, newPassword: String): BaseResponse<String> {
        if (newPassword.trim() == "") return BaseResponse(false, "新密码不能为空")
        if (oldPassword == newPassword) return BaseResponse(false, "新密码不能和原密码相同")
        val userInfo = findOne(userId)
        return if (oldPassword != userInfo.password) {
            0
            BaseResponse(false, "原密码错误")
        } else {
            val newUserInfo = Userinfo().apply {
                guid = userInfo.guid
                password = newPassword
                remark = "pwChanged"
            }
            update(newUserInfo)
            BaseResponse(true, "密码修改成功")
        }
    }
@@ -249,20 +252,22 @@
        return result
    }
    override fun getBaseInfo(userId: String): UserBaseInfo {
    override fun getBaseInfo(userId: String, wxUserId: String?): UserBaseInfo {
        val userInfo = userinfoMapper.selectByPrimaryKey(userId) ?: return UserBaseInfo(userId)
        val baseInfo = baseInfoMapper.selectByPrimaryKey(userId) ?: return UserBaseInfo(userId)
        val baseInfo = baseInfoMapper.selectByPrimaryKey(userId)
        val wxUser = if (wxUserId != null) userInfoWxMapper.selectByPrimaryKey(wxUserId) else null
        val mapper = when (userInfo.extension2) {
            SceneType.Restaurant.value.toString() -> restaurantBaseInfoMapper
            SceneType.VehicleRepair.value.toString() -> vehicleBaseInfoMapper
            else -> restaurantBaseInfoMapper
            else -> null
        }
        val specialInfo = mapper.selectByPrimaryKey(baseInfo.biGuid)
        val companyInfo = companyMapper.selectByPrimaryKey(baseInfo.ciGuid)
        val specialInfo = mapper?.selectByPrimaryKey(baseInfo?.biGuid)
        val companyInfo = companyMapper.selectByPrimaryKey(if (baseInfo?.ciGuid != null) baseInfo.ciGuid else wxUser?.ciGuid)
        val personalInfo = personalInfoMapper.selectByPrimaryKey(wxUser?.piGuid)
        return UserBaseInfo(userId, userInfo.realname, baseInfo, companyInfo, specialInfo)
        return UserBaseInfo(userId, userInfo.realname, baseInfo, companyInfo, specialInfo, personalInfo)
    }
    override fun search(district: String?, sceneType: Int?, userType: Int?, page: Int, perPage: Int): BaseResponse<List<Userinfo>> {
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/WxUserServiceImpl.kt
@@ -1,53 +1,58 @@
package cn.flightfeather.supervision.lightshare.service.Impl
import cn.flightfeather.supervision.common.net.WXHttpService
import cn.flightfeather.supervision.common.wx.SHA1
import cn.flightfeather.supervision.common.wx.WxConfig
import cn.flightfeather.supervision.domain.entity.LogMsgSubscribeWx
import cn.flightfeather.supervision.domain.entity.MsgSubscribeWx
import cn.flightfeather.supervision.domain.entity.UserInfoWx
import cn.flightfeather.supervision.domain.entity.Userinfo
import cn.flightfeather.supervision.domain.entity.Version
import cn.flightfeather.supervision.domain.enumeration.UserType
import cn.flightfeather.supervision.domain.mapper.UserInfoWxMapper
import cn.flightfeather.supervision.domain.mapper.UserinfoMapper
import cn.flightfeather.supervision.domain.mapper.VersionMapper
import cn.flightfeather.supervision.infrastructure.utils.FileUtil
import cn.flightfeather.supervision.infrastructure.utils.UUIDGenerator
import cn.flightfeather.supervision.lightshare.service.VersionService
import cn.flightfeather.supervision.domain.mapper.*
import cn.flightfeather.supervision.lightshare.service.WxUserService
import cn.flightfeather.supervision.lightshare.vo.AccessToken
import cn.flightfeather.supervision.lightshare.vo.AccessTokenPW
import cn.flightfeather.supervision.lightshare.vo.AccessTokenWX
import cn.flightfeather.supervision.lightshare.vo.BaseResponse
import cn.flightfeather.supervision.lightshare.vo.VersionVo
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONArray
import com.alibaba.fastjson.JSONObject
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import org.springframework.web.multipart.MultipartFile
import tk.mybatis.mapper.entity.Example
import java.util.*
@Service
class WxUserServiceImpl(val userinfoMapper: UserinfoMapper, val userInfoWxMapper: UserInfoWxMapper): WxUserService {
class WxUserServiceImpl(
        val userinfoMapper: UserinfoMapper,
        val userInfoWxMapper: UserInfoWxMapper,
        val userMapMapper: UserMapMapper,
        val baseInfoMapper: BaseInfoMapper,
        private val msgSubscribeWxMapper: MsgSubscribeWxMapper,
        private val logMsgSubscribeWxMapper: LogMsgSubscribeWxMapper
): WxUserService {
    private val LOGGER = LoggerFactory.getLogger(WxUserServiceImpl::class.java)
    override fun loginWx(accessTokenWX: AccessTokenWX): BaseResponse<Userinfo> {
        accessTokenWX.code ?: return BaseResponse(false, "登录凭证不能为空")
        val res = WXHttpService.code2Session(accessTokenWX.code!!)
//        return BaseResponse(false)
        if (res.success) {
            val json = JSONObject.parseObject(res.m.responseBodyAsString)
            if (json["errcode"] == 0 || json["errcode"] == null) {
                val openid = json["openid"] as String
                val unionid = json["unionid"] as String
                val user = userInfoWxMapper.selectByPrimaryKey(openid)
                return if (user.uiOpenId == null) {
                    val newUserWx = UserInfoWx().apply {
        WXHttpService.code2Session(accessTokenWX.code!!)?.let {
            val openid = it.first
            val unionid = it.second
            val user = userInfoWxMapper.selectByPrimaryKey(openid)
            return if (user == null || user.uiOpenId == null) {
                val newUserWx = UserInfoWx().apply {
//                        uiGuid = UUIDGenerator.generate16ShortUUID()
                        uiOpenId = openid
                        uiNickName = accessTokenWX.nickName
                        uiGender
                        uiCountry
                        uiProvince
                        uiCity
                        uiAvatarUrl = accessTokenWX.avatarUrl
                        uiUnionid = unionid
                    }
                    uiOpenId = openid
                    uiNickName = accessTokenWX.nickName
                    uiGender
                    uiCountry
                    uiProvince
                    uiCity
                    uiAvatarUrl = accessTokenWX.avatarUrl
                    uiUnionid = unionid
                }
//                    val newUser = Userinfo().apply {
//                        guid = newUserWx.uiGuid
//                        headIconUrl = newUserWx.uiAvatarUrl
@@ -59,30 +64,246 @@
//                        isenable = true
//                        wechatid = newUserWx.uiOpenId
//                    }
                    var r = userInfoWxMapper.insert(newUserWx)
                var r = userInfoWxMapper.insert(newUserWx)
//                    r += userinfoMapper.insert(newUser)
                    return if (r == 1) {
                        BaseResponse(true, "微信用户注册成功")
                    } else {
                        BaseResponse(false, "微信用户注册失败")
                    }
                return if (r == 1) {
                    BaseResponse(true, "微信用户注册成功")
                } else {
                    if (user.uiGuid != null) {
                        val userinfo = userinfoMapper.selectByPrimaryKey(user.uiGuid)
                        BaseResponse(true, "微信用户登录成功", data = userinfo)
                    } else {
                        BaseResponse(true, "微信用户未绑定企业")
                    }
                    BaseResponse(false, "微信用户注册失败")
                }
            } else {
                return BaseResponse(false, "请求失败:errcode=${json["errcode"]}")
                if (user.uiGuid != null) {
                    val userinfo = userinfoMapper.selectByPrimaryKey(user.uiGuid)
                    BaseResponse(true, "微信用户登录成功", data = userinfo)
                } else {
                    BaseResponse(true, "微信用户未绑定企业")
                }
            }
        }
        return BaseResponse(false, "请求失败, æ— æ³•访问微信接口")
    }
    override fun loginPw(accessTokenPW: AccessTokenPW): AccessToken {
        //1. è´¦å·å¯†ç ç™»å½•时,将附带的微信id与账号绑定
        if (!accessTokenPW.userName.isNullOrBlank() && !accessTokenPW.password.isNullOrBlank()) {
            val user = userinfoMapper.selectByExample(Example(Userinfo::class.java).apply {
                createCriteria().andEqualTo("acountname", accessTokenPW.userName)
                        .andEqualTo("password",accessTokenPW.password)
            })
            if (user.isEmpty()) return AccessToken()
            val result = AccessToken()
            //1.1 å¾®ä¿¡id为空,直接登录; å¾®ä¿¡id不为空,绑定至账号
            if (!accessTokenPW.code.isNullOrBlank()) {
                val baseInfo = baseInfoMapper.selectByPrimaryKey(user[0].guid)
                WXHttpService.code2Session(accessTokenPW.code!!)?.let {
                    val openid = it.first
                    val unionid = it.second
                    val userWx = userInfoWxMapper.selectByPrimaryKey(openid)
                    result.openId = openid
                    return@let if (userWx == null || userWx.uiOpenId == null) {
                        val newUserWx = UserInfoWx().apply {
                            uiGuid = user[0].guid
                            ciGuid = baseInfo?.ciGuid
                            uiOpenId = openid
                            uiNickName = accessTokenPW.nickName
                            uiGender
                            uiCountry
                            uiProvince
                            uiCity
                            uiAvatarUrl = accessTokenPW.avatarUrl
                            uiUnionid = unionid
                        }
                        userInfoWxMapper.insert(newUserWx)
                    } else if (userWx.uiGuid != user[0].guid) {
                        userWx.uiGuid = user[0].guid
                        userWx.ciGuid = baseInfo?.ciGuid
                        userInfoWxMapper.updateByPrimaryKey(userWx)
                    } else {
                        return@let
                    }
                }
            }
            return result.apply {
                userId = user[0].guid
                val sUser = userMapMapper.selectByPrimaryKey(userId)
                sUserId = sUser?.svUserId
                success = true
            }
        }
        //2. å½“微信一键登录时,判断账号绑定情况
        else if (!accessTokenPW.code.isNullOrBlank()) {
            WXHttpService.code2Session(accessTokenPW.code!!)?.let {
                val openid = it.first
                val unionid = it.second
                val userWx = userInfoWxMapper.selectByPrimaryKey(openid)
                return if (userWx == null || userWx.uiOpenId == null) {
                    AccessToken()
                } else {
                    val user = userinfoMapper.selectByPrimaryKey(userWx.uiGuid)
                    AccessToken().apply {
                        if (user?.guid == null) {
                            success = false
                        } else {
                            userId = user.guid
                            val sUser = userMapMapper.selectByPrimaryKey(userId)
                            sUserId = sUser?.svUserId
                            openId = openid
                            success = true
                        }
                    }
                }
            }
            return AccessToken()
        } else {
            return BaseResponse(false, "请求失败, æ— æ³•访问微信接口")
            return AccessToken()
        }
    }
    override fun loginPw(accessTokenPW: AccessTokenPW): BaseResponse<Userinfo> {
        TODO("Not yet implemented")
    override fun subscribeCheck(signature: String, timestamp: String, nonce: String, echostr: String): String {
        val s = SHA1.getSHA1(WxConfig.TOKEN, timestamp, nonce, "")
        LOGGER.info("微信服务器验证,sha1:${s}")
        return if (signature == s) {
            LOGGER.info("success")
            echostr
        } else {
            LOGGER.info("fail")
            "fail"
        }
    }
    override fun subscribeResult(msg: String): String{
        println(msg)
        val json = JSON.parseObject(msg)
        //小程序原始id,不是APP_ID
        val appName = (json["ToUserName"] as String?) ?: throw NullPointerException("微信服务器发送的订阅返回消息ToUserName字段为null")
        if (appName != WxConfig.USER_NAME) throw IllegalStateException("小程序原始ID不匹配,微信[${appName}],服务器记录[${WxConfig.USER_NAME}]")
        //用户openid
        val openId = (json["FromUserName"] as String?) ?: throw NullPointerException("微信服务器发送的订阅返回消息FromUserName字段为null")
        //时间戳
        val createTime = (json["CreateTime"] as Int?) ?: throw NullPointerException("微信服务器发送的订阅返回消息CreateTime字段为null")
        //事件类型,此处应该都是event
        val msgType = json["MsgType"] as String?
        //各消息模板返回结果
        val results = mutableListOf<JSONObject>()
        val list = json["List"]
        if (list is JSONArray) {
            list.forEach {
                if (it is JSONObject) {
                    results.add(it)
                }
            }
        } else if (list is JSONObject) {
            results.add(list)
        }
        val event = json["Event"]
        if (event is String) {
            when (event) {
                //用户触发订阅消息弹框后的行为时间结果
                //用户在手机端服务通知消息卡片右上角管理消息时的操作结果
                "subscribe_msg_popup_event" ,
                "subscribe_msg_change_event" -> {
                    results.forEach {
                        val templateId = it["TemplateId"] as String?
                        val sStatus = it["SubscribeStatusString"] as String?
                        //根据时间和用户openId,判断此次操作是否重复
                        val time = Date(createTime.toLong() * 1000)
                        val records = msgSubscribeWxMapper.selectByExample(Example(MsgSubscribeWx::class.java).apply {
                            createCriteria().andEqualTo("msOpenId", openId)
                                .andEqualTo("msTemplateId", templateId)
                        })
                        //用户无订阅记录
                        if (records.isEmpty()) {
                            val m = MsgSubscribeWx().apply {
                                msTemplateId = templateId
                                msOpenId = openId
                                msUpdateTime = time
                                if (sStatus == WxConfig.S_ACCEPT) {
                                    msCount = 1
                                    msAccept = true
                                }else if (sStatus == WxConfig.S_REJECT) {
                                    msCount = 0
                                    msAccept = false
                                }
                            }
                            msgSubscribeWxMapper.insert(m)
                        }
                        //用户有订阅记录
                        else if (records.size == 1) {
                            //先排重
                            val r = records[0]
                            if (r != null && createTime > (r.msUpdateTime?.time ?: 0)) {
                                r.msUpdateTime = time
                                if (sStatus == WxConfig.S_ACCEPT) {
                                    r.msCount += 1
                                    r.msAccept = true
                                } else if (sStatus == WxConfig.S_REJECT) {
                                    r.msAccept = false
                                }
                                msgSubscribeWxMapper.updateByPrimaryKey(r)
                            }
                        }
                        //用户单个模板有多条记录,错误
                        else {
                            throw IllegalStateException("用户[${openId}]微信订阅单条消息记录大于1")
                        }
                    }
                }
                //发送给用户订阅消息的返回结果
                "subscribe_msg_sent_event" -> {
                    results.forEach {
                        val templateId = it["TemplateId"] as String?
                        val msgId = it["MsgID"] as String?
                        val eCode = it["ErrorCode"] as String?
                        val eStatus = it["ErrorStatus"] as String?
                        //推送成功后,该用户该条模板可用次数减一,并且记录推送日志
                        if (eCode == "0") {
                            val records = msgSubscribeWxMapper.selectByExample(Example(MsgSubscribeWx::class.java).apply {
                                createCriteria().andEqualTo("msOpenId", openId)
                                    .andEqualTo("msTemplateId", templateId)
                            })
                            if (records.size != 1) throw IllegalStateException("微信用户[${openId}]模板[${templateId}]订阅次数记录缺失")
                            records[0]?.let { ms ->
                                ms.msCount--
                                msgSubscribeWxMapper.updateByPrimaryKey(ms)
                            }
                            //查找返回消息24小时之内的记录
                            // ä¸€èˆ¬æƒ…况下,主动推送消息后,微信服务器会立即返回对应的推送结果,所以应该只查询到一条没有lmsMsgId的记录,
                            // æˆ–者在微信服务器重复发送的情况下,只查询到一条有对应lmsMsgId的记录,
                            val logs = logMsgSubscribeWxMapper.selectByExample(Example(LogMsgSubscribeWx::class.java).apply {
                                createCriteria().andEqualTo("lmsOpenId", openId)
                                    .andEqualTo("lmsTemplateId", templateId)
                                and(
                                    createCriteria().orIsNull("lmsMsgId").orEqualTo("lmsMsgId", msgId)
                                )
                            }).let { list->
                                var result = list
                                for (l in list) {
                                    if (l?.lmsMsgId == msgId) {
                                        result = emptyList()
                                        break
                                    }
                                }
                                result
                            }
                            if (logs.isNotEmpty()) {
                                val l = logs[0]
                                l?.lmsTime = Date()
                                l?.lmsMsgId = msgId
                                l?.lmsResult = true
                                logMsgSubscribeWxMapper.updateByPrimaryKey(l)
                            }
                        }
                    }
                }
            }
            return "success"
        } else {
            return "fail"
        }
    }
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/LedgerService.kt
@@ -15,7 +15,7 @@
    fun getLedgerDetail(userId: String, ledgerSubTypeId: Int? = null, sceneType: Int, startTime: String, endTime: String, page: Int? = null, perPage: Int, response: HttpServletResponse): ArrayList<LedgerVo>
    fun getLedgerDetail2(userId: String, ledgerSubTypeId: Int? = null, sceneType: Int, time: String): List<LedgerVo>
    fun getLedgerDetail2(userId: String, ledgerSubTypeId: Int? = null, sceneType: Int, time: String?): List<LedgerVo>
    fun uploadLedger(userId: String, ledgerVoList: String, files: Array<MultipartFile>): Boolean
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/NotificationService.kt
@@ -1,5 +1,6 @@
package cn.flightfeather.supervision.lightshare.service
import cn.flightfeather.supervision.common.wx.MessageWxVo
import cn.flightfeather.supervision.domain.entity.MeetingInfo
import cn.flightfeather.supervision.domain.entity.VMRoom
import cn.flightfeather.supervision.lightshare.vo.LedgerSubTypeVo
@@ -26,4 +27,6 @@
    fun pushMeetingReleaseNotification(meetingVo: MeetingInfo, roomVo: VMRoom?, userId: String, title: String, body: String)
    fun releaseNotice(userId: String, noticeVo: NotificationVo): Boolean
    fun pushMsgWx(templateId: Int): String
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/UserinfoService.kt
@@ -29,11 +29,11 @@
    fun upLoadAccountPic(userId: String, files: Array<MultipartFile>): String
    fun changePassword(userId: String, oldPassword: String, newPassword: String): Int
    fun changePassword(userId: String, oldPassword: String, newPassword: String): BaseResponse<String>
    fun searchUser(userId: String,condition: UserSearchCondition, page: Int, perPage: Int, response: HttpServletResponse): List<Userinfo>
    fun getBaseInfo(userId: String): UserBaseInfo
    fun getBaseInfo(userId: String, wxUserId: String?): UserBaseInfo
    fun search(district: String?, sceneType: Int?, userType: Int?, page: Int, perPage: Int): BaseResponse<List<Userinfo>>
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/WxUserService.kt
@@ -1,6 +1,7 @@
package cn.flightfeather.supervision.lightshare.service
import cn.flightfeather.supervision.domain.entity.Userinfo
import cn.flightfeather.supervision.lightshare.vo.AccessToken
import cn.flightfeather.supervision.lightshare.vo.AccessTokenPW
import cn.flightfeather.supervision.lightshare.vo.AccessTokenWX
import cn.flightfeather.supervision.lightshare.vo.BaseResponse
@@ -12,5 +13,14 @@
     */
    fun loginWx(accessTokenWX: AccessTokenWX): BaseResponse<Userinfo>
    fun loginPw(accessTokenPW: AccessTokenPW): BaseResponse<Userinfo>
    /**
     * ç”¨æˆ·åå¯†ç ç™»å½•,同时绑定微信用户
     */
    // FIXME: 2022/9/25 æš‚定
    fun loginPw(accessTokenPW: AccessTokenPW): AccessToken
    fun subscribeCheck(signature: String, timestamp: String, nonce: String, echostr: String): String
    fun subscribeResult(msg: String): String
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/vo/AccessToken.kt
@@ -10,5 +10,7 @@
    var userId: String? = null
    // å¯¹åº”飞羽监管中的用户id
    var sUserId: String? = null
    // å¾®ä¿¡ç”¨æˆ·id
    var openId: String? = null
    var success: Boolean = false
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/vo/AuthSceneRestVo.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,104 @@
package cn.flightfeather.supervision.lightshare.vo
import cn.flightfeather.supervision.domain.entity.BaseInfo
import cn.flightfeather.supervision.domain.entity.FumePurifyDevice
import cn.flightfeather.supervision.domain.entity.MonitorDevice
import cn.flightfeather.supervision.domain.entity.RestaurantBaseInfo
import cn.flightfeather.supervision.infrastructure.utils.UUIDGenerator
import org.springframework.beans.BeanUtils
import java.util.*
/**
 * é¤é¥®åº—认证信息
 */
class AuthSceneRestVo : AuthSceneVo() {
    //经营菜系
    var rbCuisine: String? = null
    //就餐位总数
    var rbTotalSeating: Int? = null
    //灶头数
    var rbCookingRangeNum: Int? = null
    //年均用油量(数据库记录为月度用油量)
    var rbCookingOilCapacity: Int? = null
    //用油类型
    var rbCookingOilType: String? = null
    //所属集中区
    var rbConcentrationArea: String? = null
    //排口数量
    var rbOutfallCount: Int? = null
    //排口位置
    var rbOutfallLocation: String? = null
    //排口编号
    var rbOutfallNum: String? = null
    //净化设备数量
    var fpNum: String? = null
    //监测设备数量
    var mdNum: String? = null
    /**
     * ç”Ÿæˆæ–°çš„餐饮信息对象
     */
    fun toNewRestInfo(userId: String) = RestaurantBaseInfo().apply {
        rbGuid = userId
        updateRestInfo(this)
    }
    /**
     * æ›´æ–°è‡³é¤é¥®ä¿¡æ¯
     */
    fun updateRestInfo(restInfo: RestaurantBaseInfo) {
        restInfo.apply {
//            rbCuisine = rbCuisine
//            rbTotalSeating = rbTotalSeating
//            rbCookingRangeNum = rbCookingRangeNum
//            rbCookingOilType = rbCookingOilType
//            rbConcentrationArea = rbConcentrationArea
//            rbOutfallCount = rbOutfallCount
//            rbOutfallLocation = rbOutfallLocation
//            rbOutfallNum = rbOutfallNum
            BeanUtils.copyProperties(this@AuthSceneRestVo, this)
            rbCookingOilCapacity = this@AuthSceneRestVo.rbCookingOilCapacity?.div(12).toString()
        }
    }
    /**
     * ç”Ÿæˆæ–°çš„餐饮油烟净化装置信息对象
     */
    fun toNewFpdInfo(baseInfo: BaseInfo) = FumePurifyDevice().apply {
        fpGuid = UUIDGenerator.generate16ShortUUID()
        fpUserId = baseInfo.biGuid
        fpUserName = baseInfo.biName
        fpUpdatingTime = Date()
        updateFpdInfo(this)
    }
    /**
     * æ›´æ–°è‡³é¤é¥®æ²¹çƒŸå‡€åŒ–装置信息
     */
    fun updateFpdInfo(fpdInfo: FumePurifyDevice) {
        fpdInfo.apply {
            fpNum = this@AuthSceneRestVo.fpNum
        }
    }
    /**
     * ç”Ÿæˆæ–°çš„餐饮油烟监测设备信息对象
     */
    fun toNewMdInfo(baseInfo: BaseInfo) = MonitorDevice().apply {
        mdGuid = UUIDGenerator.generate16ShortUUID()
        mdUserId = baseInfo.biGuid
        mdUserName = baseInfo.biName
        mdUpdatingTime = Date()
        updateMdInfo(this)
    }
    /**
     * æ›´æ–°è‡³é¤é¥®æ²¹çƒŸç›‘测设备信息
     */
    fun updateMdInfo(mdInfo: MonitorDevice) {
        mdInfo.apply {
            mdNum = this@AuthSceneRestVo.mdNum
        }
    }
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/vo/AuthSceneVo.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package cn.flightfeather.supervision.lightshare.vo
import cn.flightfeather.supervision.domain.entity.BaseInfo
import cn.flightfeather.supervision.domain.entity.Company
import cn.flightfeather.supervision.domain.entity.Userinfo
import cn.flightfeather.supervision.domain.enumeration.AuthenticationStatus
import org.springframework.beans.BeanUtils
import java.util.*
/**
 * åœºæ™¯è®¤è¯ä¿¡æ¯
 */
open class AuthSceneVo {
    //场景名称
    var biName: String? = null
    //场景简称
    var biNickName: String? = null
    //场景联系人
    var biContact: String? = null
    //场景联系电话
    var biTelephone: String? = null
    //场景地址
    var biAddress: String? = null
    //场景所属行政区域
    var biLocation: Array<String> = emptyArray()
    /**
     * ç”Ÿæˆæ–°çš„场景基础信息对象
     */
    fun toNewBaseInfo(newUser: Userinfo, cInfo:Company?) = BaseInfo().apply {
        biGuid = newUser.guid
        ciGuid = cInfo?.ciGuid
        ciName = cInfo?.ciName
        biCreateTime = Date()
        biExtension1 = newUser.acountname
        updateBaseInfo(this)
    }
    /**
     * æ›´æ–°è‡³åœºæ™¯åŸºç¡€ä¿¡æ¯
     */
    fun updateBaseInfo(baseInfo: BaseInfo) {
        baseInfo.apply {
//            biName = biName
//            biNickName = biNickName
//            biContact = biContact
//            biTelephone = biTelephone
//            biAddress = biAddress
            if (biLocation.isNotEmpty()) {
                biProvinceName = biLocation[0]
                biCityName = biLocation[1]
                biDistrictName = biLocation[2]
                biTownName = biLocation[3]
                biProvinceCode = biLocation[4]
                biCityCode = biLocation[5]
                biDistrictCode = biLocation[6]
                biTownCode = biLocation[7]
            }
            biUpdateTime = Date()
            biExtension3 = AuthenticationStatus.YES.des
            BeanUtils.copyProperties(this@AuthSceneVo, this)
        }
    }
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/vo/UserBaseInfo.kt
@@ -3,6 +3,7 @@
import cn.flightfeather.supervision.domain.entity.BaseInfo
import cn.flightfeather.supervision.domain.entity.BaseSpecialInfo
import cn.flightfeather.supervision.domain.entity.Company
import cn.flightfeather.supervision.domain.entity.PersonalInfo
import com.fasterxml.jackson.annotation.JsonInclude
/**
@@ -15,5 +16,6 @@
        val name: String? = null,
        val baseInfo: BaseInfo? = null,
        val company: Company? = null,
        val specialInfo: BaseSpecialInfo? = null
        val specialInfo: BaseSpecialInfo? = null,
        val personalInfo: PersonalInfo? = null
)
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/AuthController.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package cn.flightfeather.supervision.lightshare.web
import cn.flightfeather.supervision.domain.entity.Company
import cn.flightfeather.supervision.domain.entity.PersonalInfo
import cn.flightfeather.supervision.lightshare.service.AuthService
import cn.flightfeather.supervision.lightshare.vo.AuthSceneVo
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import io.swagger.annotations.ApiParam
import org.springframework.web.bind.annotation.*
@Api(tags = ["用户认证相关API接口"])
@RestController
@RequestMapping("/auth")
class AuthController(val authService: AuthService) {
    @ApiOperation(value = "企业信息认证")
    @PostMapping("/company")
    fun authCompany(
        @ApiParam("用户微信id") @RequestParam("wxUserId") wxUserId: String,
        @ApiParam("企业信息") @RequestBody company: Company,
    ) = authService.authCompany(wxUserId, company)
    @ApiOperation(value = "场景信息认证")
    @PostMapping("/scene")
    fun authScene(
        @ApiParam("用户微信id") @RequestParam("wxUserId") wxUserId: String,
        @ApiParam("用户场景类型编号") @RequestParam("sceneType") sceneType: Int,
        @ApiParam("场景信息") @RequestBody sceneInfo: String,
    ) = authService.authScene(wxUserId, sceneType, sceneInfo)
    @ApiOperation(value = "个人信息认证")
    @PostMapping("/personal")
    fun authPersonal(
        @ApiParam("用户微信id") @RequestParam("wxUserId") wxUserId: String,
        @ApiParam("个人信息") @RequestBody personalInfo: PersonalInfo
    ) = authService.authPersonal(wxUserId, personalInfo)
    @ApiOperation(value = "获取用户认证状态")
    @GetMapping("/status")
    fun authStatus(
        @ApiParam("用户微信id") @RequestParam(value = "wxUserId", required = false) wxUserId: String?,
        @ApiParam("用户场景id") @RequestParam(value = "userId", required = false) userId: String?,
    ) = authService.authStatus(wxUserId, userId)
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/LedgerController.kt
@@ -52,7 +52,7 @@
        @ApiParam("用户id") @PathVariable userId: String,
        @ApiParam(value = "台账子类型id, å¦‚果不传,则默认根据场景类型获取所有台账", required = false) @RequestParam(value = "ledgerSubTypeId", required = false) ledgerSubTypeId: Int?,
        @ApiParam("场景类型id") @RequestParam(value = "sceneType") sceneType: Int,
        @ApiParam(value = "时间", example = "yyyy-MM-dd") @RequestParam(value = "time") time: String
        @ApiParam(value = "时间", example = "yyyy-MM-dd") @RequestParam(value = "time", required = false) time: String?
    ) = ledgerService.getLedgerDetail2(userId, ledgerSubTypeId, sceneType, time)
    @ApiOperation(value = "上传台账信息")
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/NotificationController.kt
@@ -43,9 +43,15 @@
    ) = notificationService.getUnReadNoticeNum(userId)
    @ApiOperation(value = "发布通知")
    @PostMapping("{userId}/release")
    @PostMapping("/{userId}/release")
    fun releaseNotice(
        @ApiParam("用户id") @PathVariable("userId") userId: String,
        @ApiParam("通知") @RequestBody notice: NotificationVo
    ) = notificationService.releaseNotice(userId, notice)
    @ApiOperation(value = "推送一条微信订阅消息")
    @GetMapping("/wx/message/subscribe/send")
    fun pushMsgWx(
        @ApiParam("模板id") @RequestParam("templateId") templateId: Int
    ) = notificationService.pushMsgWx(templateId)
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/UserinfoController.kt
@@ -71,7 +71,7 @@
    ) = userinfoService.upLoadAccountPic(userId, files)
    @ApiOperation(value = "修改密码")
    @PostMapping("/password/change/{userId}")
    @PostMapping("/password/change")
    fun changePassword(
        @ApiParam("用户id") @RequestParam("userId") userId: String,
        @ApiParam("旧密码") @RequestParam("oldPassword") oldPassword: String,
@@ -91,8 +91,9 @@
    @ApiOperation(value = "获取用户基本信息")
    @GetMapping("/baseInfo")
    fun getBaseInfo(
        @ApiParam("用户id") @RequestParam("userId") userId: String
    ) = userinfoService.getBaseInfo(userId)
        @ApiParam("用户id") @RequestParam("userId") userId: String,
        @ApiParam("微信用户id") @RequestParam(value = "wxUserId", required = false) wxUserId: String?
    ) = userinfoService.getBaseInfo(userId, wxUserId)
    @ApiOperation(value = "根据给定条件,搜索用户")
    @GetMapping("/search")
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/WxUserController.kt
@@ -1,14 +1,12 @@
package cn.flightfeather.supervision.lightshare.web
import cn.flightfeather.supervision.lightshare.service.WxUserService
import cn.flightfeather.supervision.lightshare.vo.AccessTokenPW
import cn.flightfeather.supervision.lightshare.vo.AccessTokenWX
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import io.swagger.annotations.ApiParam
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*
@Api(tags = ["微信用户信息API接口"])
@RestController
@@ -20,4 +18,26 @@
    fun loginWx(
        @ApiParam("登录信息") @RequestBody accessTokenWX: AccessTokenWX
    ) = wxUserService.loginWx(accessTokenWX)
    @ApiOperation(value = "登录")
    @PostMapping("/loginPW")
    fun loginPW(
            @ApiParam("登录信息") @RequestBody accessTokenPW: AccessTokenPW
    ) = wxUserService.loginPw(accessTokenPW)
    @ApiOperation(value = "微信接入验证")
    @GetMapping("/message/subscribe/result")
    fun subscribeCheck(
        @ApiParam("微信加密签名") @RequestParam signature: String,
        @ApiParam("时间戳") @RequestParam timestamp: String,
        @ApiParam("随机数") @RequestParam nonce: String,
        @ApiParam("随机字符串") @RequestParam echostr: String,
    ) = wxUserService.subscribeCheck(signature, timestamp, nonce, echostr)
    @ApiOperation(value = "微信用户订阅消息结果推送", notes = "微信会将用户订阅消息、消息推送情况等信息通过此接口发送过来")
    @PostMapping("/message/subscribe/result")
    fun subscribeResult(
        @ApiParam("消息") @RequestBody msg: String
    ) = wxUserService.subscribeResult(msg)
}
src/main/kotlin/cn/flightfeather/supervision/timingtask/BaseTimingTask.kt
@@ -19,7 +19,7 @@
    // ä»»åŠ¡æ‰§è¡Œå‘¨æœŸï¼Œå•ä½ï¼šåˆ†é’Ÿ
    abstract val period: Long
    fun execute(localtime:LocalDateTime) {
    open fun execute(localtime:LocalDateTime) {
        val now = LocalDateTime.now()
        if (now.minusSeconds(period * 60 - 5) >= lastTime) {
            lastTime = now
src/main/kotlin/cn/flightfeather/supervision/timingtask/TaskController.kt
@@ -14,7 +14,7 @@
 */
@Component
class TaskController(
        fetchVOC: FetchVOC, pushFume: PushFume
    fetchVOC: TaskFetchVOC, pushFume: TaskPushFume
) {
    companion object {
@@ -32,8 +32,8 @@
    init {
        LOGGER.info("添加定时任务")
        timeTask.clear()
        timeTask.add(fetchVOC)
        timeTask.add(pushFume)
//        timeTask.add(fetchVOC)
//        timeTask.add(pushFume)
        LOGGER.info("添加定时任务完成,任务总计${timeTask.size}个")
    }
src/main/kotlin/cn/flightfeather/supervision/timingtask/TaskFetchVOC.kt
ÎļþÃû´Ó src/main/kotlin/cn/flightfeather/supervision/timingtask/FetchVOC.kt ÐÞ¸Ä
@@ -19,9 +19,9 @@
 * èŽ·å–voc监测数据
 */
@Component
class FetchVOC : BaseTimingTask() {
class TaskFetchVOC : BaseTimingTask() {
    companion object {
        private lateinit var instance: FetchVOC
        private lateinit var instance: TaskFetchVOC
    }
    @Autowired
src/main/kotlin/cn/flightfeather/supervision/timingtask/TaskLedgerRemind.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package cn.flightfeather.supervision.timingtask
import cn.flightfeather.supervision.common.wx.TemplateManager
import cn.flightfeather.supervision.domain.entity.MsgSubscribeWx
import cn.flightfeather.supervision.domain.mapper.MsgSubscribeWxMapper
import org.springframework.stereotype.Component
import tk.mybatis.mapper.entity.Example
import java.time.LocalDateTime
/**
 * å°è´¦ä¸Šä¼ å€’计时提醒
 */
@Component
class TaskLedgerRemind(
    private val msgSubscribeWxMapper: MsgSubscribeWxMapper
) : BaseTimingTask() {
    override val period: Long
        get() = 1440L
    override fun doTask(localtime: LocalDateTime) {
        //1.选择已订阅了该条提醒的微信用户
        val ms = msgSubscribeWxMapper.selectByExample(Example(MsgSubscribeWx::class.java).apply {
            createCriteria().andEqualTo("msTemplateId", TemplateManager.TEMPLATE_1)
                .andGreaterThan("msCount", 0)
        })
        //2.查找微信用户绑定的场景台账上传情况
        //3.根据统计结果决定是否发送提醒推送
    }
    /**
     * å°è´¦æé†’任务定为每月5号早上10点提醒当月10号之前提交台账
     */
    override fun execute(localtime: LocalDateTime) {
        if (localtime.dayOfMonth == 5) {
            doTask(localtime)
        }
    }
}
src/main/kotlin/cn/flightfeather/supervision/timingtask/TaskPushFume.kt
ÎļþÃû´Ó src/main/kotlin/cn/flightfeather/supervision/timingtask/PushFume.kt ÐÞ¸Ä
@@ -8,29 +8,24 @@
import cn.flightfeather.supervision.domain.mapper.DeviceInfoMapper
import cn.flightfeather.supervision.domain.mapper.FumeMinuteValueMapper
import cn.flightfeather.supervision.infrastructure.utils.DateUtil
import com.github.pagehelper.PageHelper
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import tk.mybatis.mapper.entity.Example
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.util.*
import javax.annotation.PostConstruct
import kotlin.math.round
/**
 * ä¸Šä¼ æ²¹çƒŸç›‘测数据
 */
@Component
class PushFume : BaseTimingTask() {
class TaskPushFume : BaseTimingTask() {
    companion object {
//        private lateinit var instance: PushFume
        val LOGGER = LoggerFactory.getLogger(PushFume::class.java)
        val LOGGER = LoggerFactory.getLogger(TaskPushFume::class.java)
    }
    @Autowired
src/main/resources/generator/generatorConfig.xml
@@ -92,7 +92,17 @@
<!--               selectByExampleQueryId="false"/>-->
<!--        <table tableName="epk_t_enforcecase" domainObjectName="EnforceCase" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"-->
<!--               selectByExampleQueryId="false"/>-->
        <table tableName="sm_t_userinfo_wx" domainObjectName="UserInfoWx" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false"/>
<!--        <table tableName="sm_t_userinfo_wx" domainObjectName="UserInfoWx" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"-->
<!--               selectByExampleQueryId="false"/>-->
<!--        <table tableName="ea_t_personal_info" domainObjectName="PersonalInfo" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"-->
<!--               selectByExampleQueryId="false"/>-->
<!--        <table tableName="ea_t_msg_subscribe_wx" domainObjectName="MsgSubscribeWx" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"-->
<!--               enableSelectByExample="false"-->
<!--               selectByExampleQueryId="false"/>-->
<!--        <table tableName="sys_log_msg_subscribe_wx" domainObjectName="LogMsgSubscribeWx" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"-->
<!--               enableSelectByExample="false"-->
<!--               selectByExampleQueryId="false"/>-->
                <table tableName="sm_t_userinfo" domainObjectName="Userinfo" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
                       selectByExampleQueryId="false"/>
    </context>
</generatorConfiguration>
src/main/resources/mapper/BaseInfoMapper.xml
@@ -7,8 +7,17 @@
    -->
    <id column="BI_GUID" property="biGuid" jdbcType="VARCHAR" />
    <result column="BI_Name" property="biName" jdbcType="VARCHAR" />
    <result column="BI_Nick_Name" property="biNickName" jdbcType="VARCHAR" />
    <result column="CI_GUID" property="ciGuid" jdbcType="VARCHAR" />
    <result column="CI_Name" property="ciName" jdbcType="VARCHAR" />
    <result column="BI_Province_Code" property="biProvinceCode" jdbcType="VARCHAR" />
    <result column="BI_Province_Name" property="biProvinceName" jdbcType="VARCHAR" />
    <result column="BI_City_Code" property="biCityCode" jdbcType="VARCHAR" />
    <result column="BI_City_Name" property="biCityName" jdbcType="VARCHAR" />
    <result column="BI_District_Code" property="biDistrictCode" jdbcType="VARCHAR" />
    <result column="BI_District_Name" property="biDistrictName" jdbcType="VARCHAR" />
    <result column="BI_Town_Code" property="biTownCode" jdbcType="VARCHAR" />
    <result column="BI_Town_Name" property="biTownName" jdbcType="VARCHAR" />
    <result column="BI_Management_Company_Id" property="biManagementCompanyId" jdbcType="VARCHAR" />
    <result column="BI_Management_Company" property="biManagementCompany" jdbcType="VARCHAR" />
    <result column="BI_Contact" property="biContact" jdbcType="VARCHAR" />
@@ -25,8 +34,9 @@
    <!--
      WARNING - @mbg.generated
    -->
    BI_GUID, BI_Name, CI_GUID, CI_Name, BI_Management_Company_Id, BI_Management_Company,
    BI_Contact, BI_Telephone, BI_Address, BI_Create_Time, BI_Update_Time, BI_Extension1,
    BI_Extension2, BI_Extension3, BI_Remark
    BI_GUID, BI_Name, BI_Nick_Name, CI_GUID, CI_Name, BI_Province_Code, BI_Province_Name,
    BI_City_Code, BI_City_Name, BI_District_Code, BI_District_Name, BI_Town_Code, BI_Town_Name,
    BI_Management_Company_Id, BI_Management_Company, BI_Contact, BI_Telephone, BI_Address,
    BI_Create_Time, BI_Update_Time, BI_Extension1, BI_Extension2, BI_Extension3, BI_Remark
  </sql>
</mapper>
src/main/resources/mapper/LogMsgSubscribeWxMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.flightfeather.supervision.domain.mapper.LogMsgSubscribeWxMapper" >
  <resultMap id="BaseResultMap" type="cn.flightfeather.supervision.domain.entity.LogMsgSubscribeWx" >
    <!--
      WARNING - @mbg.generated
    -->
    <id column="LMS_ID" property="lmsId" jdbcType="INTEGER" />
    <result column="LMS_Open_Id" property="lmsOpenId" jdbcType="VARCHAR" />
    <result column="LMS_Template_Id" property="lmsTemplateId" jdbcType="VARCHAR" />
    <result column="LMS_Data" property="lmsData" jdbcType="VARCHAR" />
    <result column="LMS_Time" property="lmsTime" jdbcType="TIMESTAMP" />
    <result column="LMS_Msg_Id" property="lmsMsgId" jdbcType="VARCHAR" />
    <result column="LMS_Result" property="lmsResult" jdbcType="BIT" />
    <result column="LMS_Extension1" property="lmsExtension1" jdbcType="VARCHAR" />
    <result column="LMS_Extension2" property="lmsExtension2" jdbcType="VARCHAR" />
    <result column="LMS_Extension3" property="lmsExtension3" jdbcType="VARCHAR" />
    <result column="LMS_Remark" property="lmsRemark" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    <!--
      WARNING - @mbg.generated
    -->
    LMS_ID, LMS_Open_Id, LMS_Template_Id, LMS_Data, LMS_Time, LMS_Msg_Id, LMS_Result,
    LMS_Extension1, LMS_Extension2, LMS_Extension3, LMS_Remark
  </sql>
</mapper>
src/main/resources/mapper/MsgSubscribeWxMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.flightfeather.supervision.domain.mapper.MsgSubscribeWxMapper" >
  <resultMap id="BaseResultMap" type="cn.flightfeather.supervision.domain.entity.MsgSubscribeWx" >
    <!--
      WARNING - @mbg.generated
    -->
    <id column="MS_ID" property="msId" jdbcType="INTEGER" />
    <result column="MS_Template_Id" property="msTemplateId" jdbcType="VARCHAR" />
    <result column="MS_Open_Id" property="msOpenId" jdbcType="VARCHAR" />
    <result column="MS_Count" property="msCount" jdbcType="INTEGER" />
    <result column="MS_Accept" property="msAccept" jdbcType="BIT" />
    <result column="MS_Update_Time" property="msUpdateTime" jdbcType="TIMESTAMP" />
    <result column="MS_Extension1" property="msExtension1" jdbcType="VARCHAR" />
    <result column="MS_Extension2" property="msExtension2" jdbcType="VARCHAR" />
    <result column="MS_Extension3" property="msExtension3" jdbcType="VARCHAR" />
    <result column="MS_Remark" property="msRemark" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    <!--
      WARNING - @mbg.generated
    -->
    MS_ID, MS_Template_Id, MS_Open_Id, MS_Count, MS_Accept, MS_Update_Time, MS_Extension1,
    MS_Extension2, MS_Extension3, MS_Remark
  </sql>
</mapper>
src/main/resources/mapper/PersonalInfoMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.flightfeather.supervision.domain.mapper.PersonalInfoMapper" >
  <resultMap id="BaseResultMap" type="cn.flightfeather.supervision.domain.entity.PersonalInfo" >
    <!--
      WARNING - @mbg.generated
    -->
    <id column="PI_GUID" property="piGuid" jdbcType="VARCHAR" />
    <result column="PI_Name" property="piName" jdbcType="VARCHAR" />
    <result column="PI_ID_Type_Num" property="piIdTypeNum" jdbcType="TINYINT" />
    <result column="PI_ID_Type" property="piIdType" jdbcType="VARCHAR" />
    <result column="PI_ID" property="piId" jdbcType="VARCHAR" />
    <result column="PI_Position_Num" property="piPositionNum" jdbcType="TINYINT" />
    <result column="PI_Position" property="piPosition" jdbcType="VARCHAR" />
    <result column="PI_Extension1" property="piExtension1" jdbcType="VARCHAR" />
    <result column="PI_Extension2" property="piExtension2" jdbcType="VARCHAR" />
    <result column="PI_Extension3" property="piExtension3" jdbcType="VARCHAR" />
    <result column="PI_Remark" property="piRemark" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    <!--
      WARNING - @mbg.generated
    -->
    PI_GUID, PI_Name, PI_ID_Type_Num, PI_ID_Type, PI_ID, PI_Position_Num, PI_Position,
    PI_Extension1, PI_Extension2, PI_Extension3, PI_Remark
  </sql>
</mapper>
src/main/resources/mapper/RestaurantBaseInfoMapper.xml
@@ -24,6 +24,9 @@
    <result column="RB_Cooking_Oil_Type" property="rbCookingOilType" jdbcType="VARCHAR" />
    <result column="RB_Cooking_Oil_Capacity" property="rbCookingOilCapacity" jdbcType="VARCHAR" />
    <result column="RB_Concentration_Area" property="rbConcentrationArea" jdbcType="VARCHAR" />
    <result column="RB_Outfall_Count" property="rbOutfallCount" jdbcType="INTEGER" />
    <result column="RB_Outfall_Location" property="rbOutfallLocation" jdbcType="VARCHAR" />
    <result column="RB_Outfall_Num" property="rbOutfallNum" jdbcType="VARCHAR" />
    <result column="RB_Extension1" property="rbExtension1" jdbcType="VARCHAR" />
    <result column="RB_Extension2" property="rbExtension2" jdbcType="VARCHAR" />
    <result column="RB_Extension3" property="rbExtension3" jdbcType="VARCHAR" />
@@ -36,7 +39,8 @@
    RB_GUID, RB_Cuisine, RB_Cooking_Fumes_Type, RB_Peak_Period, RB_Indoor_Seating, RB_Outdoor_Seating, 
    RB_Total_Seating, RB_Outdoor_Barbecue, RB_Open_Kitchen, RB_Environment, RB_Cooking_Range_Num, 
    RB_Electric_Oven_Num, RB_Other_Cooker_Num, RB_Fresh_Air, RB_Air_Conditioner, RB_Exhaust_Fan, 
    RB_Cooking_Oil_Type, RB_Cooking_Oil_Capacity, RB_Concentration_Area, RB_Extension1,
    RB_Extension2, RB_Extension3, RB_Remark
    RB_Cooking_Oil_Type, RB_Cooking_Oil_Capacity, RB_Concentration_Area, RB_Outfall_Count,
    RB_Outfall_Location, RB_Outfall_Num, RB_Extension1, RB_Extension2, RB_Extension3,
    RB_Remark
  </sql>
</mapper>
src/main/resources/mapper/UserInfoWxMapper.xml
@@ -7,6 +7,7 @@
    -->
    <id column="UI_Open_Id" property="uiOpenId" jdbcType="VARCHAR" />
    <result column="UI_GUID" property="uiGuid" jdbcType="VARCHAR" />
    <result column="CI_GUID" property="ciGuid" jdbcType="VARCHAR" />
    <result column="UI_Nick_Name" property="uiNickName" jdbcType="VARCHAR" />
    <result column="UI_Gender" property="uiGender" jdbcType="VARCHAR" />
    <result column="UI_Country" property="uiCountry" jdbcType="VARCHAR" />
@@ -14,12 +15,13 @@
    <result column="UI_City" property="uiCity" jdbcType="VARCHAR" />
    <result column="UI_Avatar_Url" property="uiAvatarUrl" jdbcType="VARCHAR" />
    <result column="UI_UnionId" property="uiUnionid" jdbcType="VARCHAR" />
    <result column="PI_GUID" property="piGuid" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    <!--
      WARNING - @mbg.generated
    -->
    UI_GUID, UI_Open_Id, UI_Nick_Name, UI_Gender, UI_Country, UI_Province, UI_City, UI_Avatar_Url,
    UI_UnionId
    UI_Open_Id, UI_GUID, CI_GUID, UI_Nick_Name, UI_Gender, UI_Country, UI_Province, UI_City,
    UI_Avatar_Url, UI_UnionId, PI_GUID
  </sql>
</mapper>
src/main/resources/templates/commitment-restaurant.ftl
@@ -38,18 +38,18 @@
                        <p style="margin:0pt; orphans:0; text-align:center; widows:0"><span
                                style=" font-size:12pt">统一社会信用代码</span></p>
                    </td>
                    <td
                    <td colspan="3"
                        style="border-bottom-color:#000000; border-bottom-style:solid; border-bottom-width:0.75pt; border-right-color:#000000; border-right-style:solid; border-right-width:0.75pt; border-top-color:#000000; border-top-style:solid; border-top-width:0.75pt; padding-left:5.4pt; padding-right:5.03pt; vertical-align:middle; width:133.55pt">
                        <p style="margin:0pt; orphans:0; text-align:center; widows:0"><span
                                style=" font-size:12pt">${SocialCode}</span></p>
                    </td>
                    <td
                        style="border-bottom-color:#000000; border-bottom-style:solid; border-bottom-width:0.75pt; border-right-color:#000000; border-right-style:solid; border-right-width:0.75pt; border-top-color:#000000; border-top-style:solid; border-top-width:0.75pt; padding-left:5.4pt; padding-right:5.03pt; vertical-align:middle; width:97.8pt">
                        style="display:none;border-bottom-color:#000000; border-bottom-style:solid; border-bottom-width:0.75pt; border-right-color:#000000; border-right-style:solid; border-right-width:0.75pt; border-top-color:#000000; border-top-style:solid; border-top-width:0.75pt; padding-left:5.4pt; padding-right:5.03pt; vertical-align:middle; width:97.8pt">
                        <p style="margin:0pt; orphans:0; text-align:center; widows:0"><span
                                style=" font-size:12pt">环评审批文号</span></p>
                    </td>
                    <td
                        style="border-bottom-color:#000000; border-bottom-style:solid; border-bottom-width:0.75pt; border-right-color:#000000; border-right-style:solid; border-right-width:0.75pt; border-top-color:#000000; border-top-style:solid; border-top-width:0.75pt; padding-left:5.4pt; padding-right:5.03pt; vertical-align:middle; width:119.55pt">
                        style="display:none;border-bottom-color:#000000; border-bottom-style:solid; border-bottom-width:0.75pt; border-right-color:#000000; border-right-style:solid; border-right-width:0.75pt; border-top-color:#000000; border-top-style:solid; border-top-width:0.75pt; padding-left:5.4pt; padding-right:5.03pt; vertical-align:middle; width:119.55pt">
                        <p style="margin:0pt; orphans:0; text-align:center; widows:0"><span
                                style=" font-size:12pt">${Number}</span></p>
                    </td>
@@ -80,7 +80,7 @@
                                style=" font-size:12pt">${IdNo}</span></p>
                    </td>
                </tr>
                <tr style="height:42.7pt">
                <tr style="height:42.7pt;display:none;">
                    <td colspan="2"
                        style="border-bottom-color:#000000; border-bottom-style:solid; border-bottom-width:0.75pt; border-left-color:#000000; border-left-style:solid; border-left-width:0.75pt; border-right-color:#000000; border-right-style:solid; border-right-width:0.75pt; border-top-color:#000000; border-top-style:solid; border-top-width:0.75pt; padding-left:5.03pt; padding-right:5.03pt; vertical-align:middle; width:106.85pt">
                        <p style="line-height:20pt; margin:0pt; orphans:0; text-align:center; widows:0"><span
@@ -128,13 +128,14 @@
                        </p>
                        <p
                            style="line-height:20pt; margin:0pt; orphans:0; text-align:justify; text-indent:21pt; widows:0">
                            <span style=" font-size:12pt">三、安装油烟净化和异味处理设施,油烟排放浓度符合&lt;上海市餐饮业油烟排放标准》(DB
                                31/844 -2014),并委托符合《清洁行业经营服务规范》的服务单位定期进行维护清洗,及时记录相关台账,确保其正常使用,防止对周边居民造成生活环境污染。 </span>
                            <span style=" font-size:12pt">三、安装油烟净化和异味处理设施,油烟排放浓度符合《上海市餐饮业油烟排放标准》(DB
                                31/844 -2014),并委托规范服务单位参照《排油烟设施清洗技术规范》(T/SHXFXH 002-2021)定期进行维护清洗,及时记录相关台账,确保其正常使用,防止对周边居民造成生活环境污染。 </span>
                        </p>
                        <p
                            style="line-height:20pt; margin:0pt; orphans:0; text-align:justify; text-indent:21pt; widows:0">
                            <span
                                style=" font-size:12pt">四、安装符合《CCAEPI-RG-Y-020-2011》的油烟在线监测装置,并及时与生态环竟部门]油烟监控系统联网,委托规范服务单位定期运维,及时记录相关台账,确保其正常使用,不超标排放。
                                style=" font-size:12pt">四、安装符合《CCAEPI-RG-Y-020-2011》的油烟在线监测装置,根据《餐饮油烟在线监测(光散射法)与监控技术规范》(T/SHAEPI 003-2022),及时与生态环境部门油烟监控数据平台联网,
                                å§”托规范服务单位定期运维,及时记录相关台账,确保其正常使用,不超标排放。
                            </span>
                        </p>
                        <p
src/test/kotlin/cn/flightfeather/supervision/CommonTest.kt
@@ -6,6 +6,7 @@
import com.google.gson.Gson
import org.junit.Test
import org.springframework.boot.json.GsonJsonParser
import java.util.*
import java.util.regex.Pattern
/**
@@ -65,6 +66,9 @@
    @Test
    fun foo4() {
        val d = Date()
        println(d.time)
        d.time = 1666262747
        println(d)
    }
}
src/test/kotlin/cn/flightfeather/supervision/common/wx/TemplateManagerTest.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package cn.flightfeather.supervision.common.wx
import org.junit.Test
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.context.junit4.SpringRunner
@RunWith(SpringRunner::class)
@ExtendWith(SpringExtension::class)
@SpringBootTest
class TemplateManagerTest{
    @Autowired
    lateinit var templateManager: TemplateManager
    @Test
    fun newTemplate() {
        val msg = templateManager.newTemplate(0, "otZkc5cC55BtV2AFZdXMvBw0oJo8", listOf("value1", "value2", "value3", "value4"))
        println(msg)
    }
}
src/test/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/NotificationServiceImplTest.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package cn.flightfeather.supervision.lightshare.service.Impl
import cn.flightfeather.supervision.lightshare.service.NotificationService
import org.junit.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.context.junit4.SpringRunner
import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
@RunWith(SpringRunner::class)
@ExtendWith(SpringExtension::class)
@SpringBootTest
class NotificationServiceImplTest{
    @Autowired
    lateinit var notificationService: NotificationService
    @Test
    fun pushMsgWx() {
        Thread.sleep(10000)
        notificationService.pushMsgWx(0)
        val input = BufferedReader(InputStreamReader(System.`in`))
        val reader = input.readLine()
    }
}
src/test/kotlin/cn/flightfeather/supervision/lightshare/service/Impl/WxUserServiceImplTest.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package cn.flightfeather.supervision.lightshare.service.Impl
import cn.flightfeather.supervision.lightshare.service.WxUserService
import org.junit.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.context.junit4.SpringRunner
@RunWith(SpringRunner::class)
@ExtendWith(SpringExtension::class)
@SpringBootTest
class WxUserServiceImplTest {
    @Autowired
    lateinit var wxUserService: WxUserService
    @Test
    fun subscribeResult() {
        val msg = "{\"ToUserName\":\"gh_213453153\", \"List\": [{\"TemplateId\":\"sadaihdfjhsadoaiwhfefe\", \"SubscribeStatusString\":\"accept\"}]}"
        wxUserService.subscribeResult(msg)
    }
}
src/test/kotlin/cn/flightfeather/supervision/timingtask/PushFumeTest.kt
@@ -1,7 +1,6 @@
package cn.flightfeather.supervision.timingtask
import cn.flightfeather.supervision.SupervisionApplication
import cn.flightfeather.supervision.common.net.FumeHttpService
import cn.flightfeather.supervision.domain.entity.AvgFumeMinuteValue
import cn.flightfeather.supervision.domain.entity.FumeMinuteValue
import cn.flightfeather.supervision.domain.mapper.AvgFumeMinuteValueMapper
@@ -21,14 +20,13 @@
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import kotlin.math.round
@RunWith(SpringJUnit4ClassRunner::class)
@SpringBootTest(classes = [SupervisionApplication::class])
class PushFumeTest {
    @Autowired
    lateinit var pushFume: PushFume
    lateinit var pushFume: TaskPushFume
    @Autowired
    lateinit var fumeMinuteValueMapper: FumeMinuteValueMapper