Merge pull request #17 from yuhan-feng/hxy-dev

小H。share key
pull/361/head
overBaker 2 years ago committed by GitHub
commit 345f7d50e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,70 @@
package v1
import (
"github.com/alimy/mir/v4"
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/model/web"
"net/http"
)
type KeyQuery interface {
_default_
Chain() gin.HandlersChain
GetKeyDetail(*web.GetUserKeysReq) (*web.GetUserKeysResp, mir.Error)
mustEmbedUnimplementedShareKeyServant()
DeleteKeyDetail(req *web.DeleteKeyReq) (*web.DeleteKeyResp, mir.Error)
}
// RegisterKeyQueryServant 使用路由组的方式注册路由 /v1/key
func RegisterKeyQueryServant(e *gin.Engine, s KeyQuery) {
router := e.Group("v1")
// use chain for router
middlewares := s.Chain()
router.Use(middlewares...)
// 注册路由
router.Handle("GET", "/user/keys", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.GetUserKeysReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.GetKeyDetail(req)
s.Render(c, resp, err)
})
//逻辑删除将status置为1
router.Handle("POST", "/user/keys", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.DeleteKeyReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.DeleteKeyDetail(req)
s.Render(c, resp, err)
})
}
type UnimplementedShareKeyServant struct{}
// GetKeyDetail 用于提供接口的默认行为
func (UnimplementedShareKeyServant) GetKeyDetail(req *web.GetUserKeysReq) (*web.GetUserKeysResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedShareKeyServant) mustEmbedUnimplementedShareKeyServant() {}

@ -1158,7 +1158,7 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartwalle/alipay/v3 v3.2.15 h1:3fvFJnINKKAOXHR/Iv20k1Z7KJ+nOh3oK214lELPqG8=
github.com/smartwalle/alipay/v3 v3.2.15/go.mod h1:niTNB609KyUYuAx9Bex/MawEjv2yPx4XOjxSAkqmGjE=
gitub.com/smartwalle/alipay/v3 v3.2.15/go.mod h1:niTNB609KyUYuAx9Bex/MawEjv2yPx4XOjxSAkqmGjE=
github.com/smartwalle/ncrypto v1.0.2 h1:pTAhCqtPCMhpOwFXX+EcMdR6PNzruBNoGQrN2S1GbGI=
github.com/smartwalle/ncrypto v1.0.2/go.mod h1:Dwlp6sfeNaPMnOxMNayMTacvC5JGEVln3CVdiVDgbBk=
github.com/smartwalle/ngx v1.0.6 h1:JPNqNOIj+2nxxFtrSkJO+vKJfeNUSEQueck/Wworjps=

@ -34,4 +34,7 @@ type DataService interface {
// 安全服务
SecurityService
AttachmentCheckService
// share_key服务
ShareKeyService
}

@ -0,0 +1,14 @@
package core
import "github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
type (
ShareKey = dbr.ShareKey
DeleteKeySuccessOrFail = dbr.DeleteKeySuccessOrFail
)
// share_key服务
type ShareKeyService interface {
GetUserKeys(UserName string) ([]*ShareKey, error)
DeleteUserKey(UserName string, key string) (DeleteKeySuccessOrFail, error)
}

@ -0,0 +1,39 @@
package dbr
import "gorm.io/gorm"
type ShareKey struct {
UserName string `json:"user_name"`
ShareKey string `json:"share_key"`
Name string `json:"name"`
Description string `json:"description"`
Status int `json:"status"`
}
func (s *ShareKey) Get(db *gorm.DB) ([]ShareKey, error) {
var sks []ShareKey
if s.UserName != "" {
db = db.Where("user_name = ? AND status = 0", s.UserName)
}
//if s.ShareKey != "" {
// db = db.Where("share_key = ?", s.ShareKey)
//}
err := db.Table("p_share_key").Find(&sks).Error
if err != nil {
return sks, err
}
return sks, nil
}
type DeleteKeySuccessOrFail struct {
SuccessOrFail bool
}
func (sf *ShareKey) POST(db *gorm.DB) (DeleteKeySuccessOrFail, error) {
var sof DeleteKeySuccessOrFail
return sof, nil
}

