feiyu02
2022-10-21 f22c4b9230808fed4fec80c435eccb4c833349a0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
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.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 com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONArray
import com.alibaba.fastjson.JSONObject
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import tk.mybatis.mapper.entity.Example
import java.util.*
 
@Service
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, "登录凭证不能为空")
 
//        return BaseResponse(false)
        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
                }
//                    val newUser = Userinfo().apply {
//                        guid = newUserWx.uiGuid
//                        headIconUrl = newUserWx.uiAvatarUrl
//                        acountname
//                        realname = newUserWx.uiNickName
//                        password
//                        usertypeid = UserType.Enterprise.value.toByte()
//                        usertype = UserType.Enterprise.des
//                        isenable = true
//                        wechatid = newUserWx.uiOpenId
//                    }
                var r = userInfoWxMapper.insert(newUserWx)
//                    r += userinfoMapper.insert(newUser)
                return if (r == 1) {
                    BaseResponse(true, "微信用户注册成功")
                } else {
                    BaseResponse(false, "微信用户注册失败")
                }
            } else {
                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 AccessToken()
        }
    }
 
    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"
        }
    }
}