| | |
| | | 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.domain.repository.UserInfoRep |
| | | 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 cn.flightfeather.supervision.bgtask.sysnotice.SysNoticeManager |
| | | 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, |
| | | private val sysNoticeManager: SysNoticeManager, |
| | | private val userInfoRep: UserInfoRep |
| | | ): 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 |
| | |
| | | // 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) { |
| | | userInfoRep.loginLog(user.uiGuid) |
| | | 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 |
| | | } |
| | | } |
| | | } |
| | | val u = user[0] |
| | | u?.uiLoginTime = Date() |
| | | userinfoMapper.updateByPrimaryKeySelective(u) |
| | | userInfoRep.loginLog(u?.guid) |
| | | 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) |
| | | user?.uiLoginTime = Date() |
| | | userinfoMapper.updateByPrimaryKeySelective(user) |
| | | userInfoRep.loginLog(user?.guid) |
| | | 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" |
| | | } |
| | | } |
| | | } |