@ -37,6 +37,7 @@ type dataServant struct {
core.ContactManageService
core.SecurityService
core.AttachmentCheckService
core.ShareKeyService
}
func NewDataService() (core.DataService, core.VersionInfo) {
@ -97,6 +98,7 @@ func NewDataService() (core.DataService, core.VersionInfo) {
ContactManageService: newContactManageService(db),
SecurityService: newSecurityService(db, pvs),
AttachmentCheckService: security.NewAttachmentCheckService(),
ShareKeyService: NewShareKeyService(db),
}
return ds, ds
}

@ -0,0 +1,38 @@
package jinzhu
import (
"github.com/rocboss/paopao-ce/internal/core"
"gorm.io/gorm"
)
type ShareKeyService struct {
db *gorm.DB
}
func (s ShareKeyService) DeleteUserKey(UserName string, key string) (core.DeleteKeySuccessOrFail, error) {
var deleteKey core.DeleteKeySuccessOrFail
deleteKey.SuccessOrFail = false
//根据username和key删除相应的key
err := s.db.Table("p_share_key").Where("user_name = ? AND share_key = ?", UserName, key).Update("status", 1).Error
if err != nil {
return deleteKey, err
}
deleteKey.SuccessOrFail = true
return deleteKey, nil
}
func (s ShareKeyService) GetUserKeys(UserName string) ([]*core.ShareKey, error) {
//根据username查询相应的keys
var keys []*core.ShareKey
err := s.db.Table("p_share_key").Where("user_name = ? and status = 0", UserName).Find(&keys).Error
if err != nil {
return nil, err
}
return keys, nil
}
func NewShareKeyService(db *gorm.DB) *ShareKeyService {
return &ShareKeyService{
db: db,
}
}

