feiyu02
2022-11-15 23bd719cebe5feeff4e48fde925b0b39755eea93
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,247 @@
//                        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")
        //时间戳
        var createTime = (json["CreateTime"] as Int?)?.toLong() ?: throw NullPointerException("微信服务器发送的订阅返回消息CreateTime字段为null")
        createTime = createTime.times(1000)//此处传过来的时间戳只精确到秒,转换为毫秒
        //事件类型,此处应该都是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())
                        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"
        }
    }
}