feat: 支持微信通知
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
|
using Volo.Abp.Caching;
|
||||||
using Volo.Abp.EventBus.Local;
|
using Volo.Abp.EventBus.Local;
|
||||||
using Volo.Abp.Users;
|
using Volo.Abp.Users;
|
||||||
using Yi.Framework.Bbs.Domain.Shared.Etos;
|
using Yi.Framework.Bbs.Domain.Shared.Etos;
|
||||||
using Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Account;
|
using Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Account;
|
||||||
|
using Yi.Framework.DigitalCollectibles.Domain.Shared.Caches;
|
||||||
using Yi.Framework.DigitalCollectibles.Domain.Shared.Consts;
|
using Yi.Framework.DigitalCollectibles.Domain.Shared.Consts;
|
||||||
using Yi.Framework.DigitalCollectibles.Domain.Shared.Enums;
|
using Yi.Framework.DigitalCollectibles.Domain.Shared.Enums;
|
||||||
using Yi.Framework.DigitalCollectibles.Domain.Shared.Etos;
|
using Yi.Framework.DigitalCollectibles.Domain.Shared.Etos;
|
||||||
@@ -25,6 +27,8 @@ public class WeChatMiniProgramAccountService : ApplicationService
|
|||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly IAccountService _accountService;
|
private readonly IAccountService _accountService;
|
||||||
private readonly ILocalEventBus _localEventBus;
|
private readonly ILocalEventBus _localEventBus;
|
||||||
|
private readonly IDistributedCache<WeChatNoticeCacheItem> _noticeCache;
|
||||||
|
|
||||||
public WeChatMiniProgramAccountService(IWeChatMiniProgramManager weChatMiniProgramManager, IAuthService authService,
|
public WeChatMiniProgramAccountService(IWeChatMiniProgramManager weChatMiniProgramManager, IAuthService authService,
|
||||||
IAccountService accountService, ILocalEventBus localEventBus)
|
IAccountService accountService, ILocalEventBus localEventBus)
|
||||||
{
|
{
|
||||||
@@ -34,6 +38,32 @@ public class WeChatMiniProgramAccountService : ApplicationService
|
|||||||
_localEventBus = localEventBus;
|
_localEventBus = localEventBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置用户一次性订阅状态
|
||||||
|
/// </summary>
|
||||||
|
[HttpPut("wechat/mini-program/notice/subscribe")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task PutSubscribeNoticeStateAsync()
|
||||||
|
{
|
||||||
|
var userId = CurrentUser.GetId();
|
||||||
|
await _noticeCache.SetAsync($"MiniProgram:notice:{userId}", new WeChatNoticeCacheItem(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("wechat/mini-program/notice")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<bool> GetSubscribeNoticeStateAsync()
|
||||||
|
{
|
||||||
|
var userId = CurrentUser.GetId();
|
||||||
|
var notice = await _noticeCache.GetAsync($"MiniProgram:notice:{userId}");
|
||||||
|
if (notice is not null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 使用小程序jsCode登录意社区账号
|
/// 使用小程序jsCode登录意社区账号
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -88,14 +118,15 @@ public class WeChatMiniProgramAccountService : ApplicationService
|
|||||||
var openId = (await _weChatMiniProgramManager.Code2SessionAsync(new Code2SessionInput(input.JsCode))).openid;
|
var openId = (await _weChatMiniProgramManager.Code2SessionAsync(new Code2SessionInput(input.JsCode))).openid;
|
||||||
|
|
||||||
//是否已经授权过绑定过auth
|
//是否已经授权过绑定过auth
|
||||||
bool isAuthed =true;
|
bool isAuthed = true;
|
||||||
//如果openId没有绑定过,代表第一次进入,否则就是临时账号进行绑定
|
//如果openId没有绑定过,代表第一次进入,否则就是临时账号进行绑定
|
||||||
var authInfo= await _authService.TryGetAuthInfoAsync(openId,AuthTypeConst.WeChatMiniProgram);
|
var authInfo = await _authService.TryGetAuthInfoAsync(openId, AuthTypeConst.WeChatMiniProgram);
|
||||||
//从来没绑定过
|
//从来没绑定过
|
||||||
if (authInfo is null)
|
if (authInfo is null)
|
||||||
{
|
{
|
||||||
isAuthed = false;
|
isAuthed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//账号绑定,不管什么情况,都将jscode与phone用户建立关系即可
|
//账号绑定,不管什么情况,都将jscode与phone用户建立关系即可
|
||||||
await PostBindToAuthAsync(userInfo.User.Id, openId, userInfo.User.UserName);
|
await PostBindToAuthAsync(userInfo.User.Id, openId, userInfo.User.UserName);
|
||||||
|
|
||||||
@@ -109,11 +140,9 @@ public class WeChatMiniProgramAccountService : ApplicationService
|
|||||||
await _localEventBus.PublishAsync(new BindAccountEto
|
await _localEventBus.PublishAsync(new BindAccountEto
|
||||||
{
|
{
|
||||||
NewUserId = userInfo.User.Id,
|
NewUserId = userInfo.User.Id,
|
||||||
OldUserId =authInfo.UserId
|
OldUserId = authInfo.UserId
|
||||||
},false);
|
}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PostBindToAuthAsync(Guid userId, string openId, string? name = null)
|
private async Task PostBindToAuthAsync(Guid userId, string openId, string? name = null)
|
||||||
@@ -142,7 +171,7 @@ public class WeChatMiniProgramAccountService : ApplicationService
|
|||||||
var userName = GenerateRandomString(6);
|
var userName = GenerateRandomString(6);
|
||||||
await _accountService.PostTempRegisterAsync(new RegisterDto
|
await _accountService.PostTempRegisterAsync(new RegisterDto
|
||||||
{
|
{
|
||||||
UserName =$"ls_{userName}",
|
UserName = $"ls_{userName}",
|
||||||
Password = GenerateRandomString(20),
|
Password = GenerateRandomString(20),
|
||||||
Nick = $"临时账号-{userName}"
|
Nick = $"临时账号-{userName}"
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using Volo.Abp.DependencyInjection;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Volo.Abp.Caching;
|
||||||
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Domain.Services;
|
using Volo.Abp.Domain.Services;
|
||||||
using Volo.Abp.EventBus;
|
using Volo.Abp.EventBus;
|
||||||
|
using Yi.Framework.DigitalCollectibles.Domain.Shared.Caches;
|
||||||
using Yi.Framework.DigitalCollectibles.Domain.Shared.Consts;
|
using Yi.Framework.DigitalCollectibles.Domain.Shared.Consts;
|
||||||
using Yi.Framework.DigitalCollectibles.Domain.Shared.Etos;
|
using Yi.Framework.DigitalCollectibles.Domain.Shared.Etos;
|
||||||
using Yi.Framework.Rbac.Application.Contracts.IServices;
|
using Yi.Framework.Rbac.Application.Contracts.IServices;
|
||||||
@@ -13,18 +16,30 @@ public class WeChatMiniProgramNoticeEventHandler : ILocalEventHandler<WeChatMini
|
|||||||
{
|
{
|
||||||
private readonly IWeChatMiniProgramManager _weChatMiniProgramManager;
|
private readonly IWeChatMiniProgramManager _weChatMiniProgramManager;
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
|
private readonly ILogger<WeChatMiniProgramNoticeEventHandler> _logger;
|
||||||
|
private readonly IDistributedCache<WeChatNoticeCacheItem> _noticeCache;
|
||||||
public WeChatMiniProgramNoticeEventHandler(IWeChatMiniProgramManager weChatMiniProgramManager,
|
public WeChatMiniProgramNoticeEventHandler(IWeChatMiniProgramManager weChatMiniProgramManager,
|
||||||
IAuthService authService)
|
IAuthService authService, ILogger<WeChatMiniProgramNoticeEventHandler> logger, IDistributedCache<WeChatNoticeCacheItem> noticeCache)
|
||||||
{
|
{
|
||||||
_weChatMiniProgramManager = weChatMiniProgramManager;
|
_weChatMiniProgramManager = weChatMiniProgramManager;
|
||||||
_authService = authService;
|
_authService = authService;
|
||||||
|
_logger = logger;
|
||||||
|
_noticeCache = noticeCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleEventAsync(WeChatMiniProgramNoticeEto eventData)
|
public async Task HandleEventAsync(WeChatMiniProgramNoticeEto eventData)
|
||||||
|
{
|
||||||
|
//需要判断该用户是否已经订阅
|
||||||
|
var noticeCache= await _noticeCache.GetAsync($"MiniProgram:notice:{eventData.UserId}");
|
||||||
|
//判断用户是否点击了一次性消息订阅
|
||||||
|
if (noticeCache is not null)
|
||||||
{
|
{
|
||||||
var authInfo = await _authService.TryGetAuthInfoAsync(null, AuthTypeConst.WeChatMiniProgram, eventData.UserId);
|
var authInfo = await _authService.TryGetAuthInfoAsync(null, AuthTypeConst.WeChatMiniProgram, eventData.UserId);
|
||||||
await SendAsync(authInfo.OpenId, eventData.Title);
|
await SendAsync(authInfo.OpenId, eventData.Title);
|
||||||
|
//发送完成之后,删除缓存
|
||||||
|
await _noticeCache.RemoveAsync($"MiniProgram:notice:{eventData.UserId}");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -32,6 +47,8 @@ public class WeChatMiniProgramNoticeEventHandler : ILocalEventHandler<WeChatMini
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="userId"></param>
|
/// <param name="userId"></param>
|
||||||
public async Task SendAsync(string openId, string title)
|
public async Task SendAsync(string openId, string title)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
//成功挖到矿,可以发消息给用户了
|
//成功挖到矿,可以发消息给用户了
|
||||||
await _weChatMiniProgramManager.SendSubscribeNoticeAsync(new SubscribeNoticeInput
|
await _weChatMiniProgramManager.SendSubscribeNoticeAsync(new SubscribeNoticeInput
|
||||||
@@ -53,4 +70,10 @@ public class WeChatMiniProgramNoticeEventHandler : ILocalEventHandler<WeChatMini
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError($"微信通知提醒失败,错误信息:{e.Message},堆栈:{e.InnerException}");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace Yi.Framework.DigitalCollectibles.Domain.Shared.Caches;
|
||||||
|
|
||||||
|
public class WeChatNoticeCacheItem
|
||||||
|
{
|
||||||
|
public WeChatNoticeCacheItem(bool isSubscribe)
|
||||||
|
{
|
||||||
|
IsSubscribe = isSubscribe;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSubscribe { get; set; }
|
||||||
|
}
|
||||||
@@ -15,12 +15,14 @@
|
|||||||
style="color: red;font-weight: bolder;font-size: large;">开始</el-menu-item>
|
style="color: red;font-weight: bolder;font-size: large;">开始</el-menu-item>
|
||||||
<el-menu-item index="3" @click="enterWatermelon"
|
<el-menu-item index="3" @click="enterWatermelon"
|
||||||
>数字藏品</el-menu-item>
|
>数字藏品</el-menu-item>
|
||||||
<el-sub-menu index="4">
|
<el-menu-item index="4" @click="enterShop"
|
||||||
<template #title>学习</template>
|
>商城</el-menu-item>
|
||||||
<el-menu-item index="3-1">前端</el-menu-item>
|
<!-- <el-sub-menu index="4">-->
|
||||||
<el-menu-item index="3-2">后端</el-menu-item>
|
<!-- <template #title>学习</template>-->
|
||||||
<el-menu-item index="3-3">运维</el-menu-item>
|
<!-- <el-menu-item index="3-1">前端</el-menu-item>-->
|
||||||
</el-sub-menu>
|
<!-- <el-menu-item index="3-2">后端</el-menu-item>-->
|
||||||
|
<!-- <el-menu-item index="3-3">运维</el-menu-item>-->
|
||||||
|
<!-- </el-sub-menu>-->
|
||||||
|
|
||||||
<!-- <el-sub-menu index="5">-->
|
<!-- <el-sub-menu index="5">-->
|
||||||
<!-- <template #title>问答</template>-->
|
<!-- <template #title>问答</template>-->
|
||||||
@@ -232,7 +234,10 @@ const enterStart = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const enterWatermelon=()=>{
|
const enterWatermelon=()=>{
|
||||||
alert("即将发布,敬请期待~")
|
alert("真即将发布,敬请期待~")
|
||||||
|
}
|
||||||
|
const enterShop=()=>{
|
||||||
|
alert("真即将发布,敬请期待~")
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {computed, ref, watch} from "vue";
|
import {computed, ref, watch} from "vue";
|
||||||
|
import { dayjs } from 'element-plus'
|
||||||
const data=ref({});
|
const data=ref({});
|
||||||
const realData=ref({});
|
const realData=ref({});
|
||||||
const props = defineProps([
|
const props = defineProps([
|
||||||
@@ -37,18 +38,21 @@ const isConformToRule=computed(()=>{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<template>
|
<template >
|
||||||
|
<div class="shop-card" >
|
||||||
<el-card shadow="hover">
|
<el-card shadow="hover">
|
||||||
<template #header>{{data.name}}</template>
|
<template #header>{{data.name}}</template>
|
||||||
<img
|
<img
|
||||||
:src="data.imageUrl"
|
:src="data.imageUrl"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
alt=""/>
|
alt=""/>
|
||||||
简介:{{data.describe}}
|
|
||||||
<ul>
|
<ul>
|
||||||
|
<li style="font-size: smaller;color: #7c8188;margin-bottom: 5px">简介:{{data.describe}}</li>
|
||||||
<li :class="{'less-li': realData.money<data.needMoney}">所需钱钱:{{data.needMoney}}</li>
|
<li :class="{'less-li': realData.money<data.needMoney}">所需钱钱:{{data.needMoney}}</li>
|
||||||
<li :class="{'less-li': realData.value<data.needValue}">所需价值:{{data.needValue}}</li>
|
<li :class="{'less-li': realData.value<data.needValue}">所需价值:{{data.needValue}}</li>
|
||||||
<li :class="{'less-li': realData.points<data.needPoints}">所需积分:{{data.needPoints}}</li>
|
<li :class="{'less-li': realData.points<data.needPoints}">所需积分:{{data.needPoints}}</li>
|
||||||
|
<li >到期时间:{{dayjs(data.endTime).format("YYYY/MM/DD")}}</li>
|
||||||
<li>限购数量:{{data.limitNumber}}</li>
|
<li>限购数量:{{data.limitNumber}}</li>
|
||||||
<li>剩余:{{data.stockNumber}}</li>
|
<li>剩余:{{data.stockNumber}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -59,6 +63,7 @@ const isConformToRule=computed(()=>{
|
|||||||
<el-button v-else :disabled="data.isLimit" type="success" @click="clickBuy">{{data.isLimit===true?"已申请":"申请购买"}} </el-button>
|
<el-button v-else :disabled="data.isLimit" type="success" @click="clickBuy">{{data.isLimit===true?"已申请":"申请购买"}} </el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -66,9 +71,35 @@ const isConformToRule=computed(()=>{
|
|||||||
{
|
{
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-top: 20px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
.less-li{
|
.less-li{
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
img{
|
||||||
|
height: 100px;
|
||||||
|
width: 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
.shop-card :deep(.el-card__body) {
|
||||||
|
padding: 0 2px 0 0!important;
|
||||||
|
}
|
||||||
|
.shop-card :deep(.el-card__header)
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 5px;
|
||||||
|
//color: red;
|
||||||
|
font-weight: bolder;
|
||||||
|
|
||||||
|
}
|
||||||
|
ul{
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.shop-card :deep(.el-divider)
|
||||||
|
{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user