@ -35,6 +35,8 @@ func (d *walletServant) GetRechargeByID(id int64) (*core.WalletRecharge, error)
return recharge.Get(d.db)
}
// 点击前往支付会创建支付数据
func (d *walletServant) CreateRecharge(userId, amount int64) (*core.WalletRecharge, error) {
recharge := &dbr.WalletRecharge{
UserID: userId,

@ -0,0 +1,57 @@
package web
import (
"github.com/alimy/mir/v4"
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/pkg/convert"
"github.com/rocboss/paopao-ce/pkg/xerror"
)
// GetUserKeysReq 获取一个用户的所有key
type GetUserKeysReq struct {
UserId int64
UserName string
}
func (g *GetUserKeysReq) Bind(c *gin.Context) mir.Error {
uid, ok := base.UserFrom(c)
if !ok {
return xerror.UnauthorizedTokenError
}
g.UserId = uid.ID
g.UserName = uid.Username
return nil
}
type KeyInfo struct {
ShareKey string `json:"share_key"`
Name string `json:"name"`
Description string `json:"description"`
}
type GetUserKeysResp struct {
ShareKeys []KeyInfo `json:"shareKeys"`
}
// 逻辑删除服务
type DeleteKeyReq struct {
ShareKey string `json:"share_key"`
UserId int64
UserName string
}
func (d *DeleteKeyReq) Bind(c *gin.Context) mir.Error {
uid, ok := base.UserFrom(c)
if !ok {
return xerror.UnauthorizedTokenError
}
d.UserId = uid.ID
d.UserName = uid.Username
d.ShareKey = convert.StrTo(c.Query("share_key")).String()
return nil
}
type DeleteKeyResp struct {
Status string
}

@ -90,4 +90,8 @@ var (
ErrFileUploadFailed = xerror.NewError(10200, "文件上传失败")
ErrFileInvalidExt = xerror.NewError(10201, "文件类型不合法")
ErrFileInvalidSize = xerror.NewError(10202, "文件大小超限")
ErrGetUserKeysFailed = xerror.NewError(11001, "获取用户Share Key失败")
ErrDsNil = xerror.NewError(11002, "数据源为空")
ErrUserNameEmpty = xerror.NewError(11003, "用户名为空")
ErrDeleteUserKeyFailed = xerror.NewError(11004, "删除用户Share Key失败")
)

@ -75,7 +75,7 @@ func (s *alipayPrivSrv) UserWalletBills(req *web.UserWalletBillsReq) (*web.UserW
}
func (s *alipayPrivSrv) UserRechargeLink(req *web.UserRechargeLinkReq) (*web.UserRechargeLinkResp, mir.Error) {
recharge, err := s.Ds.CreateRecharge(req.User.ID, req.Amount) //天数换算成时间戳
recharge, err := s.Ds.CreateRecharge(req.User.ID, req.Amount) //天数换算成时间戳将数据存入p_wallet_recharge表
if err != nil {
logrus.Errorf("Ds.CreateRecharge err: %v", err)
return nil, web.ErrRechargeReqFail
@ -95,7 +95,9 @@ func (s *alipayPrivSrv) UserRechargeLink(req *web.UserRechargeLinkReq) (*web.Use
p.OutTradeNo = fmt.Sprintf("%d", recharge.ID)
p.Subject = "PaoPao用户钱包充值"
p.TotalAmount = fmt.Sprintf("%.2f", float64(recharge.Amount)/100.0*a)
p.NotifyURL = "https://" + req.Host + "/v1/alipay/notify"
//p.NotifyURL = "http://" + req.Host + "/v1/alipay/notify"
//支付宝回调,如果是内网环境的话,需要增加内网穿透
p.NotifyURL = "http://5tc4bf.natappfree.cc/v1/alipay/notify"
rsp, err := s.alipayClient.TradePreCreate(p)
if err != nil {
logrus.Errorf("client.TradePreCreate err: %v\n", err)

@ -0,0 +1,99 @@
package web
import (
"fmt"
"github.com/alimy/mir/v4"
"github.com/gin-gonic/gin"
api "github.com/rocboss/paopao-ce/auto/api/v1"
"github.com/rocboss/paopao-ce/internal/model/web"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/internal/servants/chain"
"github.com/sirupsen/logrus"
)
var (
_ api.KeyQuery = (*shareKey)(nil)
)
type shareKey struct {
api.UnimplementedShareKeyServant
*base.DaoServant
}
func (s *shareKey) Chain() gin.HandlersChain {
return gin.HandlersChain{chain.JWT()}
}
func (s *shareKey) GetKeyDetail(req *web.GetUserKeysReq) (*web.GetUserKeysResp, mir.Error) {
//判断GetUserKeys是否为nil
if s.Ds == nil {
logrus.Errorf("GetKeyDetail err: %s", web.ErrDsNil)
return nil, web.ErrDsNil
} else {
logrus.Info("GetKeyDetail success")
}
if req.UserName == "" {
logrus.Errorf("GetKeyDetail err: %s", web.ErrUserNameEmpty)
return nil, web.ErrUserNameEmpty
}
// 调用数据源的方法查询用户的所有key信息
keys, err := s.Ds.GetUserKeys(req.UserName)
if err != nil {
logrus.Errorf("GetUserKeys err: %s", err)
return nil, web.ErrGetUserKeysFailed
}
// 将查询到的key信息转换为KeyInfo结构体
var keyInfos []web.KeyInfo
for _, key := range keys {
keyInfos = append(keyInfos, web.KeyInfo{
ShareKey: key.ShareKey,
Name: key.Name,
Description: key.Description,
})
}
// 构建返回结果并返回
resp := &web.GetUserKeysResp{
ShareKeys: keyInfos,
}
return resp, nil
}
func (s *shareKey) DeleteKeyDetail(req *web.DeleteKeyReq) (*web.DeleteKeyResp, mir.Error) {
//判断GetUserKeys是否为nil
if s.Ds == nil {
logrus.Errorf("GetKeyDetail err: %s", web.ErrDsNil)
return nil, web.ErrDsNil
} else {
logrus.Info("GetKeyDetail success")
}
if req.UserName == "" {
logrus.Errorf("GetKeyDetail err: %s", web.ErrUserNameEmpty)
return nil, web.ErrUserNameEmpty
}
fmt.Print(req.UserName, req.ShareKey)
sof, err := s.Ds.DeleteUserKey(req.UserName, req.ShareKey)
if err != nil {
logrus.Errorf("DeleteUserKey err: %s", err)
return nil, web.ErrDeleteUserKeyFailed
}
if sof.SuccessOrFail == false {
logrus.Errorf("DeleteUserKey err: %s", err)
return nil, web.ErrDeleteUserKeyFailed
}
// 构建返回结果并返回
resp := &web.DeleteKeyResp{
Status: "DELETE_SUCCESS",
}
return resp, nil
}
// NewShareKeyServant 创建share_key服务
func NewShareKeyServant(s *base.DaoServant) api.KeyQuery {
return &shareKey{
DaoServant: s,
}
}

@ -32,6 +32,7 @@ func RouteWeb(e *gin.Engine) {
api.RegisterLooseServant(e, newLooseSrv(ds))
api.RegisterPrivServant(e, newPrivSrv(ds, oss))
api.RegisterPubServant(e, newPubSrv(ds))
api.RegisterKeyQueryServant(e, NewShareKeyServant(ds))
// regster servants if needed by configure
cfg.In(cfg.Actions{
"Alipay": func() {

@ -0,0 +1,32 @@
import { request } from "@/utils/request";
//获取用户的sharekey
// export const getShareKeys = (
// params: NetParams.UserGetShareKeys
// ): Promise<NetReq.UserGetShareKeys> => {
// return request({
// method: "get",
// url: "/v1/user/keys",
// params,
// });
// }
export const getShareKeys = (token: NetParams.AuthUserInfo = ""): Promise<NetReq.UserGetShareKeys> => {
return request({
method: "get",
url: "/v1/user/keys",
headers: {
Authorization: `Bearer ${token}`,
},
});
}
export const deleteThisShareKey = (params: NetParams.UserDeleteShareKey,token: NetParams.AuthUserInfo = ""): Promise<NetReq.UserDeleteShareKey> => {
return request({
method: "post",
url: "/v1/user/keys",
headers: {
Authorization: `Bearer ${token}`,
},
params,
});
}

@ -181,6 +181,13 @@ const menuOptions = computed(() => {
href: '/setting',
});
options.push({
label: '',
key: 'sharekey',
icon: () => h(SettingsOutline),
href: '/sharekey',
});
return store.state.userInfo.id > 0
? options
: [

@ -90,6 +90,14 @@ const routes = [
},
component: () => import('@/views/Setting.vue'),
},
{
path: '/sharekey',
name: 'sharekey',
meta: {
title: '密钥',
},
component: () => import('@/views/ShareKey.vue'),
},
{
path: '/404',
name: '404',

@ -325,4 +325,13 @@ declare module Item {
change_amount: number;
created_on: number;
}
//sharekey参数
interface ShareKeyProps {
share_key: string;
//名称
name: string;
//描述
description: string;
}
}

@ -58,6 +58,19 @@ declare module NetParams {
page_size: number;
}
//获取用户分享码请求参数
interface UserGetShareKeys {
userId : number;
userName : string;
// page: number;
// page_size: number;
}
//删除用户分享码请求参数
interface UserDeleteShareKey {
share_key : string;
}
interface UserStatusReq {
id: number;
status: number;

@ -63,6 +63,18 @@ declare module NetReq {
pager: Item.PagerProps;
}
//用户获取分享码响应参数
interface UserGetShareKeys {
shareKeys: Item.ShareKeyProps[];
/** 页码信息 */
pager: Item.PagerProps;
}
//用户删除分享码响应参数
interface UserDeleteShareKey {
Status: string;
}
interface UserReqRecharge {
id: number;
pay: string;

@ -0,0 +1,202 @@
<template>
<div>
<!-- <main-nav title="我的分享码" /> -->
<n-list class="main-content-wrap" bordered>
<div class="balance-wrap">
<div class="balance-line">
<n-statistic style="color:black; font-weight:bold;"></n-statistic>
</div>
</div>
<div v-if="loading" class="skeleton-wrap">
<post-skeleton :num="pageSize" />
</div>
<div v-else>
<div class="empty-wrap" v-if="shareKeys.length === 0">
<n-empty size="large" description="暂无数据" />
</div>
<n-list-item v-for="sharekey in shareKeys" :key="sharekey.share_key">
<div class="bill-line">
<div>{{ sharekey.share_key }}</div>
<div>{{ sharekey.name }}</div>
<div>{{ sharekey.description }}</div>
<n-button
size="small"
secondary
type="primary"
@click="isDelete(sharekey)"
>
</n-button>
</div>
</n-list-item>
</div>
</n-list>
<n-modal v-model:show="showRecharge">
<n-card
:bordered="false"
title="删除"
role="dialog"
aria-modal="true"
style="width: 100%; max-width: 330px"
>
<div class="amount-options" v-if="rechargeQrcode.length === 0">
<div v-if="selectedShareKey !== null" class="amount-options">
<n-space align="baseline">
<div>{{ selectedShareKey.share_key }}</div>
</n-space>
</div>
</div>
<div v-if="selectedShareKey !== null"
style="margin-top: 20px"
>
<n-button
style="width: 30%;left: 10%;"
type="primary"
@click="deleteShareKey(selectedShareKey.share_key)"
>
</n-button>
<n-button
style="width: 30% ; left: 30%"
@click="showRecharge = false"
>
</n-button>
</div>
</n-card>
</n-modal>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useStore } from 'vuex';
import { useRoute } from 'vue-router';
import { getShareKeys, deleteThisShareKey } from '@/api/shareKey';
const route = useRoute();
const showRecharge = ref(false);
const rechargeQrcode = ref('');
const loading = ref(false);
// 获取 ShareKey
const shareKeys = ref<Item.ShareKeyProps[]>([]);
const selectedShareKey = ref<Item.ShareKeyProps | null>(null);
const page = ref(+(route.query.p as string) || 1);
const pageSize = ref(20);
const totalPage = ref(0);
const loadKeys = () => {
loading.value = true;
const token = localStorage.getItem('PAOPAO_TOKEN') || '';
if(token) {
getShareKeys(token)
.then((rsp) => {
loading.value = false;
shareKeys.value = rsp.shareKeys;
window.scrollTo(0, 0);
})
.catch((err) => {
loading.value = false;
});
}
};
const isDelete = (sharekey: Item.ShareKeyProps) => {
selectedShareKey.value = sharekey;
showRecharge.value = true;
};
const deleteShareKey = (keyToDelete: string) => {
const params: NetParams.UserDeleteShareKey = {
share_key: keyToDelete,
}
const token = localStorage.getItem('PAOPAO_TOKEN') || '';
if(token) {
deleteThisShareKey(params, token)
.then((rsp) => {
if(rsp.Status === "DELETE_SUCCESS") {
window.$message.success('');
showRecharge.value = false;
loadKeys();
}
loading.value = false;
window.scrollTo(0, 0);
})
.catch((err) => {
loading.value = false;
});
}
showRecharge.value = false;
};
// const updatePage = (p: number) => {
// page.value = p;
// loadPosts();
// };
onMounted(() => {
loadKeys();
});
</script>
<style lang="less" scoped>
.balance-wrap {
padding: 16px;
.balance-line {
display: flex;
justify-content: space-between;
.balance-opts {
display: flex;
flex-direction: column;
}
}
}
.bill-line {
padding: 16px;
display: flex;
justify-content: space-between;
.income,
.out {
font-weight: bold;
}
.income {
color: #18a058;
}
}
.pagination-wrap {
padding: 10px;
display: flex;
justify-content: center;
overflow: hidden;
}
.qrcode-wrap {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.pay-tips {
margin-top: 16px;
}
.pay-sub-tips {
margin-top: 4px;
font-size: 12px;
opacity: 0.75;
display: flex;
align-items: center;
}
}
.dark {
.income {
color: #63e2b7;
}
.main-content-wrap {
background-color: rgba(16, 16, 20, 0.75);
}
}
</style>
Loading…
Cancel
Save