修复已知问题
新增好友代付功能
优化wap端分包方案
This commit is contained in:
wangchen147 2025-01-16 11:51:14 +08:00
parent 71d3055aeb
commit 305952d13b
134 changed files with 6504 additions and 0 deletions

View File

@ -0,0 +1,171 @@
import request from '@/utils/request'
/***************************************************** 充值订单 ****************************************************/
/**
*
* @param params
* @returns
*/
export function getRechargeOrderList(params: Record<string, any>) {
return request.get(`recharge/order`, { params })
}
/**
*
* @param params
* @returns
*/
export function getRechargeStat(params: Record<string, any>) {
return request.get(`recharge/order/stat`, { params })
}
/**
*
* @param order_id
* @returns
*/
export function getRechargeOrderInfo(order_id: number) {
return request.get(`recharge/order/${ order_id }`);
}
/**
*
* @returns
*/
export function getRechargeOrderStatusList() {
return request.get(`recharge/order/status`)
}
/**
* 退
* @returns
*/
export function getRechargeRefund(params: Record<string, any>) {
return request.get(`recharge/refund`, { params })
}
/**
* 退
* @returns
*/
export function getRechargeRefundStatus() {
return request.get(`recharge/refund/status`)
}
/**
* 退
* @returns
*/
export function rechargeRefund(order_id: number) {
return request.put(`recharge/refund/${ order_id }`, {}, { showSuccessMessage: true });
}
/**
* 退
* @returns
*/
export function getRechargeRefundStat() {
return request.get(`recharge/refund/stat`);
}
/**
*
* @param params
* @returns
*/
export function getRechargePackageList(params: Record<string, any>) {
return request.get(`recharge/package`, { params })
}
/**
*
* @returns
*/
export function getRechargePackageStatusList() {
return request.get(`recharge/package/status`)
}
/**
*
* @param params
* @returns
*/
export function addRechargePackage(params: Record<string, any>) {
return request.post('recharge/package', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editRechargePackage(params: Record<string, any>) {
return request.put(`recharge/package/${ params.recharge_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function getRechargePackageInfo(params: Record<string, any>) {
return request.get(`recharge/package/init`, { params });
}
/**
*
* @param recharge_id
* @returns
*/
export function deleteRechargePackage(recharge_id: number) {
return request.delete(`recharge/package/${ recharge_id }`, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function setRechargeConfig(params: Record<string, any>) {
return request.post(`recharge/package/config`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getRechargeConfig() {
return request.get(`recharge/package/config`)
}
/**
*
* @param params
* @returns
*/
export function editRechargeStatus(params: Record<string, any>) {
return request.put(`recharge/package/status`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function modifyRechargeSort(params: Record<string, any>) {
return request.put(`recharge/package/sort`, params, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getPackageGiftDict() {
return request.get(`recharge/dict/package_gift`)
}

View File

@ -0,0 +1,20 @@
{
"orderInfo": "订单详情",
"orderDiscountMoney": "优惠金额",
"ip": "下单IP",
"payTime": "支付时间",
"remark": "商家留言",
"memberMessage": "买家留言",
"orderNo": "订单编号",
"orderStatus": "订单状态",
"orderNoPlaceholder": "请输入订单编号",
"createTime": "创建时间",
"rechargeMoney": "充值金额",
"orderMoney": "订单金额",
"member": "买家",
"mobile": "手机号",
"orderFromName": "订单来源",
"payTypeName": "支付方式",
"startDate": "开始时间",
"endDate": "结束时间"
}

View File

@ -0,0 +1,32 @@
{
"totalRechargeRefundMoney": "充值退款(元)",
"totalRechargeMoney": "充值金额(元)",
"rechargeRefundMoney": "充值退款",
"rechargeNo": "充值单号",
"orderStatus": "订单状态",
"rechargeNoPlaceholder": "请输入充值单号",
"createTime": "充值时间",
"rechargeMoney": "充值金额",
"orderMoney": "订单金额",
"member": "会员信息",
"orderFromName": "订单来源",
"payTypeName": "支付方式",
"startDate": "开始时间",
"endDate": "结束时间",
"namePlaceholder": "请选择",
"refundBtn": "退款",
"refundContent": "是否确认退款",
"payTime": "支付时间",
"refundStatus": "退款状态",
"startMoney": "起始金额",
"endMoney": "结束金额",
"orderInfo": "订单详情",
"orderDiscountMoney": "优惠金额",
"ip": "下单IP",
"remark": "商家留言",
"memberMessage": "买家留言",
"orderNo": "订单编号",
"orderNoPlaceholder": "请输入订单编号",
"memberInfo": "买家",
"mobile": "手机号"
}

View File

@ -0,0 +1,24 @@
{
"refundNumber": "退款单号",
"userInfo": "用户信息",
"sourceNumber": "来源单号",
"refundAmount": "退款金额",
"refundTime": "退款时间",
"detail": "详情",
"statusName": "状态",
"memberInfo": "会员信息",
"refundSource": "退款来源",
"startDate": "开始时间",
"endDate": "结束时间",
"refundStatus": "退款状态",
"accumulateRefundMoney": "累计退款金额(元)",
"haveRefundMoney": "退款中金额(元)",
"refundSuccessMonry": "退款成功金额(元)",
"refundFailMoney": "退款失败金额(元)",
"memberInfoPlaceholder": "请输入会员编号/昵称/手机号",
"refundNumberPlaceholder": "请输入退款单号",
"orderNumber": "来源单号",
"orderNumberPlaceholder": "请输入来源单号",
"refundDetail": "退款详情",
"nickname": "会员昵称"
}

View File

@ -0,0 +1,16 @@
{
"enableRecharge": "充值开关",
"rechargeExplain": "充值说明",
"rechargeExplainPlaceholder": "请输入充值说明",
"min_price": "最低充值金额",
"limitTipsTwo": "最低充值金额不能小于0.01",
"limitTips": "格式不正确",
"minPricePlaceholder": "请输入最低充值金额",
"enableRechargeTips": "关闭后前台将隐藏充值入口",
"closeOrderInfo": "自动取消订单",
"closeOrderInfoLeft": "提交订单",
"closeOrderInfoRight": "分钟未付款",
"closeOrderInfoBottom": "订单取消时间必须在10-1440分钟之间",
"CloseLengthPlaceholder": "请输入订单取消时间",
"isClose": "自动取消订单"
}

View File

@ -0,0 +1,2 @@
{
}

View File

@ -0,0 +1,32 @@
{
"rechargeName": "套餐名称",
"addRecharge": "添加套餐",
"editRecharge": "编辑套餐",
"faceValue": "面值",
"price": "价格",
"point": "赠送积分",
"growth": "赠送成长值",
"coupon": "赠送优惠券",
"sort": "排序",
"status": "状态",
"couponPrice": "面值",
"namePlaceholder": "请输入套餐名称",
"faceValuePlaceholder": "请输入面值",
"pricePlaceholder": "请输入价格",
"pointPlaceholder": "请输入赠送积分",
"growthPlaceholder": "请输入赠送成长值",
"faceValueMustBeGreaterThanZero": "面值必须大于0",
"priceMustBeGreaterThanZero": "价格必须大于0",
"pointMustBeGreaterThanZero": "赠送积分必须大于0",
"growthMustBeGreaterThanZero": "赠送成长值必须大于0",
"limitTips": "格式不正确",
"name": "名称",
"type": "类型",
"useThreshold": "使用门槛",
"termOfValidity": "有效期",
"yuan": "元",
"giveNum": "赠送数量",
"giveNumPlaceholder": "请输入赠送数量",
"giveNumMustBeGreaterThanZero": "赠送数量必须大于0",
"giveCouponTips": "如果优惠券数量不足、限领数量已达上限或优惠券状态不是“进行中”,都将导致赠送失败。"
}

View File

@ -0,0 +1,31 @@
{
"rechargeName": "套餐名称",
"rechargeInfo": "套餐信息",
"giftPackInfo": "礼包信息",
"detail": "详情",
"close": "关闭",
"rechargeNamePlaceholder": "请输入套餐名称",
"createTime": "创建时间",
"addRecharge": "添加套餐",
"status": "状态",
"faceValue": "面值",
"couponPrice": "面值",
"price": "价格",
"point": "积分",
"growth": "成长值",
"saleNum": "购买数量",
"deleteTips": "确定要删除该充值套餐吗?",
"detailTitle": "套餐详情",
"basicInfo": "基本信息",
"rechargeRecord": "充值记录",
"sortTips": "排序号格式输入错误",
"coupon": "优惠券",
"sortRules": "按照从大到小排序",
"name": "优惠券名称",
"type": "类型",
"useThreshold": "使用门槛",
"termOfValidity": "有效期",
"startDate": "开始时间",
"endDate": "结束时间",
"num": "赠送数量"
}

View File

@ -0,0 +1,111 @@
<template>
<el-drawer v-model="showDialog" :title="popTitle" direction="rtl" :before-close="handleClose" class="member-detail-drawer">
<div class="main-container" v-loading="loading">
<el-form :model="formData" label-width="100px" ref="formRef" class="page-form" v-if="!loading" label-position="left">
<el-row class="row-bg px-[30px]" :gutter="20">
<el-col :span="8">
<el-form-item :label="t('orderNo')">
<div class="input-width">{{ formData.order_no }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('orderMoney')">
<div class="input-width">{{ formData.order_money }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('orderDiscountMoney')">
<div class="input-width">{{ formData.order_discount_money }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('memberInfo')">
<div class="input-width">
<span>{{ formData.member.nickname || '' }}</span>
</div>
</el-form-item>
</el-col>
<el-col :span="8" v-if="formData.member.mobile">
<el-form-item :label="t('mobile')">
<div class="input-width">{{ formData.member.mobile || '' }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('ip')">
<div class="input-width">{{ formData.ip }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('orderFromName')">
<div class="input-width">{{ formData.order_from_name }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('orderStatus')">
<div class="input-width">{{ formData.order_status_info.name }}</div>
</el-form-item>
</el-col>
<el-col :span="8" v-if="formData.pay_type_name">
<el-form-item :label="t('payTypeName')">
<div class="input-width">{{ formData.pay_type_name }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('createTime')">
<div class="input-width">{{ formData.create_time || '' }}</div>
</el-form-item>
</el-col>
<el-col :span="8" v-if="formData.pay_time">
<el-form-item :label="t('payTime')">
<div class="input-width">{{ formData.pay_time || '' }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('remark')" v-if="formData.remark">
<div class="input-width">{{ formData.remark || '' }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('memberMessage')" v-if="formData.member_message">
<div class="input-width">{{ formData.member_message || '' }}</div>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</el-drawer>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { t } from '@/lang'
import { getRechargeOrderInfo } from '@/addon/recharge/api/recharge'
const popTitle: string = '订单详情'
const loading = ref(false)
const formData: Record<string, any> | null = ref(null)
const showDialog = ref(false)
const handleClose = (done: () => void) => {
showDialog.value = false
}
const setFormData = async (orderId: number = 0) => {
loading.value = true
formData.value = null
await getRechargeOrderInfo(orderId).then(({ data }) => {
formData.value = data
})
loading.value = false
}
defineExpose({
showDialog,
setFormData
})
</script>
<style lang="scss">
.member-detail-drawer{
width: 1000px !important;
}
</style>

View File

@ -0,0 +1,111 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-page-header :content="t('orderInfo')" :icon="ArrowLeft" @back="back()" />
</el-card>
<el-form :model="formData" label-width="150px" ref="formRef" class="page-form" v-loading="loading">
<el-card class="box-card !border-none relative" shadow="never" v-if="formData">
<h3 class="panel-title">{{ t('orderInfo') }}</h3>
<el-form-item :label="t('orderNo')">
<div class="input-width">{{ formData.order_no }}</div>
</el-form-item>
<el-form-item :label="t('orderMoney')">
<div class="input-width">{{ formData.order_money }}</div>
</el-form-item>
<el-form-item :label="t('orderDiscountMoney')">
<div class="input-width">{{ formData.order_discount_money }}</div>
</el-form-item>
<el-form-item :label="t('member')">
<div class="input-width">
<span>{{ formData.member.nickname || '' }}</span>
</div>
</el-form-item>
<el-form-item :label="t('mobile')">
<div class="input-width">{{ formData.member.mobile || '' }}</div>
</el-form-item>
<el-form-item :label="t('ip')">
<div class="input-width">{{ formData.ip }}</div>
</el-form-item>
<el-form-item :label="t('orderFromName')">
<div class="input-width">{{ formData.order_from_name }}</div>
</el-form-item>
<el-form-item :label="t('orderStatus')">
<div class="input-width">{{ formData.order_status_info.name }}</div>
</el-form-item>
<el-form-item :label="t('payTypeName')">
<div class="input-width">{{ formData.pay_type_name }}</div>
</el-form-item>
<el-form-item :label="t('createTime')">
<div class="input-width">{{ formData.create_time || '' }}</div>
</el-form-item>
<el-form-item :label="t('payTime')">
<div class="input-width">{{ formData.pay_time || '' }}</div>
</el-form-item>
<el-form-item :label="t('remark')" v-if="formData.remark">
<div class="input-width">{{ formData.remark || '' }}</div>
</el-form-item>
<el-form-item :label="t('memberMessage')" v-if="formData.member_message">
<div class="input-width">{{ formData.member_message || '' }}</div>
</el-form-item>
</el-card>
</el-form>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { t } from '@/lang'
import { ArrowLeft } from "@element-plus/icons-vue";
import type { FormInstance } from 'element-plus'
import { getRechargeOrderInfo } from '@/addon/recharge/api/recharge'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const orderId: number = parseInt(route.query.order_id as string)
const loading = ref(true)
const formData: Record<string, any> | null = ref(null)
const setFormData = async (orderId: number = 0) => {
loading.value = true
formData.value = null
await getRechargeOrderInfo(orderId).then(({ data }) => {
formData.value = data
})
loading.value = false
}
if (orderId) setFormData(orderId)
else loading.value = false
const formRef = ref<FormInstance>()
/**
* 会员详情
*/
const toMember = (memberId: number) => {
router.push(`/member/detail?id=${memberId}`)
}
const back = () => {
router.push('/recharge/order/list')
};
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,299 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center mb-[5px]">
<span class="text-page-title">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none table-search-wra base-bg !px-[35px]" shadow="never">
<el-row class="flex">
<el-col :span="12" class="min-w-[100px]">
<el-statistic :value="rechargeStatistics.recharge_money ? Number.parseFloat(rechargeStatistics.recharge_money).toFixed(2) : '0.00'">
<template #title>
<div class="text-[14px] mb-[9px]">{{ t('totalRechargeMoney') }}</div>
</template>
</el-statistic>
</el-col>
<el-col :span="12" class="min-w-[100px]">
<el-statistic :value="rechargeStatistics.recharge_refund_money ? Number.parseFloat(rechargeStatistics.recharge_refund_money).toFixed(2) : '0.00'">
<template #title>
<div class="text-[14px] mb-[9px]">{{ t('totalRechargeRefundMoney') }}</div>
</template>
</el-statistic>
</el-col>
</el-row>
</el-card>
<el-card class="box-card !border-none mb-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="orderTableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('rechargeNo')" prop="order_no">
<el-input v-model="orderTableData.searchParam.order_no" :placeholder="t('rechargeNoPlaceholder')" />
</el-form-item>
<el-form-item :label="t('orderFromName')" prop="order_from">
<el-select v-model="orderTableData.searchParam.order_from" clearable class="input-width">
<el-option :label="t('selectPlaceholder')" value="" />
<el-option :label="item" :value="key" v-for="(item, key) in channelList" :key="key" />
</el-select>
</el-form-item>
<el-form-item :label="t('orderStatus')" prop="order_status">
<el-select v-model="orderTableData.searchParam.order_status" clearable class="input-width">
<el-option :label="t('selectPlaceholder')" value="" />
<el-option :label="item['name']" :value="item['status']" v-for="(item,index) in statusList" :key="index"/>
</el-select>
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker v-model="orderTableData.searchParam.create_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item :label="t('rechargeMoney')">
<div class="region-input">
<el-form-item prop="start_money">
<input type="text" :placeholder="t('startMoney')" v-model="orderTableData.searchParam.start_money">
</el-form-item>
<span class="separator">-</span>
<el-form-item prop="end_money">
<input type="text" :placeholder="t('endMoney')" v-model="orderTableData.searchParam.end_money">
</el-form-item>
</div>
</el-form-item>
<el-form-item :label="t('payTime')">
<el-date-picker v-model="orderTableData.searchParam.pay_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadOrderList()">{{ t('search') }}</el-button>
<el-button @click="searchFormRef?.resetFields()">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[16px]">
<el-table :data="orderTableData.data" size="large" v-loading="orderTableData.loading">
<template #empty>
<span>{{ !orderTableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column :show-overflow-tooltip="true" :label="t('member')" align="left" min-width="140">
<template #default="{ row }">
<div class="flex items-center cursor-pointer " @click="toMember(row.member.member_id)">
<img class="w-[50px] h-[50px] mr-[10px]" v-if="row.member.headimg" :src="img(row.member.headimg)" alt="">
<img class="w-[50px] h-[50px] rounded-full mr-[10px]" v-else src="@/app/assets/images/member_head.png" alt="">
<div class="flex flex flex-col">
<span>{{ row.member.nickname || '' }}</span>
<span>{{ row.member.mobile || '' }}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="order_no" :show-overflow-tooltip="true" :label="t('rechargeNo')" align="center" min-width="140" />
<el-table-column prop="order_money" :label="t('rechargeMoney')" align="center" min-width="140" />
<el-table-column prop="order_from_name" :label="t('orderFromName')" align="center" min-width="140" />
<el-table-column :label="t('orderStatus')" min-width="120" align="center">
<template #default="{ row }">
{{ row.order_status_info.name }}
</template>
</el-table-column>
<el-table-column prop="pay_type_name" :label="t('payTypeName')" align="center" min-width="140" />
<el-table-column :label="t('createTime')" min-width="180" align="center">
<template #default="{ row }">
{{ row.create_time || '' }}
</template>
</el-table-column>
<el-table-column :label="t('payTime')" min-width="180" align="center">
<template #default="{ row }">
{{ row.pay_time || '' }}
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" align="right" width="130">
<template #default="{ row }">
<el-button type="primary" link @click="infoEvent(row)">{{ t('info') }}</el-button>
<!-- <el-button-->
<!-- v-if="[1, 10].includes(row.order_status_info.status) && row.is_enable_refund && row.refund_status == 0"-->
<!-- type="primary" link @click="refundFn(row)">{{ t('refundBtn') }}-->
<!-- </el-button>-->
<template v-for="(item, index) in row.order_status_info.action" :key="index">
<el-button type="primary" link @click="orderEvent(row, item.class)">{{ item.name }}</el-button>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="orderTableData.page" v-model:page-size="orderTableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="orderTableData.total"
@size-change="loadOrderList()" @current-change="loadOrderList" />
</div>
</div>
</el-card>
<!-- 是否退款 -->
<el-dialog v-model="refundShowDialog" :title="t('refundBtn')" width="500px" :destroy-on-close="true">
<p>{{ t('refundContent') }}</p>
<template #footer>
<span class="dialog-footer">
<el-button @click="refundShowDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirmRefund" :loading="refundLoading">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
<recharge-detail ref="rechargeDetailDialog" />
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { getRechargeOrderStatusList, getRechargeOrderList, rechargeRefund, getRechargeStat } from '@/addon/recharge/api/recharge'
import { getChannelType } from '@/app/api/sys'
import { img } from '@/utils/common'
import { useRouter, useRoute } from 'vue-router'
import { AnyObject } from '@/types/global'
import type { FormInstance } from 'element-plus'
import rechargeDetail from '@/addon/recharge/views/order/components/recharge-detail.vue'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const memberId: number = parseInt(route.query.id as string || '0')
const channelList = ref([])
const rechargeDetailDialog: Record<string, any> | null = ref(null)
const setChannelList = async () => {
channelList.value = await (await getChannelType()).data
}
setChannelList()
const orderTableData = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
order_no: '',
order_status: '',
member_id: memberId,
create_time: [],
pay_time: [],
order_from: '',
start_money: '',
end_money: '',
recharge_id:route.query.recharge_id
}
})
interface RechargeType {
recharge_money?: string,
recharge_refund_money?: string
}
const rechargeStatistics = ref<RechargeType>({})
const checkRechargeInfo = () => {
getRechargeStat({
member_id: memberId
}).then(res => {
rechargeStatistics.value = res.data
})
}
checkRechargeInfo()
const statusList = ref([])
const searchFormRef = ref<FormInstance>()
const setCategoryList = async () => {
statusList.value = await (await getRechargeOrderStatusList()).data
}
setCategoryList()
const loadOrderList = (page: number = 1) => {
orderTableData.loading = true
orderTableData.page = page
getRechargeOrderList({
page: orderTableData.page,
limit: orderTableData.limit,
...orderTableData.searchParam
}).then(res => {
orderTableData.loading = false
orderTableData.data = res.data.data
orderTableData.total = res.data.total
}).catch(() => {
orderTableData.loading = false
})
}
loadOrderList()
/**
* 订单详情
* @param data
*/
const infoEvent = (data: any) => {
rechargeDetailDialog.value.setFormData(data.order_id)
rechargeDetailDialog.value.showDialog = true
// router.push(`/recharge/order/detail?order_id=${data.order_id}`)
}
/**
* 订单操作
*/
const orderEvent = (data: any, type: string) => {
}
/**
* 退款操作
*/
const refundShowDialog = ref(false)
let refundData: AnyObject | null = null
const refundLoading = ref(false)
const refundFn = (data: AnyObject) => {
refundShowDialog.value = true
refundData = data
}
/**
* 确认退款
*/
const confirmRefund = () => {
if (refundLoading.value) return
refundLoading.value = true
rechargeRefund(refundData?.order_id).then(res => {
refundShowDialog.value = false
refundLoading.value = false
loadOrderList()
}).catch(() => {
refundLoading.value = false
})
}
/**
* 会员详情
*/
const toMember = (memberId: number) => {
router.push(`/member/detail?id=${memberId}`)
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,249 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center mb-[5px]">
<span class="text-page-title">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none table-search-wra base-bg !px-[35px]" shadow="never">
<el-row class="flex">
<el-col :span="12">
<div class="statistic-card">
<el-statistic :value="refundStat.refund_all_money"></el-statistic>
<div class="statistic-footer">
<div class="footer-item text-[14px] text-[#666]">
<span>{{ t('accumulateRefundMoney') }}</span>
</div>
</div>
</div>
</el-col>
<el-col :span="12">
<div class="statistic-card">
<el-statistic :value="refundStat.refund_have_money"></el-statistic>
<div class="statistic-footer">
<div class="footer-item text-[14px] text-[#666]">
<span>{{ t('haveRefundMoney') }}</span>
</div>
</div>
</div>
</el-col>
</el-row>
</el-card>
<el-card class="box-card !border-none mb-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="refundTableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('memberInfo')" prop="keywords">
<el-input v-model="refundTableData.searchParam.keywords" class="w-[240px]" :placeholder="t('memberInfoPlaceholder')" />
</el-form-item>
<el-form-item :label="t('refundNumber')" prop="refund_no">
<el-input v-model="refundTableData.searchParam.refund_no" class="w-[240px]" :placeholder="t('refundNumberPlaceholder')" />
</el-form-item>
<el-form-item :label="t('orderNumber')" prop="order_no">
<el-input v-model="refundTableData.searchParam.order_no" class="w-[240px]" :placeholder="t('orderNumberPlaceholder')" />
</el-form-item>
<el-form-item :label="t('refundStatus')" prop="status">
<el-select v-model="refundTableData.searchParam.status" clearable class="input-width">
<el-option :label="t('selectPlaceholder')" value="" />
<el-option :label="item.name" :value="key" v-for="(item, key) in refundList" :key="key"/>
</el-select>
</el-form-item>
<el-form-item :label="t('refundTime')" prop="create_time">
<el-date-picker v-model="refundTableData.searchParam.create_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadRefundList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div>
<el-table :data="refundTableData.data" size="large" v-loading="refundTableData.loading">
<template #empty>
<span>{{ !refundTableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column :show-overflow-tooltip="true" :label="t('memberInfo')" align="left" min-width="140">
<template #default="{ row }">
<div class="flex items-center cursor-pointer " @click="toMember(row.member.member_id)">
<img class="w-[50px] h-[50px] mr-[10px]" v-if="row.member.headimg" :src="img(row.member.headimg)" alt="">
<img class="w-[50px] h-[50px] rounded-full mr-[10px]" v-else src="@/app/assets/images/member_head.png" alt="">
<div class="flex flex flex-col">
<span>{{ row.member.nickname || '' }}</span>
<span>{{ row.member.mobile || '' }}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="refund_no" :label="t('refundNumber')" align="center" min-width="200" />
<el-table-column prop="item.item_name" :label="t('refundSource')" align="center" min-width="140" />
<el-table-column prop="money" :label="t('refundAmount')" align="center" min-width="140" />
<el-table-column :label="t('refundTime')" min-width="180" align="center">
<template #default="{ row }">
{{ row.create_time || '' }}
</template>
</el-table-column>
<el-table-column :label="t('statusName')" min-width="180" align="center">
<template #default="{ row }">
{{ row.status_name || '' }}
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" align="right" width="130">
<template #default="{ row }">
<el-button type="primary" link @click="infoEvent(row)">{{ t('info') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="refundTableData.page" v-model:page-size="refundTableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="refundTableData.total"
@size-change="loadRefundList()" @current-change="loadRefundList" />
</div>
</div>
</el-card>
<el-dialog v-model="refundInfoShowDialog" :title="t('refundDetail')" width="500px" :destroy-on-close="true">
<el-form :model="refundInfo" label-width="120px" ref="formRef" class="page-form">
<el-form-item :label="t('nickname')">
<div class="input-width"> {{ refundInfo.member.nickname }}</div>
</el-form-item>
<el-form-item :label="t('refundSource')">
<div class="input-width"> {{ refundInfo.item.item_name }}</div>
</el-form-item>
<el-form-item :label="t('refundAmount')">
<div class="input-width"> {{ refundInfo.money }}</div>
</el-form-item>
<el-form-item :label="t('orderNumber')">
<div class="input-width"> {{ refundInfo.item.order_no }}</div>
</el-form-item>
<el-form-item :label="t('refundNumber')">
<div class="input-width"> {{ refundInfo.refund_no }}</div>
</el-form-item>
<el-form-item :label="t('refundTime')">
<div class="input-width"> {{ refundInfo.create_time }}</div>
</el-form-item>
<el-form-item :label="t('statusName')">
<div class="input-width"> {{ refundInfo.status_name }}</div>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="refundInfoShowDialog = false">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { img } from '@/utils/common'
import { useRouter, useRoute } from 'vue-router'
import { getRechargeRefund, getRechargeRefundStatus, getRechargeRefundStat } from '@/addon/recharge/api/recharge'
import { FormInstance } from 'element-plus'
const router = useRouter()
const route = useRoute()
const pageName = route.meta.title
const searchFormRef = ref<FormInstance>()
const refundTableData = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
refund_no: '',
create_time: [],
status: '',
keywords: '',
order_no: ''
}
})
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadRefundList()
}
/**
* 获取退款列表
*/
const loadRefundList = (page: number = 1) => {
refundTableData.loading = true
refundTableData.page = page
getRechargeRefund({
page: refundTableData.page,
limit: refundTableData.limit,
...refundTableData.searchParam
}).then(res => {
refundTableData.loading = false
refundTableData.data = res.data.data
refundTableData.total = res.data.total
}).catch(() => {
refundTableData.loading = false
})
}
loadRefundList()
const refundList = ref([])
const checkRefundList = () => {
getRechargeRefundStatus().then(res => {
refundList.value = res.data
})
}
checkRefundList()
const refundStat = reactive({
refund_all_money: 0.00,
refund_have_money: 0.00,
refund_fail_money: 0.00
})
const checkRefundStat = () => {
getRechargeRefundStat().then(res => {
refundStat.refund_all_money = res.data.all.money
refundStat.refund_have_money = res.data.have.money
refundStat.refund_fail_money = res.data['-1'].money
})
}
checkRefundStat()
const refundInfoShowDialog = ref<Boolean>(false)
interface RefundInfoType {
member: {
nickname: string
}
item: {
item_name: string
order_no: string
}
money: string
refund_no: string
create_time: string
status_name: string
}
const refundInfo = ref<RefundInfoType>({})
const infoEvent = (info:any) => {
refundInfo.value = info
refundInfoShowDialog.value = true
}
/**
* 会员详情
*/
const toMember = (memberId: number) => {
router.push(`/member/detail?id=${memberId}`)
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,90 @@
<template>
<el-form ref="formRef" label-width="120px" :model="formData" :rules="formRules" label-position="left">
<el-form-item :label="t('growth')" prop="value">
<span>{{ formData.value }}</span>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue'
import { FormRules } from 'element-plus'
import Test from '@/utils/test'
import { t } from "@/lang";
const props = defineProps({
modelValue: {
type: Object,
default: () => {
return {}
}
}
})
const emits = defineEmits(['update:modelValue'])
const formData = ref({
value: ''
})
const formRef = ref(null)
//
const regExp = {
required: /[\S]+/,
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
special: /^\d{0,10}(.?\d{0,3})$/
}
const formRules = reactive<FormRules>({
value: [{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value === null || value === '') {
callback()
} else if (isNaN(value) || !regExp.number.test(value)) {
callback(t('limitTips'))
} else if (Number(value) < 0) {
callback(t('valueMustBeGreaterThanZero'))
} else {
callback();
}
}
}],
})
const value = computed({
get () {
return props.modelValue
},
set (value) {
emits('update:modelValue', value)
}
})
watch(() => value.value, (nval, oval) => {
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
formData.value = value.value
}
}, { immediate: true })
watch(() => formData.value, () => {
value.value = formData.value
}, { deep: true })
const verify = async () => {
let verify = true
await formRef.value?.validate((valid) => {
verify = valid
})
return verify
}
defineExpose({
verify
})
</script>
<style lang="scss" scoped>
.input-width-short{
width: 190px;
}
</style>

View File

@ -0,0 +1,90 @@
<template>
<el-form ref="formRef" label-width="120px" :model="formData" :rules="formRules" label-position="left">
<el-form-item :label="t('point')" prop="value">
<span>{{ formData.value }}</span>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue'
import { FormRules } from 'element-plus'
import Test from '@/utils/test'
import { t } from "@/lang";
const props = defineProps({
modelValue: {
type: Object,
default: () => {
return {}
}
}
})
const emits = defineEmits(['update:modelValue'])
const formData = ref({
value: ''
})
const formRef = ref(null)
//
const regExp = {
required: /[\S]+/,
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
special: /^\d{0,10}(.?\d{0,3})$/
}
const formRules = reactive<FormRules>({
value: [{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value === null || value === '') {
callback()
} else if (isNaN(value) || !regExp.number.test(value)) {
callback(t('limitTips'))
} else if (value < 0) {
callback(t('valueMustBeGreaterThanZero'))
} else {
callback();
}
}
}],
})
const value = computed({
get () {
return props.modelValue
},
set (value) {
emits('update:modelValue', value)
}
})
watch(() => value.value, (nval, oval) => {
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
formData.value = value.value
}
}, { immediate: true })
watch(() => formData.value, () => {
value.value = formData.value
}, { deep: true })
const verify = async () => {
let verify = true
await formRef.value?.validate((valid) => {
verify = valid
})
return verify
}
defineExpose({
verify
})
</script>
<style lang="scss" scoped>
.input-width-short{
width: 190px;
}
</style>

View File

@ -0,0 +1,90 @@
<template>
<el-form ref="formRef" label-width="120px" :model="formData" :rules="formRules">
<el-form-item :label="t('growth')" prop="value">
<el-input v-model.trim="formData.value" clearable placeholder="0" class="input-width-short" maxlength="8" show-word-limit />
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue'
import { FormRules } from 'element-plus'
import Test from '@/utils/test'
import { t } from "@/lang";
const props = defineProps({
modelValue: {
type: Object,
default: () => {
return {}
}
}
})
const emits = defineEmits(['update:modelValue'])
const formData = ref({
value: ''
})
const formRef = ref(null)
//
const regExp = {
required: /[\S]+/,
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
special: /^\d{0,10}(.?\d{0,3})$/
}
const formRules = reactive<FormRules>({
value: [{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value === null || value === '') {
callback()
} else if (isNaN(value) || !regExp.number.test(value)) {
callback(t('limitTips'))
} else if (Number(value) < 0) {
callback(t('valueMustBeGreaterThanZero'))
} else {
callback();
}
}
}],
})
const value = computed({
get () {
return props.modelValue
},
set (value) {
emits('update:modelValue', value)
}
})
watch(() => value.value, (nval, oval) => {
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
formData.value = value.value
}
}, { immediate: true })
watch(() => formData.value, () => {
value.value = formData.value
}, { deep: true })
const verify = async () => {
let verify = true
await formRef.value?.validate((valid) => {
verify = valid
})
return verify
}
defineExpose({
verify
})
</script>
<style lang="scss" scoped>
.input-width-short{
width: 190px;
}
</style>

View File

@ -0,0 +1,90 @@
<template>
<el-form ref="formRef" label-width="120px" :model="formData" :rules="formRules">
<el-form-item :label="t('point')" prop="value">
<el-input v-model.trim="formData.value" clearable placeholder="0" class="input-width-short" maxlength="8" show-word-limit />
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue'
import { FormRules } from 'element-plus'
import Test from '@/utils/test'
import { t } from "@/lang";
const props = defineProps({
modelValue: {
type: Object,
default: () => {
return {}
}
}
})
const emits = defineEmits(['update:modelValue'])
const formData = ref({
value: ''
})
const formRef = ref(null)
//
const regExp = {
required: /[\S]+/,
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
special: /^\d{0,10}(.?\d{0,3})$/
}
const formRules = reactive<FormRules>({
value: [{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value === null || value === '') {
callback()
} else if (isNaN(value) || !regExp.number.test(value)) {
callback(t('limitTips'))
} else if (value < 0) {
callback(t('valueMustBeGreaterThanZero'))
} else {
callback();
}
}
}],
})
const value = computed({
get () {
return props.modelValue
},
set (value) {
emits('update:modelValue', value)
}
})
watch(() => value.value, (nval, oval) => {
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
formData.value = value.value
}
}, { immediate: true })
watch(() => formData.value, () => {
value.value = formData.value
}, { deep: true })
const verify = async () => {
let verify = true
await formRef.value?.validate((valid) => {
verify = valid
})
return verify
}
defineExpose({
verify
})
</script>
<style lang="scss" scoped>
.input-width-short{
width: 190px;
}
</style>

View File

@ -0,0 +1,69 @@
<template>
<div v-for="item in gifts">
<component :is="item.edit_component" v-model="formData[item.key]" ref="giftRefs" v-if="item.edit_component"/>
</div>
</template>
<script lang="ts" setup>
import { ref, defineAsyncComponent, computed, watch } from 'vue'
import {getPackageGiftDict} from "@/addon/recharge/api/recharge";
const gifts = ref({})
const props = defineProps({
modelValue: {
type: Object,
default: () => {
return {}
}
}
})
const emits = defineEmits(['update:modelValue'])
const formData = ref({})
const value = computed({
get () {
return props.modelValue
},
set (value) {
emits('update:modelValue', value)
}
})
const giftRefs = ref([])
watch(() => value.value, (nval, oval) => {
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
formData.value = value.value
}
}, { immediate: true })
watch(() => formData.value, () => {
value.value = formData.value
}, { deep: true })
const modules: any = import.meta.glob('@/**/*.vue')
getPackageGiftDict().then(({ data }) => {
Object.keys(data).forEach((key: string) => {
if (modules[data[key].edit_component]) {
data[key].edit_component && (data[key].edit_component = defineAsyncComponent(modules[data[key].edit_component]))
} else {
data.splice(key, 1);
}
})
gifts.value = data
})
const verify = async () => {
let verify = true
for (let i = 0; i < giftRefs.value.length; i++) {
const item = giftRefs.value[i]
!await item.verify() && (verify = false)
}
return verify
}
defineExpose({
verify
})
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,135 @@
<template>
<div>
<!-- 充值设置 -->
<el-card class="box-card !border-none main-container" shadow="never">
<div class="flex justify-between items-center mb-[10px]">
<span class="text-page-title">{{ pageName }}</span>
</div>
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form">
<el-card class="box-card !border-none" shadow="never">
<el-form-item :label="t('enableRecharge')" prop="is_use">
<div class="flex items-center mx-[5px]">
<el-switch v-model="formData.is_use" :active-value="1" :inactive-value="0" />
</div>
</el-form-item>
<el-form-item :label="t('rechargeExplain')" prop="recharge_explain">
<el-input v-model="formData.recharge_explain" :placeholder="t('rechargeExplainPlaceholder')" type="textarea" maxlength="500" show-word-limit rows="5" class="!w-[400px]" clearable />
</el-form-item>
<el-form-item :label="t('min_price')" prop="min_price">
<el-input v-model.trim="formData.min_price" clearable placeholder="0.00" class="input-width-short" maxlength="8" show-word-limit />
</el-form-item>
<el-form-item :label="t('closeOrderInfo')" prop="close_length">
<div>
<p class="!text-sm">
<span>{{ t('closeOrderInfoLeft') }}</span>
<el-input v-model.trim="formData.close_length" class="!w-[120px] mx-[10px]" @keyup="filterNumber($event)" clearable />
<span>{{ t('closeOrderInfoRight') }}</span>
</p>
<p class="text-[12px] text-[#a9a9a9] leading-normal mt-[5px]">{{ t('closeOrderInfoBottom') }}</p>
</div>
</el-form-item>
</el-card>
</el-form>
</el-card>
<!-- 提交按钮 -->
<div class="fixed-footer-wrap">
<div class="fixed-footer h-[48px]">
<el-button type="primary" @click="onSave">{{ t("save") }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed } from "vue";
import { t } from "@/lang";
import { useRoute } from "vue-router";
import { FormInstance } from "element-plus";
import {setRechargeConfig ,getRechargeConfig} from "@/addon/recharge/api/recharge";
import { filterNumber } from '@/utils/common'
const route = useRoute();
const pageName = route.meta.title;
const formData = ref({
is_use: 1,
recharge_explain: "",
min_price: "",
close_length: ''
});
const formRef = ref<FormInstance>();
//
const regExp: any = {
required: /[\S]+/,
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
special: /^\d{0,10}(.?\d{0,3})$/
}
const formRules = computed(() => {
return {
min_price: [{
required: true,
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value === null || value === '') {
callback(t('minPricePlaceholder'))
} else if (isNaN(value) || !regExp.digit.test(value)) {
callback(t('limitTips'))
} else if (value < 0.01) {
callback(t('limitTipsTwo'))
} else {
callback();
}
}
}],
close_length: [
{ validator: validCloseLength, trigger: 'blur' }
],
};
});
const validCloseLength = (rule:any, value:any, callback:Function) => {
if (value) {
if (value == '') {
return callback(new Error(t('CloseLengthPlaceholder')))
} else if (Number(value) >= 10 && Number(value) <= 1440) {
return callback()
} else {
return callback(new Error(t('closeOrderInfoBottom')))
}
} else {
return callback()
}
}
const getRechargeConfigFn = () => {
getRechargeConfig().then((res: any) => {
formData.value = res.data;
})
};
getRechargeConfigFn();
/**** 提交 ****/
const onSave = async () => {
await formRef.value?.validate(async(valid) => {
if (valid) {
setRechargeConfig(formData.value).then(() => {
getRechargeConfigFn();
}).catch(() => {
});
}
});
};
</script>
<style lang="scss" scoped>
.input-width-short{
width: 150px;
}
</style>

View File

@ -0,0 +1,126 @@
<template>
<el-drawer v-model="showDialog" :title="t('detailTitle')" direction="rtl" :before-close="handleClose" class="member-detail-drawer">
<div class="main-container" v-loading="loading">
<el-tabs v-model="activeName" class="pb-[10px]" @tab-change="handleClick">
<el-tab-pane :label="t('basicInfo')" name="basicInfo" />
<el-tab-pane :label="t('giftPackInfo')" name="giftsList" />
</el-tabs>
<div v-if="activeName == 'basicInfo'">
<el-card class="card !border-none" shadow="never" >
<el-form class="mt-[15px]" :model="formData" label-width="120px" ref="formRef" label-position="left">
<div class="relative" shadow="never" v-if="formData">
<el-row>
<el-col :span="8">
<el-form-item :label="t('rechargeName')">
<div class="input-width">
<span>{{formData.recharge_name}}</span>
</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('status')">
<div class="input-width">
<span>{{formData.status_name}}</span>
</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('faceValue')">
<div class="input-width">
<span>{{formData.face_value}}</span>
</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('price')">
<div class="input-width">
<span>{{formData.buy_price}}</span>
</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('createTime')">
<div class="input-width">
<span>{{formData.create_time}}</span>
</div>
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
</el-card>
</div>
<div v-if="activeName == 'giftsList'">
<el-card class="card !border-none" shadow="never" >
<div v-for="item in gifts">
<component :is="item.detail_component" v-model="formDataComponent[item.key]" ref="giftRefs" v-if="item.detail_component"/>
</div>
</el-card>
</div>
</div>
</el-drawer>
</template>
<script lang="ts" setup>
import {t} from '@/lang'
import {getPackageGiftDict, getRechargePackageInfo} from "@/addon/recharge/api/recharge";
import {useRoute, useRouter} from 'vue-router'
import {defineAsyncComponent, ref} from 'vue'
const route = useRoute()
const router = useRouter()
const gifts = ref({})
const showDialog = ref(false)
const loading = ref(false)
let id = ''
const formData:Record<string, any> = ref({})
const formDataComponent:Record<string, any> = ref({})
const activeName = ref('basicInfo')
const handleClick = (data:string) => {
activeName.value = data
}
const handleClose = (done: () => void) => {
activeName.value = 'basicInfo';
showDialog.value = false;
}
const getRechargePackageInfoFn= (id:number)=>{
loading.value = true
const data = {
recharge_id: id
}
getRechargePackageInfo(data).then((res:any)=>{
formData.value = Object.assign(formData.value,res.data)
formDataComponent.value = Object.assign(formDataComponent.value,res.data.gift_json)
loading.value = false
})
}
const setFormData = async (row: any = null) => {
id = row.id;
getRechargePackageInfoFn(Number(id))
const modules: any = import.meta.glob('@/**/*.vue')
getPackageGiftDict().then(({ data }) => {
console.log(data)
Object.keys(data).forEach((key: string) => {
if (modules[data[key].detail_component]) {
data[key].detail_component && (data[key].detail_component = defineAsyncComponent(modules[data[key].detail_component]))
} else {
data.splice(key, 1);
}
})
gifts.value = data
})
}
defineExpose({
showDialog,
setFormData
})
</script>
<style lang="scss">
.member-detail-drawer{
width: 1000px !important;
}
</style>

View File

@ -0,0 +1,170 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-page-header :content="recharge_id ? t('editRecharge') : t('addRecharge')" :icon="ArrowLeft" @back="back()" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never" v-loading="loading">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('rechargeName')" prop="recharge_name">
<el-input v-model.trim="formData.recharge_name" clearable :placeholder="t('namePlaceholder')" class="input-width" maxlength="10" show-word-limit />
</el-form-item>
<el-form-item :label="t('faceValue')" prop="face_value">
<el-input v-model.trim="formData.face_value" clearable placeholder="0.00" class="input-width-short" maxlength="5">
<template #append>{{ t('yuan') }}</template>
</el-input>
</el-form-item>
<el-form-item :label="t('price')" prop="buy_price">
<el-input v-model.trim="formData.buy_price" clearable placeholder="0.00" class="input-width-short" maxlength="5">
<template #append>{{ t('yuan') }}</template>
</el-input>
</el-form-item>
<div>
<package-gift v-if="!loading" ref="giftRef" v-model="formData.gift_json"/>
</div>
<el-form-item :label="t('sort')" prop="sort">
<el-input v-model.number="formData.sort" clearable placeholder="0" class="input-width-short" maxlength="8" @keyup="filterNumber($event)" @blur="formData.sort = $event.target.value" show-word-limit />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-switch v-model="formData.status" class="input-width" :active-value="1" :inactive-value="0" />
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="save()">{{ t("save") }}</el-button>
<el-button @click="back()">{{ t("cancel") }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, reactive} from "vue";
import { t } from "@/lang";
import { useRoute, useRouter } from "vue-router";
import { ArrowLeft } from "@element-plus/icons-vue";
import { FormInstance } from "element-plus";
import {img, filterNumber } from '@/utils/common'
import {addRechargePackage,editRechargePackage,getRechargePackageInfo} from "@/addon/recharge/api/recharge";
import packageGift from '@/addon/recharge/views/package/components/package-gift.vue'
const router = useRouter();
const route = useRoute();
const loading = ref(false);
const recharge_id = route.query.recharge_id;
const formData = reactive({
recharge_id:0,
recharge_name: "",
face_value: "",
buy_price: "",
sort: "",
status: 1,
gift_json:{}
});
const gifts = ref({})
const giftRef = ref(null)
const formRef = ref<FormInstance>();
//
const regExp: any = {
required: /[\S]+/,
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
special: /^\d{0,10}(.?\d{0,3})$/
}
const formRules = computed(() => {
return {
recharge_name: [{ required: true, message: t("namePlaceholder"), trigger: "blur" }],
face_value: [{
required: true,
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value === null || value === '') {
callback(t('faceValuePlaceholder'))
} else if (isNaN(value) || !regExp.digit.test(value)) {
callback(t('limitTips'))
} else if (value <= 0) {
callback(t('faceValueMustBeGreaterThanZero'))
} else {
callback();
}
}
}],
buy_price: [{
required: true,
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value === null || value === '') {
callback(t('pricePlaceholder'))
} else if (isNaN(value) || !regExp.digit.test(value)) {
callback(t('limitTips'))
} else if (value <= 0) {
callback(t('priceMustBeGreaterThanZero'))
} else {
callback();
}
}
}]
};
});
const getRechargePackageInfoFn= () => {
if (recharge_id) {
//
loading.value = true;
let data = {
recharge_id: Number(recharge_id)
}
getRechargePackageInfo(data).then((res) => {
const data = res.data;
if (data) {
Object.assign(formData, data);
loading.value = false;
}
})
}
};
getRechargePackageInfoFn();
const save = async () => {
//
await formRef.value?.validate(async(valid) => {
if (valid) {
if (!await giftRef.value?.verify()) return
loading.value = true
if (recharge_id) {
formData.recharge_id = Number(recharge_id);
editRechargePackage(formData).then((res) => {
loading.value = false;
back()
}).catch(() => {
loading.value = false;
});
} else {
addRechargePackage(formData).then((res) => {
loading.value = false;
back()
}).catch(() => {
loading.value = false;
});
}
}
});
};
const back = () => {
router.push('/recharge/package/list')
};
</script>
<style lang="scss" scoped>
.input-width-short{
width: 190px;
}
</style>

View File

@ -0,0 +1,220 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
<el-button type="primary" @click="handleChange">{{ t("addRecharge") }}
</el-button>
</div>
<!-- 搜索 -->
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('rechargeName')" prop="recharge_name">
<el-input v-model.trim="tableData.searchParam.recharge_name" :placeholder="t('rechargeNamePlaceholder')" />
</el-form-item>
<el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker v-model="tableData.searchParam.create_time" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
</el-form-item>
<el-button type="primary" @click="loadRechargePackageList()">{{ t("search") }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t("reset") }}</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 列表 -->
<div>
<el-table :data="tableData.data" size="large" v-loading="tableData.loading" ref="rechargePackageTableRef">
<el-table-column prop="recharge_name" :label="t('rechargeName')" min-width="130" />
<el-table-column prop="rechargeInfo" :label="t('rechargeInfo')" min-width="130" >
<template #default="{ row }">
<div>
<p>{{t('faceValue')}}{{ row.face_value }}</p>
<p>{{t('price')}}{{ row.buy_price }}</p>
</div>
</template>
</el-table-column>
<el-table-column prop="giftPackInfo" :label="t('giftPackInfo')" min-width="130" >
<template #default="{ row }">
<div>
<p v-if="row.point>0">{{t('point')}}{{ row.point }}</p>
<p v-if="row.growth>0">{{t('growth')}}{{ row.growth }}</p>
<template v-if="row.gift_content">
<p v-for="(item,index) in row.gift_content" :key="index">{{ item.info }}</p>
</template>
</div>
</template>
</el-table-column>
<el-table-column prop="sale_num" :label="t('saleNum')" min-width="100" />
<el-table-column prop="sort" min-width="100" :show-overflow-tooltip="true">
<template #header>
<div style="display: inline-flex; align-items: center">
<span class="mr-[5px]">{{ t('sort') }}</span>
<el-tooltip class="box-item" effect="light" :content="t('sortRules')" placement="top">
<el-icon color="#666">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<template #default="{ row }">
<el-input v-model.number="row.sort" class="w-[70px]" maxlength="8" @blur="sortInputListener(row.sort, row)" />
</template>
</el-table-column>
<el-table-column prop="status" :label="t('status')" min-width="130">
<template #default="{ row }">
<el-tag class="cursor-pointer" :type="row.status != 0 ? 'success' : 'danger'" @click="showClick(row)">{{ row.status != 0 ? '开启' : '关闭 ' }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="create_time" :label="t('createTime')" min-width="150">
<template #default="{ row }">
<div>{{ row.create_time }}</div>
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{t("edit")}}</el-button>
<el-button type="primary" link @click="detailEvent(row.recharge_id)">{{ t('detail') }}</el-button>
<el-button type="primary" link @click="toOrderList(row.recharge_id)">{{ t('rechargeRecord') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.recharge_id)">{{ t("delete") }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.limit" layout="total, sizes, prev, pager, next, jumper" :total="tableData.total" @size-change="loadRechargePackageList()" @current-change="loadRechargePackageList" />
</div>
</div>
</el-card>
<package-detail ref="packageDetailDialog"></package-detail>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, reactive } from "vue";
import {t} from "@/lang";
import { useRoute, useRouter } from "vue-router";
import { FormInstance, ElMessage, ElMessageBox } from "element-plus";
import packageDetail from '@/addon/recharge/views/package/detail.vue'
import {debounce} from "@/utils/common";
import {getRechargePackageList,deleteRechargePackage,editRechargeStatus,modifyRechargeSort} from "@/addon/recharge/api/recharge";
import { isArray } from "lodash-es";
const router = useRouter();
const route = useRoute();
const pageName = route.meta.title;
const searchFormRef = ref<FormInstance>();
//
const tableData = reactive({
page: 1,
limit: 10,
total: 0,
loading: false,
data: [],
searchParam: {
create_time: [],
recharge_name: ""
}
});
//
const loadRechargePackageList = (page: number = 1) => {
tableData.loading = true;
tableData.page = page;
getRechargePackageList({
page: tableData.page,
limit: tableData.limit,
...tableData.searchParam,
}).then((res) => {
tableData.loading = false;
tableData.data = res.data.data;
tableData.total = res.data.total;
}).catch(() => {
tableData.loading = false;
});
};
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.resetFields();
loadRechargePackageList();
};
const handleChange = () => {
router.push('/recharge/package/edit')
}
const editEvent = (data: any) => {
router.push('/recharge/package/edit?recharge_id='+data.recharge_id)
}
//
const packageDetailDialog: Record<string, any> | null = ref(null)
const detailEvent=(id:number)=>{
let data = {id: id};
packageDetailDialog.value.setFormData(data);
packageDetailDialog.value.showDialog = true;
}
const showClick = (row: any) => {
row.status = row.status === 1 ? 0 : 1
const obj = {
recharge_id: row.recharge_id,
status: row.status,
}
editRechargeStatus(obj)
}
//
const regExp = {
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
};
//
const sortInputListener = debounce((sort, row) => {
if (isNaN(sort) || !regExp.number.test(sort)) {
ElMessage({
type: "warning",
message: `${ t("sortTips") }`,
});
return;
}
if (sort > 99999999) {
row.sort = 99999999;
}
modifyRechargeSort({
recharge_id: row.recharge_id,
sort,
}).then((res) => {
loadRechargePackageList();
});
});
//
const deleteEvent = (id:number) => {
ElMessageBox.confirm(t('deleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
deleteRechargePackage(id).then(() => {
loadRechargePackageList()
}).catch(() => {
})
})
}
const toOrderList = (id:number) => {
router.push('/recharge/order/list?recharge_id='+id)
}
loadRechargePackageList()
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,12 @@
{
"title": "会员充值",
"desc": "在线充值到会员账户",
"key": "recharge",
"version": "1.0.0",
"author": "niucloud",
"type": "addon",
"support_app": "",
"support_version": "0.0.3",
"jar": "recharge.jar",
"mainClass": "com.niu.recharge.WebApplication"
}

Binary file not shown.

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>niucloud-addon</artifactId>
<groupId>com.niu</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>recharge</artifactId>
<version>1.0.0</version>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<finalName>recharge</finalName>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/services/*</exclude>
<exclude>META-INF/spring/*</exclude>
<exclude>META-INF/**.factories</exclude>
<exclude>META-INF/**.xml</exclude>
<exclude>META-INF/**.properties</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks />
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.niu</groupId>
<artifactId>niucloud-core</artifactId>
<version>1.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>yly</artifactId>
<groupId>com.yly.print_sdk_library</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>niucloud-addon</artifactId>
<groupId>com.niu</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>recharge</artifactId>
<version>1.0.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.niu</groupId>
<artifactId>niucloud-core</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>javax.mail-api</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
</dependencies>
<build>
<finalName>recharge</finalName>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/services/*</exclude>
<exclude>META-INF/spring/*</exclude>
<exclude>META-INF/**.factories</exclude>
<exclude>META-INF/**.xml</exclude>
<exclude>META-INF/**.properties</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
</tasks>
<!-- <copy overwrite="true" file="target/recharge.jar"-->
<!-- tofile="../../webroot/jar/addon/recharge.jar"/>-->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,12 @@
package com.niu.recharge;
import com.niu.core.common.annotation.SpringModuleApplication;
import org.springframework.boot.SpringApplication;
@SpringModuleApplication("recharge")
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}

View File

@ -0,0 +1,57 @@
package com.niu.recharge.controller.adminapi;
import com.niu.core.common.domain.PageParam;
import com.niu.core.common.domain.Result;
import com.niu.recharge.enums.OrderStatusEnum;
import com.niu.recharge.service.admin.IRechargeOrderService;
import com.niu.recharge.service.admin.param.RechargeOrderSearchParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Map;
@RestController
@RequestMapping("adminapi/recharge/order")
public class OrderController {
@Resource
IRechargeOrderService rechargeOrderService;
@GetMapping("")
public Result lists(PageParam pageParam, RechargeOrderSearchParam searchParam) {
return Result.success(rechargeOrderService.list(pageParam, searchParam));
}
/**
* 订单详情
*
* @return
*/
@GetMapping("/{order_id}")
public Result detail(@PathVariable("order_id") Integer orderId) {
return Result.success(rechargeOrderService.detail(orderId));
}
/**
* 订单状态
*
* @return
*/
@GetMapping("/status")
public Result<Map<Integer, Map<String, Object>>> status() {
return Result.success(OrderStatusEnum.getMap());
}
/**
* 订单统计
*
* @return
*/
@GetMapping("/stat")
public Result stat(RechargeOrderSearchParam searchParam) {
return Result.success(rechargeOrderService.stat(searchParam));
}
}

View File

@ -0,0 +1,76 @@
package com.niu.recharge.controller.adminapi;
import com.niu.core.common.domain.PageParam;
import com.niu.core.common.domain.Result;
import com.niu.recharge.service.admin.IRechargeService;
import com.niu.recharge.service.admin.param.RechargeEditSortParam;
import com.niu.recharge.service.admin.param.RechargeModifyStatusParam;
import com.niu.recharge.service.admin.param.RechargePageParam;
import com.niu.recharge.service.admin.param.RechargeParam;
import com.niu.recharge.service.core.param.RechargeSetConfigParam;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RestController
@RequiredArgsConstructor
@RequestMapping("adminapi/recharge")
public class RechargeController {
private final IRechargeService rechargeService;
@PostMapping("/package/config")
Result<?> setRechargeConfig(@RequestBody RechargeSetConfigParam param) {
rechargeService.setRechargeConfig(param);
return Result.success();
}
@GetMapping("/package/config")
Result<?> getRechargeConfig() {
return Result.success(rechargeService.getRechargeConfig());
}
@GetMapping("/package")
Result<?> lists(PageParam pageParam, RechargePageParam param) {
return Result.success(rechargeService.getPage(pageParam, param));
}
@PostMapping("/package")
Result<?> add(@RequestBody RechargeParam param) {
rechargeService.add(param);
return Result.success();
}
@PutMapping("/package/{id}")
Result<?> edit(@PathVariable Integer id, @RequestBody RechargeParam param) {
rechargeService.edit(id, param);
return Result.success();
}
@GetMapping("/package/init")
Result<?> init(@RequestParam("recharge_id") Integer rechargeId) {
return Result.success(rechargeService.getInit(rechargeId));
}
@DeleteMapping("/package/{id}")
Result<?> del(@PathVariable Integer id) {
rechargeService.del(id);
return Result.success();
}
@PutMapping("/package/status")
Result<?> modifyStatus(@RequestBody RechargeModifyStatusParam param) {
rechargeService.modifyStatus(param);
return Result.success();
}
@PutMapping("/package/sort")
Result<?> editSort(@RequestBody RechargeEditSortParam param) {
rechargeService.editSort(param);
return Result.success();
}
@GetMapping("dict/package_gift")
Result<?> getPackageGiftDict() {
return Result.success(rechargeService.getPackageGiftDict());
}
}

View File

@ -0,0 +1,74 @@
package com.niu.recharge.controller.adminapi;
import com.niu.core.common.domain.PageParam;
import com.niu.core.common.domain.Result;
import com.niu.recharge.enums.RefundStatusEnum;
import com.niu.recharge.service.admin.IRechargeRefundService;
import com.niu.recharge.service.admin.param.RechargeRefundSearchParam;
import com.niu.recharge.service.admin.vo.RechargeRefundStatVo;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Map;
@RestController
@RequestMapping("adminapi/recharge/refund")
public class RefundController {
@Resource
IRechargeRefundService rechargeRefundService;
/**
* 退款列表
*
* @param pageParam
* @param searchParam
* @return
*/
@GetMapping("")
public Result lists(PageParam pageParam, RechargeRefundSearchParam searchParam) {
return Result.success(rechargeRefundService.list(pageParam, searchParam));
}
/**
* 发起退款
*
* @param orderId
* @return
*/
@PutMapping("/{order_id}")
public Result refund(@PathVariable("order_id") Integer orderId) {
rechargeRefundService.refund(orderId);
return Result.success();
}
/**
* 退款详情
*
* @return
*/
@GetMapping("/{refund_id}")
public Result detail(@PathVariable("refund_id") Integer refundId) {
return Result.success(rechargeRefundService.detail(refundId));
}
/**
* 退款状态
*
* @return
*/
@GetMapping("/status")
public Result<Map<Integer, Map<String, Object>>> status() {
return Result.success(RefundStatusEnum.getMap());
}
/**
* 退款统计
*
* @return
*/
@GetMapping("/stat")
public Result<RechargeRefundStatVo> stat(RechargeRefundSearchParam searchParam) {
return Result.success(rechargeRefundService.stat(searchParam));
}
}

View File

@ -0,0 +1,83 @@
package com.niu.recharge.controller.api;
import com.niu.core.common.domain.PageParam;
import com.niu.core.common.domain.PageResult;
import com.niu.core.common.domain.Result;
import com.niu.recharge.enums.OrderStatusEnum;
import com.niu.recharge.service.api.IRechargeOrderService;
import com.niu.recharge.service.api.IRechargeService;
import com.niu.recharge.service.api.param.RechargeOrderSearchParam;
import com.niu.recharge.service.api.vo.RechargeOrderInfoVo;
import com.niu.recharge.service.api.vo.RechargeOrderListVo;
import com.niu.recharge.service.core.param.RechargeOrderCreateParam;
import com.niu.recharge.service.core.vo.RechargeOrderCreateVo;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Map;
@RestController
@RequestMapping("api/recharge")
public class RechargeController {
@Resource
IRechargeOrderService rechargeOrderService;
@Resource
IRechargeService rechargeService;
/**
* 充值订单创建
*
* @param param
* @return
*/
@PostMapping("/recharge")
public Result<RechargeOrderCreateVo> create(@Validated @RequestBody RechargeOrderCreateParam param) {
return Result.success(rechargeOrderService.create(param));
}
/**
* 订单列表
*
* @param
* @return
*/
@GetMapping("/recharge")
public Result<PageResult<RechargeOrderListVo>> list(PageParam pageParam, RechargeOrderSearchParam searchParam) {
return Result.success(rechargeOrderService.list(pageParam, searchParam));
}
/**
* 订单详情
*
* @param orderId
* @return
*/
@GetMapping("/recharge/{order_id}")
public Result<RechargeOrderInfoVo> detail(@PathVariable("order_id") Integer orderId) {
return Result.success(rechargeOrderService.detail(orderId));
}
@GetMapping("/package")
public Result<?> getRechargeLists() {
return Result.success(rechargeService.getRechargeLists());
}
@GetMapping("/config")
public Result<?> getRechargeConfig() {
return Result.success(rechargeService.getRechargeConfig());
}
@GetMapping("/status")
public Result<Map<Integer, Map<String, Object>>> status() {
return Result.success(OrderStatusEnum.getMap());
}
@GetMapping("/recharge/status")
public Result<Map<Integer, Map<String, Object>>> rechargeStatus() {
return Result.success(OrderStatusEnum.getMap());
}
}

View File

@ -0,0 +1,88 @@
package com.niu.recharge.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 会员充值套餐实体
*/
@Data
public class Recharge implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@TableId(value = "recharge_id", type = IdType.AUTO)
private Integer rechargeId;
/**
* 站点ID
*/
private Integer siteId;
/**
* 套餐名称
*/
private String rechargeName;
/**
* 面值
*/
private BigDecimal faceValue;
/**
* 价格
*/
private BigDecimal buyPrice;
/**
* 赠送积分
*/
private Integer point;
/**
* 赠送成长值
*/
private Integer growth;
/**
* 赠送优惠券ID
*/
private String couponId;
/**
* 发放数量
*/
private Integer saleNum;
/**
* 排序号
*/
private Integer sort;
/**
* 状态0关闭 1开启
*/
private Integer status;
/**
* 创建时间
*/
private Long createTime;
/**
* 修改时间
*/
private Long updateTime;
private String giftJson;
}

View File

@ -0,0 +1,81 @@
package com.niu.recharge.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class RechargeOrder implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value="order_id", type= IdType.AUTO)
private Integer orderId;
/** 站点id */
private Integer siteId;
/** 订单编号 */
private String orderNo;
/** 订单来源 */
private String orderFrom;
/** 订单类型 */
private String orderType;
/** 支付流水号 */
private String outTradeNo;
/** 订单状态 */
private Integer orderStatus;
/** 退款状态 */
private Integer refundStatus;
/** 会员id */
private Integer memberId;
/** 会员ip */
private String ip;
/** 会员留言信息 */
private String memberMessage;
/** 订单项目金额 */
private BigDecimal orderItemMoney;
/** 订单优惠金额 */
private BigDecimal orderDiscountMoney;
/** 订单金额 */
private BigDecimal orderMoney;
/** 创建时间 */
private Long createTime;
/** 订单支付时间 */
private Long payTime;
/** 订单关闭时间 */
private Long closeTime;
/** 是否删除(针对后台) */
private Integer isDelete;
/** 是否允许退款 */
private Integer isEnableRefund;
/** 商家留言 */
private String remark;
/** 发票id0表示不开发票 */
private Integer invoiceId;
/** 关闭原因 */
private String closeReason;
}

View File

@ -0,0 +1,60 @@
package com.niu.recharge.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class RechargeOrderItem implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value="order_item_id", type= IdType.AUTO)
private Integer orderItemId;
/** 订单id */
private Integer orderId;
/** 站点id */
private Integer siteId;
/** 会员id */
private Integer memberId;
/** 项目id */
private Integer itemId;
/** 项目类型 */
private String itemType;
/** 项目名称 */
private String itemName;
/** 项目图片 */
private String itemImage;
/** 项目单价 */
private BigDecimal price;
/** 购买数量 */
private Integer num;
/** 项目总价 */
private BigDecimal itemMoney;
/** 是否退款 */
private Integer isRefund;
/** 退款编号 */
private String refundNo;
/** 退款状态 */
private Integer refundStatus;
/** 创建时间 */
private Long createTime;
}

View File

@ -0,0 +1,56 @@
package com.niu.recharge.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class RechargeOrderItemRefund implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value="refund_id", type= IdType.AUTO)
private Integer refundId;
private Integer orderItemId;
/** 订单id */
private Integer orderId;
/** 订单编号 */
private String orderNo;
/** 退款编号 */
private String refundNo;
/** 站点id */
private Integer siteId;
/** 会员id */
private Integer memberId;
/** 退货数量 */
private Integer num;
/** 总退款 */
private BigDecimal money;
/** 退款状态 */
private Integer status;
/** 创建时间 */
private Long createTime;
/** 审核时间 */
private Long auditTime;
/** 转账时间 */
private Long transferTime;
/** 项目类型 */
private String itemType;
}

View File

@ -0,0 +1,44 @@
package com.niu.recharge.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class RechargeOrderLog implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value="id", type= IdType.AUTO)
private Integer id;
private Integer orderId;
/** 站点id */
private Integer siteId;
/** 操作内容 */
private String action;
/** 操作人id */
private Integer uid;
/** 操作人名称 */
private String nickName;
/** 订单状态 */
private Integer orderStatus;
/** 操作类型1买家2卖家 3 系统任务 */
private Integer actionWay;
/** 订单状态名称,操作后 */
private String orderStatusName;
/** 操作时间 */
private Long actionTime;
}

View File

@ -0,0 +1,47 @@
package com.niu.recharge.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
@Getter
@AllArgsConstructor
public enum OrderStatusEnum {
WAIT_PAY("待支付", 0),
FINISH("已完成", 1),
CLOSE("已关闭", -1);
private final String name;
private final Integer status;
public static Map<Integer, Map<String, Object>> getMap() {
Map<Integer, Map<String, Object>> map = new HashMap<>();
for(OrderStatusEnum item : OrderStatusEnum.values())
{
Map<String, Object> mapItem = new HashMap<>();
mapItem.put("name", item.getName());
mapItem.put("status", item.getStatus());
map.put(item.getStatus(), mapItem);
}
return map;
}
/**
* 通过status获取当前名称
* @param status
* @return
*/
public static String getNameByStatus(Integer status){
for(OrderStatusEnum item : OrderStatusEnum.values())
{
if(item.getStatus().equals(status))
{
return item.getName();
}
}
return "";
}
}

View File

@ -0,0 +1,29 @@
package com.niu.recharge.enums;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.niu.core.common.utils.json.JsonModuleLoader;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class PackageGiftEnum {
public static JSONArray getPackageGiftEnum() {
JSONObject entries = JsonModuleLoader.build().mergeResultElement("recharge/package_gift.json");
log.info("======PackageGiftEnum entries: {}", entries.toString());
JSONArray jsonArray = new JSONArray();
entries.forEach((k, v) -> {
JSONObject value = JSONUtil.parseObj(v);
JSONObject jsonObject = new JSONObject();
jsonObject.set("key", k);
jsonObject.set("name", value.get("name"));
jsonObject.set("value", value.get("value"));
jsonObject.set("edit_component", value.get("edit_component"));
jsonObject.set("detail_component", value.get("detail_component"));
jsonObject.set("sort", value.get("sort"));
jsonArray.add(jsonObject);
});
return jsonArray;
}
}

View File

@ -0,0 +1,47 @@
package com.niu.recharge.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
@Getter
@AllArgsConstructor
public enum RefundStatusEnum {
REFUNDING("退款中", 1),
REFUND_FAIL("退款失败", -1),
REFUND_COMPLETED("退款完成", 2);
private final String name;
private final Integer status;
public static Map<Integer, Map<String, Object>> getMap() {
Map<Integer, Map<String, Object>> map = new HashMap<>();
for(RefundStatusEnum item : RefundStatusEnum.values())
{
Map<String, Object> mapItem = new HashMap<>();
mapItem.put("name", item.getName());
mapItem.put("status", item.getStatus());
map.put(item.getStatus(), mapItem);
}
return map;
}
/**
* 通过status获取当前名称
* @param status
* @return
*/
public static String getNameByStatus(Integer status){
for(RefundStatusEnum item : RefundStatusEnum.values())
{
if(item.getStatus().equals(status))
{
return item.getName();
}
}
return "";
}
}

View File

@ -0,0 +1,18 @@
package com.niu.recharge.event;
import com.niu.core.common.component.context.event.Event;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@Accessors(chain = true)
@NoArgsConstructor
public class RechargeAfterEvent extends Event {
private Integer memberId;
private String giftJson;
}

View File

@ -0,0 +1,12 @@
package com.niu.recharge.event;
import com.niu.core.common.component.context.event.Event;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class RechargeGiftContentEvent extends Event {
private String key;
private Object value;
}

View File

@ -0,0 +1,41 @@
package com.niu.recharge.listener;
import com.niu.core.common.annotation.EventCallback;
import com.niu.core.common.exception.CommonException;
import com.niu.core.event.pay.PayCreateEventDefiner;
import com.niu.recharge.entity.RechargeOrder;
import com.niu.recharge.enums.OrderStatusEnum;
import com.niu.recharge.service.core.ICoreRechargeOrderService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 一对多的事件监听且有返回值
*/
@EventCallback("recharge")
@Component
public class PayCreateListener extends PayCreateEventDefiner {
@Resource
ICoreRechargeOrderService coreRechargeOrderService;
@Override
public PayCreateEventDefiner.PayCreateEventResult handleCallback(PayCreateEventDefiner.PayCreateEvent event) {
if (!event.getTradeType().equals("recharge")) return null;
RechargeOrder order = coreRechargeOrderService.orderInfo(event.getTradeId());
if (!order.getOrderStatus().equals(OrderStatusEnum.WAIT_PAY.getStatus())) throw new CommonException("只有待支付的订单可以支付");
PayCreateEventDefiner.PayCreateEventResult result = new PayCreateEventDefiner.PayCreateEventResult();
result.setTradeId(event.getTradeId());
result.setTradeType(event.getTradeType());
result.setMainType("member");
result.setMainId(order.getMemberId());
result.setMoney(order.getOrderMoney());
result.setBody("充值订单");
return result;
}
}

View File

@ -0,0 +1,30 @@
package com.niu.recharge.listener;
import cn.hutool.json.JSONUtil;
import com.niu.core.common.annotation.EventListen;
import com.niu.core.common.component.context.listener.AbstractListener;
import com.niu.core.event.pay.PaySuccessEvent;
import com.niu.recharge.service.core.ICoreRechargeOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 支付成功
*/
@Slf4j
@EventListen("recharge")
@Component
public class PaySuccessListener extends AbstractListener {
@Resource
ICoreRechargeOrderService coreRechargeOrderService;
public void handleEvent(PaySuccessEvent event) {
log.info("PaySuccessListener handleEvent: {}", JSONUtil.toJsonStr(event));
if (event.getTradeType().equals("recharge")) {
coreRechargeOrderService.pay(event.getPay());
}
}
}

View File

@ -0,0 +1,26 @@
package com.niu.recharge.listener;
import com.niu.core.common.annotation.EventListen;
import com.niu.core.common.component.context.listener.AbstractListener;
import com.niu.core.event.refund.RefundSuccessEvent;
import com.niu.recharge.service.core.ICoreRechargeRefundService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 支付成功
*/
@EventListen("recharge")
@Component
public class RefundSuccessListener extends AbstractListener {
@Resource
ICoreRechargeRefundService coreRechargeRefundService;
public void handleEvent(RefundSuccessEvent event) {
if (event.getTradeType().equals("recharge")) {
coreRechargeRefundService.refundComplete(event.getRefund().getRefundNo());
}
}
}

View File

@ -0,0 +1,12 @@
package com.niu.recharge.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.niu.recharge.entity.Recharge;
import org.apache.ibatis.annotations.Mapper;
/**
* 会员充值套餐 Mapper
*/
@Mapper
public interface RechargeMapper extends MPJBaseMapper<Recharge> {
}

View File

@ -0,0 +1,9 @@
package com.niu.recharge.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.niu.recharge.entity.RechargeOrderItem;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RechargeOrderItemMapper extends BaseMapper<RechargeOrderItem> {
}

View File

@ -0,0 +1,10 @@
package com.niu.recharge.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.niu.recharge.entity.RechargeOrderItemRefund;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RechargeOrderItemRefundMapper extends BaseMapper<RechargeOrderItemRefund> {
}

View File

@ -0,0 +1,10 @@
package com.niu.recharge.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.niu.recharge.entity.RechargeOrderLog;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RechargeOrderLogMapper extends BaseMapper<RechargeOrderLog> {
}

View File

@ -0,0 +1,9 @@
package com.niu.recharge.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.niu.recharge.entity.RechargeOrder;
import org.mapstruct.Mapper;
@Mapper
public interface RechargeOrderMapper extends BaseMapper<RechargeOrder> {
}

View File

@ -0,0 +1,32 @@
package com.niu.recharge.service.admin;
import com.niu.core.common.domain.PageParam;
import com.niu.core.common.domain.PageResult;
import com.niu.recharge.service.admin.param.RechargeOrderSearchParam;
import com.niu.recharge.service.admin.vo.RechargeOrderInfoVo;
import com.niu.recharge.service.admin.vo.RechargeOrderListVo;
import com.niu.recharge.service.admin.vo.RechargeOrderStatVo;
import com.niu.recharge.service.core.param.RechargeOrderCreateParam;
import com.niu.recharge.service.core.vo.RechargeOrderCreateVo;
public interface IRechargeOrderService {
/**
* 订单列表
*
* @param pageParam
* @param searchParam
* @return
*/
PageResult<RechargeOrderListVo> list(PageParam pageParam, RechargeOrderSearchParam searchParam);
/**
* 充值订单详情
*
* @param orderId
* @return
*/
RechargeOrderInfoVo detail(Integer orderId);
RechargeOrderStatVo stat(RechargeOrderSearchParam searchParam);
}

View File

@ -0,0 +1,33 @@
package com.niu.recharge.service.admin;
import com.niu.core.common.domain.PageParam;
import com.niu.core.common.domain.PageResult;
import com.niu.recharge.service.admin.param.RechargeRefundSearchParam;
import com.niu.recharge.service.admin.vo.*;
public interface IRechargeRefundService {
/**
* 退款列表
*
* @param pageParam
* @param searchParam
* @return
*/
PageResult<RechargeRefundListVo> list(PageParam pageParam, RechargeRefundSearchParam searchParam);
/**
* 发起退款
*/
void refund(Integer orderId);
/**
* 充值退款详情
*
* @param refundId
* @return
*/
RechargeRefundInfoVo detail(Integer refundId);
RechargeRefundStatVo stat(RechargeRefundSearchParam searchParam);
}

View File

@ -0,0 +1,35 @@
package com.niu.recharge.service.admin;
import cn.hutool.json.JSONArray;
import com.niu.core.common.domain.PageParam;
import com.niu.core.common.domain.PageResult;
import com.niu.recharge.service.admin.param.RechargeEditSortParam;
import com.niu.recharge.service.admin.param.RechargeModifyStatusParam;
import com.niu.recharge.service.admin.param.RechargePageParam;
import com.niu.recharge.service.admin.param.RechargeParam;
import com.niu.recharge.service.admin.vo.RechargeInitVo;
import com.niu.recharge.service.admin.vo.RechargeListVo;
import com.niu.recharge.service.core.param.RechargeSetConfigParam;
import com.niu.recharge.service.core.vo.RechargeConfigVo;
public interface IRechargeService {
void setRechargeConfig(RechargeSetConfigParam param);
RechargeConfigVo getRechargeConfig();
PageResult<RechargeListVo> getPage(PageParam pageParam, RechargePageParam param);
void add(RechargeParam param);
void edit(Integer id, RechargeParam param);
RechargeInitVo getInit(Integer rechargeId);
void del(Integer id);
void modifyStatus(RechargeModifyStatusParam param);
void editSort(RechargeEditSortParam param);
JSONArray getPackageGiftDict();
}

View File

@ -0,0 +1,149 @@
package com.niu.recharge.service.admin.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.niu.core.common.domain.PageParam;
import com.niu.core.common.domain.PageResult;
import com.niu.core.common.utils.RequestUtils;
import com.niu.core.common.utils.mapper.QueryMapperUtils;
import com.niu.core.entity.member.Member;
import com.niu.core.mapper.member.MemberMapper;
import com.niu.core.service.admin.member.vo.MemberBriefInfoVo;
import com.niu.recharge.entity.RechargeOrder;
import com.niu.recharge.entity.RechargeOrderItem;
import com.niu.recharge.enums.OrderStatusEnum;
import com.niu.recharge.enums.RefundStatusEnum;
import com.niu.recharge.mapper.RechargeOrderItemMapper;
import com.niu.recharge.mapper.RechargeOrderMapper;
import com.niu.recharge.service.admin.IRechargeOrderService;
import com.niu.recharge.service.admin.param.RechargeOrderSearchParam;
import com.niu.recharge.service.admin.vo.RechargeOrderInfoVo;
import com.niu.recharge.service.admin.vo.RechargeOrderListVo;
import com.niu.recharge.service.admin.vo.RechargeOrderStatVo;
import com.niu.recharge.service.core.ICoreRechargeOrderService;
import com.niu.recharge.service.core.param.RechargeOrderCreateParam;
import com.niu.recharge.service.core.vo.RechargeOrderCreateVo;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
@Service
public class RechargeOrderServiceImpl implements IRechargeOrderService {
@Resource
ICoreRechargeOrderService coreRechargeOrderService;
@Resource
RechargeOrderMapper rechargeOrderMapper;
@Resource
RechargeOrderItemMapper rechargeOrderItemMapper;
@Resource
MemberMapper memberMapper;
/**
* 订单列表
*
* @param pageParam
* @param searchParam
* @return
*/
@Override
public PageResult<RechargeOrderListVo> list(PageParam pageParam, RechargeOrderSearchParam searchParam) {
Integer page = pageParam.getPage();
Integer limit = pageParam.getLimit();
QueryWrapper<RechargeOrder> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("site_id", RequestUtils.siteId());
queryWrapper.orderByDesc("create_time");
if (ObjectUtil.isNotEmpty(searchParam.getOrderNo())) queryWrapper.eq("order_no", searchParam.getOrderNo());
if (ObjectUtil.isNotEmpty(searchParam.getOrderStatus())) queryWrapper.eq("order_status", searchParam.getOrderStatus());
if (ObjectUtil.isNotEmpty(searchParam.getOrderFrom())) queryWrapper.eq("order_from", searchParam.getOrderFrom());
if (ObjectUtil.isNotEmpty(searchParam.getCreateTime())) QueryMapperUtils.buildByTime(queryWrapper, "create_time", searchParam.getCreateTime());
if (ObjectUtil.isNotEmpty(searchParam.getPayTime())) QueryMapperUtils.buildByTime(queryWrapper, "pay_time", searchParam.getPayTime());
if (searchParam.getStartMoney() > 0) queryWrapper.ge("order_money", searchParam.getStartMoney());
if (searchParam.getEndMoney() > 0) queryWrapper.le("order_money", searchParam.getEndMoney());
IPage<RechargeOrder> iPage = rechargeOrderMapper.selectPage(new Page<RechargeOrder>(page, limit), queryWrapper);
List<RechargeOrderListVo> list = new LinkedList<>();
for(RechargeOrder item : iPage.getRecords()) {
RechargeOrderListVo vo = new RechargeOrderListVo();
BeanUtils.copyProperties(item, vo);
MemberBriefInfoVo memberInfo = new MemberBriefInfoVo();
Member member = memberMapper.selectOne(new QueryWrapper<Member>().eq("member_id", item.getMemberId()));
if (member != null) BeanUtils.copyProperties(member, memberInfo);
vo.setMember(memberInfo);
List<RechargeOrderItem> itemList = rechargeOrderItemMapper.selectList(new QueryWrapper<RechargeOrderItem>().eq("order_id", item.getOrderId()));
vo.setItem(itemList);
list.add(vo);
}
return PageResult.build(page, limit, iPage.getTotal()).setData(list);
}
/**
* 充值订单详情
*
* @param orderId
* @return
*/
@Override
public RechargeOrderInfoVo detail(Integer orderId) {
QueryWrapper<RechargeOrder> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_id", orderId);
queryWrapper.eq("site_id", RequestUtils.siteId());
RechargeOrder order = rechargeOrderMapper.selectOne(queryWrapper);
if (order == null) return null;
RechargeOrderInfoVo vo = new RechargeOrderInfoVo();
BeanUtils.copyProperties(order, vo);
MemberBriefInfoVo memberInfo = new MemberBriefInfoVo();
Member member = memberMapper.selectOne(new QueryWrapper<Member>().eq("member_id", order.getMemberId()));
if (member != null) BeanUtils.copyProperties(member, memberInfo);
vo.setMember(memberInfo);
List<RechargeOrderItem> itemList = rechargeOrderItemMapper.selectList(new QueryWrapper<RechargeOrderItem>().eq("order_id", order.getOrderId()));
vo.setItem(itemList);
return vo;
}
/**
* 统计
*
* @param searchParam
* @return
*/
public RechargeOrderStatVo stat(RechargeOrderSearchParam searchParam) {
RechargeOrderStatVo vo = new RechargeOrderStatVo();
QueryWrapper rechargeMoneyQueryWrapper = new QueryWrapper<RechargeOrder>();
rechargeMoneyQueryWrapper.select("SUM(order_money) AS order_money");
rechargeMoneyQueryWrapper.eq("site_id", RequestUtils.siteId());
rechargeMoneyQueryWrapper.eq("order_status", OrderStatusEnum.FINISH.getStatus());
if (searchParam.getMemberId() > 0) rechargeMoneyQueryWrapper.eq("member_id", searchParam.getMemberId());
rechargeMoneyQueryWrapper.last("limit 1");
RechargeOrder rechargeMoney = rechargeOrderMapper.selectOne(rechargeMoneyQueryWrapper);
if (rechargeMoney != null) vo.setRechargeMoney(rechargeMoney.getOrderMoney());
QueryWrapper refundMoneyQueryWrapper = new QueryWrapper<RechargeOrder>();
refundMoneyQueryWrapper.select("SUM(order_money) AS order_money");
refundMoneyQueryWrapper.eq("site_id", RequestUtils.siteId());
refundMoneyQueryWrapper.eq("refund_status", RefundStatusEnum.REFUND_COMPLETED.getStatus());
if (searchParam.getMemberId() > 0) refundMoneyQueryWrapper.eq("member_id", searchParam.getMemberId());
refundMoneyQueryWrapper.last("limit 1");
RechargeOrder refundeMoney = rechargeOrderMapper.selectOne(refundMoneyQueryWrapper);
if (refundeMoney != null) vo.setRechargeRefundMoney(refundeMoney.getOrderMoney());
return vo;
}
}

View File

@ -0,0 +1,162 @@
package com.niu.recharge.service.admin.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.niu.core.common.domain.PageParam;
import com.niu.core.common.domain.PageResult;
import com.niu.core.common.utils.RequestUtils;
import com.niu.core.common.utils.mapper.QueryMapperUtils;
import com.niu.core.entity.member.Member;
import com.niu.core.mapper.member.MemberMapper;
import com.niu.core.service.admin.member.vo.MemberBriefInfoVo;
import com.niu.recharge.entity.RechargeOrderItem;
import com.niu.recharge.entity.RechargeOrderItemRefund;
import com.niu.recharge.enums.RefundStatusEnum;
import com.niu.recharge.mapper.RechargeOrderItemMapper;
import com.niu.recharge.mapper.RechargeOrderItemRefundMapper;
import com.niu.recharge.service.admin.IRechargeRefundService;
import com.niu.recharge.service.admin.param.RechargeRefundSearchParam;
import com.niu.recharge.service.admin.vo.*;
import com.niu.recharge.service.core.ICoreRechargeRefundService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
@Service
public class RechargeRefundServiceImpl implements IRechargeRefundService {
@Resource
RechargeOrderItemRefundMapper rechargeOrderItemRefundMapper;
@Resource
RechargeOrderItemMapper rechargeOrderItemMapper;
@Resource
MemberMapper memberMapper;
@Resource
ICoreRechargeRefundService coreRechargeRefundService;
/**
* 订单列表
*
* @param pageParam
* @param searchParam
* @return
*/
@Override
public PageResult<RechargeRefundListVo> list(PageParam pageParam, RechargeRefundSearchParam searchParam) {
Integer page = pageParam.getPage();
Integer limit = pageParam.getLimit();
QueryWrapper<RechargeOrderItemRefund> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("site_id", RequestUtils.siteId());
queryWrapper.orderByDesc("create_time");
if (ObjectUtil.isNotEmpty(searchParam.getOrderNo())) queryWrapper.eq("order_no", searchParam.getOrderNo());
if (ObjectUtil.isNotEmpty(searchParam.getOrderStatus())) queryWrapper.eq("order_status", searchParam.getOrderStatus());
if (ObjectUtil.isNotEmpty(searchParam.getOrderFrom())) queryWrapper.eq("order_from", searchParam.getOrderFrom());
if (ObjectUtil.isNotEmpty(searchParam.getCreateTime())) QueryMapperUtils.buildByTime(queryWrapper, "create_time", searchParam.getCreateTime());
if (ObjectUtil.isNotEmpty(searchParam.getPayTime())) QueryMapperUtils.buildByTime(queryWrapper, "pay_time", searchParam.getPayTime());
if (searchParam.getStartMoney() > 0) queryWrapper.ge("order_money", searchParam.getStartMoney());
if (searchParam.getEndMoney() > 0) queryWrapper.le("order_money", searchParam.getEndMoney());
IPage<RechargeOrderItemRefund> iPage = rechargeOrderItemRefundMapper.selectPage(new Page<RechargeOrderItemRefund>(page, limit), queryWrapper);
List<RechargeRefundListVo> list = new LinkedList<>();
for(RechargeOrderItemRefund item : iPage.getRecords()) {
RechargeRefundListVo vo = new RechargeRefundListVo();
BeanUtils.copyProperties(item, vo);
MemberBriefInfoVo memberInfo = new MemberBriefInfoVo();
Member member = memberMapper.selectOne(new QueryWrapper<Member>().eq("member_id", item.getMemberId()));
if (member != null) BeanUtils.copyProperties(member, memberInfo);
vo.setMember(memberInfo);
RechargeOrderItem orderItem = rechargeOrderItemMapper.selectOne(new QueryWrapper<RechargeOrderItem>().eq("order_item_id", item.getOrderItemId()));
vo.setItem(orderItem);
list.add(vo);
}
return PageResult.build(page, limit, iPage.getTotal()).setData(list);
}
/**
* 发起退款
*
*/
public void refund(Integer orderId) {
coreRechargeRefundService.create(orderId);
}
/**
* 充值订单详情
*
* @param refundId
* @return
*/
@Override
public RechargeRefundInfoVo detail(Integer refundId) {
QueryWrapper<RechargeOrderItemRefund> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("refund_id", refundId);
queryWrapper.eq("site_id", RequestUtils.siteId());
RechargeOrderItemRefund refund = rechargeOrderItemRefundMapper.selectOne(queryWrapper);
if (refund == null) return null;
RechargeRefundInfoVo vo = new RechargeRefundInfoVo();
BeanUtils.copyProperties(refund, vo);
MemberBriefInfoVo memberInfo = new MemberBriefInfoVo();
Member member = memberMapper.selectOne(new QueryWrapper<Member>().eq("member_id", refund.getMemberId()));
if (member != null) BeanUtils.copyProperties(member, memberInfo);
vo.setMember(memberInfo);
RechargeOrderItem item = rechargeOrderItemMapper.selectOne(new QueryWrapper<RechargeOrderItem>().eq("order_item_id", refund.getOrderItemId()));
vo.setItem(item);
return vo;
}
/**
* 统计
*
* @param searchParam
* @return
*/
public RechargeRefundStatVo stat(RechargeRefundSearchParam searchParam) {
RechargeRefundStatVo vo = new RechargeRefundStatVo();
QueryWrapper allMoneyQueryWrapper = new QueryWrapper<RechargeOrderItemRefund>();
allMoneyQueryWrapper.select("SUM(money) AS money");
allMoneyQueryWrapper.eq("site_id", RequestUtils.siteId());
allMoneyQueryWrapper.ne("status", RefundStatusEnum.REFUND_FAIL.getStatus());
if (searchParam.getMemberId() > 0) allMoneyQueryWrapper.eq("member_id", searchParam.getMemberId());
allMoneyQueryWrapper.last("limit 1");
RechargeOrderItemRefund allMoney = rechargeOrderItemRefundMapper.selectOne(allMoneyQueryWrapper);
RechargeRefundStatVo.Stat all = new RechargeRefundStatVo.Stat();
all.setKey("all");
all.setName("累计退款金额(元)");
if (allMoney != null)all.setMoney(allMoney.getMoney());
vo.setAll(all);
QueryWrapper haveMoneyQueryWrapper = new QueryWrapper<RechargeOrderItemRefund>();
haveMoneyQueryWrapper.select("SUM(money) AS money");
haveMoneyQueryWrapper.eq("site_id", RequestUtils.siteId());
haveMoneyQueryWrapper.eq("status", RefundStatusEnum.REFUNDING.getStatus());
if (searchParam.getMemberId() > 0) haveMoneyQueryWrapper.eq("member_id", searchParam.getMemberId());
haveMoneyQueryWrapper.last("limit 1");
RechargeOrderItemRefund haveMoney = rechargeOrderItemRefundMapper.selectOne(haveMoneyQueryWrapper);
RechargeRefundStatVo.Stat have = new RechargeRefundStatVo.Stat();
have.setKey("have");
have.setName("退款中金额(元)");
if (haveMoney != null) have.setMoney(haveMoney.getMoney());
vo.setHave(have);
return vo;
}
}

View File

@ -0,0 +1,233 @@
package com.niu.recharge.service.admin.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.niu.core.common.domain.PageParam;
import com.niu.core.common.domain.PageResult;
import com.niu.core.common.utils.RequestUtils;
import com.niu.core.common.utils.date.DateUtils;
import com.niu.core.common.utils.mapper.QueryMapperUtils;
import com.niu.core.service.core.app.helper.EventAndSubscribeOfPublisher;
import com.niu.recharge.entity.Recharge;
import com.niu.recharge.enums.PackageGiftEnum;
import com.niu.recharge.event.RechargeGiftContentEvent;
import com.niu.recharge.mapper.RechargeMapper;
import com.niu.recharge.service.admin.IRechargeService;
import com.niu.recharge.service.admin.param.RechargeEditSortParam;
import com.niu.recharge.service.admin.param.RechargeModifyStatusParam;
import com.niu.recharge.service.admin.param.RechargePageParam;
import com.niu.recharge.service.admin.param.RechargeParam;
import com.niu.recharge.service.admin.vo.RechargeInitVo;
import com.niu.recharge.service.admin.vo.RechargeListVo;
import com.niu.recharge.service.core.ICoreRechargeConfigService;
import com.niu.recharge.service.core.param.RechargeSetConfigParam;
import com.niu.recharge.service.core.vo.RechargeConfigVo;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.Map;
@Service
@RequiredArgsConstructor
public class RechargeServiceImpl implements IRechargeService {
private final RechargeMapper rechargeMapper;
private final ICoreRechargeConfigService coreRechargeConfigService;
@Override
public void setRechargeConfig(RechargeSetConfigParam param) {
coreRechargeConfigService.setRechargeConfig(RequestUtils.siteId(), param);
}
@Override
public RechargeConfigVo getRechargeConfig() {
return coreRechargeConfigService.getRechargeConfig(RequestUtils.siteId());
}
@Override
public PageResult<RechargeListVo> getPage(PageParam pageParam, RechargePageParam param) {
QueryWrapper<Recharge> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("site_id", RequestUtils.siteId())
.orderByDesc("create_time");
if (ObjectUtil.isNotEmpty(param.getRechargeName())) {
queryWrapper.like("recharge_name", param.getRechargeName());
}
if (ObjectUtil.isNotEmpty(param.getCreateTime())) {
QueryMapperUtils.buildByTime(queryWrapper, "create_time", param.getCreateTime());
}
Page<Recharge> iPage = rechargeMapper.selectPage(new Page<>(pageParam.getPage(), pageParam.getLimit()), queryWrapper);
if (ObjectUtil.isEmpty(iPage.getRecords())) {
return PageResult.build(iPage, Collections.emptyList());
}
IPage<RechargeListVo> voPage = iPage.convert(recharge -> {
RechargeListVo vo = new RechargeListVo();
BeanUtils.copyProperties(recharge, vo);
vo.setCreateTime(DateUtils.timestampToString(recharge.getCreateTime()));
JSONObject gift = JSONUtil.parseObj(recharge.getGiftJson());
for (Map.Entry<String, Object> entry : gift.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
RechargeGiftContentEvent event = new RechargeGiftContentEvent();
event.setKey(key);
event.setValue(JSONUtil.parseObj(value).get("value"));
vo.setGiftContent(EventAndSubscribeOfPublisher.publishAndCallback(event));
}
return vo;
});
return PageResult.build(voPage);
}
@Override
public void add(RechargeParam param) {
QueryWrapper<Recharge> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("site_id", RequestUtils.siteId())
.eq("face_value", param.getFaceValue());
long count = rechargeMapper.selectCount(queryWrapper);
if (count > 0) {
throw new RuntimeException("充值金额不能重复");
}
param.setPoint(0);
param.setGrowth(0);
int growth = param.getPoint();
int point = param.getPoint();
JSONObject giftJson = JSONUtil.parseObj(param.getGiftJson());
if (ObjectUtil.isNotEmpty(giftJson)) {
if (giftJson.containsKey("growth")) {
Integer value = giftJson.getJSONObject("growth").getInt("value");
if (ObjectUtil.isNotEmpty(value)) {
growth = value;
giftJson.remove("growth");
}
}
if (giftJson.containsKey("point")) {
Integer value = giftJson.getJSONObject("point").getInt("value");
if (ObjectUtil.isNotEmpty(point)) {
point = value;
giftJson.remove("point");
}
}
}
Recharge recharge = new Recharge();
BeanUtils.copyProperties(param, recharge);
recharge.setSiteId(RequestUtils.siteId());
recharge.setPoint(point);
recharge.setGrowth(growth);
recharge.setCreateTime(System.currentTimeMillis() / 1000);
recharge.setGiftJson(giftJson.toString());
rechargeMapper.insert(recharge);
}
@Override
public void edit(Integer id, RechargeParam param) {
Recharge recharge = rechargeMapper.selectById(id);
if (ObjectUtil.isEmpty(recharge)) {
throw new RuntimeException("充值套餐不存在");
}
QueryWrapper<Recharge> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("site_id", RequestUtils.siteId())
.ne("recharge_id", id)
.eq("face_value", param.getFaceValue());
Long count = rechargeMapper.selectCount(queryWrapper);
if (count > 0) {
throw new RuntimeException("充值金额不能重复");
}
param.setPoint(0);
param.setGrowth(0);
int growth = param.getPoint();
JSONObject giftJson = JSONUtil.parseObj(param.getGiftJson());
Integer value = giftJson.getJSONObject("growth").getInt("value");
if (ObjectUtil.isNotEmpty(value)) {
growth = value;
giftJson.remove("growth");
}
int point = param.getPoint();
value = giftJson.getJSONObject("point").getInt("value");
if (ObjectUtil.isNotEmpty(point)) {
point = value;
giftJson.remove("point");
}
BeanUtils.copyProperties(param, recharge);
recharge.setGrowth(growth);
recharge.setPoint(point);
recharge.setGiftJson(giftJson.toString());
recharge.setUpdateTime(System.currentTimeMillis() / 1000);
rechargeMapper.updateById(recharge);
}
@Override
public RechargeInitVo getInit(Integer rechargeId) {
if (rechargeId == null) {
return new RechargeInitVo();
}
Recharge recharge = rechargeMapper.selectById(rechargeId);
RechargeInitVo vo = new RechargeInitVo();
BeanUtils.copyProperties(recharge, vo);
vo.setCreateTime(DateUtils.timestampToString(recharge.getCreateTime()));
vo.setGiftJson(JSONUtil.parseObj(recharge.getGiftJson()));
vo.setStatusName(ObjectUtil.equal(recharge.getStatus(), 1) ? "开启" : "关闭");
if (ObjectUtil.isNotEmpty(vo)) {
if (ObjectUtil.isNotEmpty(vo.getPoint())) {
vo.getGiftJson().set("point", Map.of("value", vo.getPoint()));
}
if (ObjectUtil.isNotEmpty(vo.getGrowth())) {
vo.getGiftJson().set("growth", Map.of("value", vo.getGrowth()));
}
}
return vo;
}
@Override
public void del(Integer id) {
rechargeMapper.deleteById(id);
}
@Override
public void modifyStatus(RechargeModifyStatusParam param) {
Recharge recharge = rechargeMapper.selectById(param.getRechargeId());
if (ObjectUtil.isEmpty(recharge)) {
throw new RuntimeException("充值套餐不存在");
}
recharge.setStatus(param.getStatus());
rechargeMapper.updateById(recharge);
}
@Override
public void editSort(RechargeEditSortParam param) {
Recharge recharge = rechargeMapper.selectById(param.getRechargeId());
if (ObjectUtil.isEmpty(recharge)) {
throw new RuntimeException("充值套餐不存在");
}
recharge.setSort(param.getSort());
rechargeMapper.updateById(recharge);
}
@Override
public JSONArray getPackageGiftDict() {
return PackageGiftEnum.getPackageGiftEnum();
}
}

View File

@ -0,0 +1,9 @@
package com.niu.recharge.service.admin.param;
import lombok.Data;
@Data
public class RechargeEditSortParam {
private Integer rechargeId;
private Integer sort;
}

View File

@ -0,0 +1,9 @@
package com.niu.recharge.service.admin.param;
import lombok.Data;
@Data
public class RechargeModifyStatusParam {
private Integer rechargeId;
private Integer status;
}

View File

@ -0,0 +1,27 @@
package com.niu.recharge.service.admin.param;
import lombok.Data;
import java.io.Serializable;
@Data
public class RechargeOrderSearchParam implements Serializable {
private static final long serialVersionUID = 1L;
private String orderNo;
private Integer orderStatus;
private String orderFrom;
private String[] createTime;
private String[] payTime;
private float startMoney;
private float endMoney;
private Integer memberId = 0;
}

View File

@ -0,0 +1,9 @@
package com.niu.recharge.service.admin.param;
import lombok.Data;
@Data
public class RechargePageParam {
private String rechargeName;
private String[] createTime;
}

View File

@ -0,0 +1,23 @@
package com.niu.recharge.service.admin.param;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class RechargeParam {
private Integer rechargeId;
private Integer siteId;
private String rechargeName;
private BigDecimal faceValue;
private BigDecimal buyPrice;
private Integer point;
private Integer growth;
private String couponId;
private Integer saleNum;
private Integer sort;
private Integer status;
private String createTime;
private String updateTime;
private Object giftJson;
}

View File

@ -0,0 +1,27 @@
package com.niu.recharge.service.admin.param;
import lombok.Data;
import java.io.Serializable;
@Data
public class RechargeRefundSearchParam implements Serializable {
private static final long serialVersionUID = 1L;
private String orderNo;
private Integer orderStatus;
private String orderFrom;
private String[] createTime;
private String[] payTime;
private float startMoney;
private float endMoney;
private Integer memberId = 0;
}

View File

@ -0,0 +1,26 @@
package com.niu.recharge.service.admin.vo;
import cn.hutool.json.JSONObject;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@NoArgsConstructor
@Data
public class RechargeInitVo {
private Integer rechargeId;
private Integer siteId;
private String rechargeName;
private BigDecimal faceValue;
private BigDecimal buyPrice;
private Integer point;
private Integer growth;
private String couponId;
private Integer saleNum;
private Integer sort;
private Integer status;
private String createTime;
private JSONObject giftJson;
private String statusName;
}

View File

@ -0,0 +1,26 @@
package com.niu.recharge.service.admin.vo;
import cn.hutool.json.JSONObject;
import com.niu.recharge.service.core.vo.RechargeGiftContentVo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
public class RechargeListVo {
private Integer rechargeId;
private Integer siteId;
private String rechargeName;
private BigDecimal faceValue;
private BigDecimal buyPrice;
private Integer point;
private Integer growth;
private String couponId;
private Integer saleNum;
private Integer sort;
private Integer status;
private String createTime;
private JSONObject giftJson;
private List<RechargeGiftContentVo> giftContent;
}

View File

@ -0,0 +1,55 @@
package com.niu.recharge.service.admin.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.niu.core.common.domain.BeanJsonSerializer;
import com.niu.core.enums.common.ChannelEnum;
import com.niu.core.service.admin.member.vo.MemberBriefInfoVo;
import com.niu.recharge.entity.RechargeOrder;
import com.niu.recharge.entity.RechargeOrderItem;
import com.niu.recharge.enums.OrderStatusEnum;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@Data
public class RechargeOrderInfoVo extends RechargeOrder implements Serializable {
private static final long serialVersionUID = 1L;
private List<RechargeOrderItem> item;
private Status orderStatusInfo;
@JsonSerialize(using = BeanJsonSerializer.BigDecimalFormatSerializer.class)
private BigDecimal orderMoney;
@JsonSerialize(using = BeanJsonSerializer.LongDateToStringSerializer.class)
private Long createTime;
@JsonSerialize(using = BeanJsonSerializer.LongDateToStringSerializer.class)
private Long payTime;
private MemberBriefInfoVo member;
private String orderFromName;
@Data
private static class Status {
private String name;
private Integer status;
}
public Status getOrderStatusInfo() {
Status status = new Status();
status.setStatus(this.getOrderStatus());
status.setName(OrderStatusEnum.getNameByStatus(this.getOrderStatus()));
return status;
}
public String getOrderFromName() {
return ChannelEnum.getNameByCode(this.getOrderFrom());
}
}

View File

@ -0,0 +1,55 @@
package com.niu.recharge.service.admin.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.niu.core.common.domain.BeanJsonSerializer;
import com.niu.core.enums.common.ChannelEnum;
import com.niu.core.service.admin.member.vo.MemberBriefInfoVo;
import com.niu.recharge.entity.RechargeOrder;
import com.niu.recharge.entity.RechargeOrderItem;
import com.niu.recharge.enums.OrderStatusEnum;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@Data
public class RechargeOrderListVo extends RechargeOrder implements Serializable {
private static final long serialVersionUID = 1L;
private List<RechargeOrderItem> item;
private Status orderStatusInfo;
@JsonSerialize(using = BeanJsonSerializer.BigDecimalFormatSerializer.class)
private BigDecimal orderMoney;
@JsonSerialize(using = BeanJsonSerializer.LongDateToStringSerializer.class)
private Long createTime;
@JsonSerialize(using = BeanJsonSerializer.LongDateToStringSerializer.class)
private Long payTime;
private MemberBriefInfoVo member;
private String orderFromName;
@Data
private static class Status {
private String name;
private Integer status;
}
public Status getOrderStatusInfo() {
Status status = new Status();
status.setStatus(this.getOrderStatus());
status.setName(OrderStatusEnum.getNameByStatus(this.getOrderStatus()));
return status;
}
public String getOrderFromName() {
return ChannelEnum.getNameByCode(this.getOrderFrom());
}
}

View File

@ -0,0 +1,26 @@
package com.niu.recharge.service.admin.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.niu.core.common.domain.BeanJsonSerializer;
import com.niu.core.enums.common.ChannelEnum;
import com.niu.core.service.admin.member.vo.MemberBriefInfoVo;
import com.niu.recharge.entity.RechargeOrder;
import com.niu.recharge.entity.RechargeOrderItem;
import com.niu.recharge.enums.OrderStatusEnum;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@Data
public class RechargeOrderStatVo implements Serializable {
private static final long serialVersionUID = 1L;
@JsonSerialize(using = BeanJsonSerializer.BigDecimalFormatSerializer.class)
private BigDecimal rechargeMoney = new BigDecimal(0);
@JsonSerialize(using = BeanJsonSerializer.BigDecimalFormatSerializer.class)
private BigDecimal rechargeRefundMoney = new BigDecimal(0);
}

View File

@ -0,0 +1,28 @@
package com.niu.recharge.service.admin.vo;
import com.niu.core.service.admin.member.vo.MemberBriefInfoVo;
import com.niu.recharge.entity.RechargeOrderItem;
import com.niu.recharge.entity.RechargeOrderItemRefund;
import com.niu.recharge.enums.RefundStatusEnum;
import lombok.Data;
import java.io.Serializable;
@Data
public class RechargeRefundInfoVo extends RechargeOrderItemRefund implements Serializable {
private static final long serialVersionUID = 1L;
private RechargeOrderItem item;
private String statusName;
private MemberBriefInfoVo member;
private String orderFromName;
public String getStatusName() {
return RefundStatusEnum.getNameByStatus(this.getStatus());
}
}

View File

@ -0,0 +1,35 @@
package com.niu.recharge.service.admin.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.niu.core.common.domain.BeanJsonSerializer;
import com.niu.core.enums.common.ChannelEnum;
import com.niu.core.service.admin.member.vo.MemberBriefInfoVo;
import com.niu.recharge.entity.RechargeOrder;
import com.niu.recharge.entity.RechargeOrderItem;
import com.niu.recharge.entity.RechargeOrderItemRefund;
import com.niu.recharge.enums.OrderStatusEnum;
import com.niu.recharge.enums.RefundStatusEnum;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@Data
public class RechargeRefundListVo extends RechargeOrderItemRefund implements Serializable {
private static final long serialVersionUID = 1L;
private RechargeOrderItem item;
private String statusName;
private MemberBriefInfoVo member;
private String orderFromName;
public String getStatusName() {
return RefundStatusEnum.getNameByStatus(this.getStatus());
}
}

View File

@ -0,0 +1,28 @@
package com.niu.recharge.service.admin.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.niu.core.common.domain.BeanJsonSerializer;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class RechargeRefundStatVo implements Serializable {
private static final long serialVersionUID = 1L;
private Stat all;
private Stat have;
@Data
public static class Stat {
private String name;
private String key;
@JsonSerialize(using = BeanJsonSerializer.BigDecimalFormatSerializer.class)
private BigDecimal money = new BigDecimal(0);
}
}

View File

@ -0,0 +1,36 @@
package com.niu.recharge.service.api;
import com.niu.core.common.domain.PageParam;
import com.niu.core.common.domain.PageResult;
import com.niu.recharge.service.api.param.RechargeOrderSearchParam;
import com.niu.recharge.service.api.vo.RechargeOrderInfoVo;
import com.niu.recharge.service.api.vo.RechargeOrderListVo;
import com.niu.recharge.service.core.param.RechargeOrderCreateParam;
import com.niu.recharge.service.core.vo.RechargeOrderCreateVo;
public interface IRechargeOrderService {
/**
* 创建订单
*
* @param param
*/
RechargeOrderCreateVo create(RechargeOrderCreateParam param);
/**
* 订单列表
*
* @param pageParam
* @param searchParam
* @return
*/
PageResult<RechargeOrderListVo> list(PageParam pageParam, RechargeOrderSearchParam searchParam);
/**
* 充值订单详情
*
* @param orderId
* @return
*/
RechargeOrderInfoVo detail(Integer orderId);
}

View File

@ -0,0 +1,12 @@
package com.niu.recharge.service.api;
import com.niu.recharge.service.api.vo.RechargeListVo;
import com.niu.recharge.service.core.vo.RechargeConfigVo;
import java.util.List;
public interface IRechargeService {
RechargeConfigVo getRechargeConfig();
List<RechargeListVo> getRechargeLists();
}

View File

@ -0,0 +1,112 @@
package com.niu.recharge.service.api.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.niu.core.common.domain.PageParam;
import com.niu.core.common.domain.PageResult;
import com.niu.core.common.utils.RequestUtils;
import com.niu.recharge.entity.RechargeOrder;
import com.niu.recharge.entity.RechargeOrderItem;
import com.niu.recharge.mapper.RechargeOrderItemMapper;
import com.niu.recharge.mapper.RechargeOrderMapper;
import com.niu.recharge.service.api.IRechargeOrderService;
import com.niu.recharge.service.api.param.RechargeOrderSearchParam;
import com.niu.recharge.service.api.vo.RechargeOrderInfoVo;
import com.niu.recharge.service.api.vo.RechargeOrderListVo;
import com.niu.recharge.service.core.ICoreRechargeOrderService;
import com.niu.recharge.service.core.param.RechargeOrderCreateParam;
import com.niu.recharge.service.core.vo.RechargeOrderCreateVo;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
@Service
public class RechargeOrderServiceImpl implements IRechargeOrderService {
@Resource
ICoreRechargeOrderService coreRechargeOrderService;
@Resource
RechargeOrderMapper rechargeOrderMapper;
@Resource
RechargeOrderItemMapper rechargeOrderItemMapper;
/**
* 创建订单
*
* @param param
* @return
*/
@Override
public RechargeOrderCreateVo create(RechargeOrderCreateParam param) {
param.setMemberId(RequestUtils.memberId());
param.setSiteId(RequestUtils.siteId());
return coreRechargeOrderService.create(param);
}
/**
* 订单列表
*
* @param pageParam
* @param searchParam
* @return
*/
@Override
public PageResult<RechargeOrderListVo> list(PageParam pageParam, RechargeOrderSearchParam searchParam) {
Integer page = pageParam.getPage();
Integer limit = pageParam.getLimit();
QueryWrapper<RechargeOrder> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("site_id", RequestUtils.siteId());
queryWrapper.eq("member_id", RequestUtils.memberId());
queryWrapper.orderByDesc("create_time");
if (ObjectUtil.isNotEmpty(searchParam.getOrderStatus()))
queryWrapper.like("order_status", searchParam.getOrderStatus());
IPage<RechargeOrder> iPage = rechargeOrderMapper.selectPage(new Page<RechargeOrder>(page, limit), queryWrapper);
List<RechargeOrderListVo> list = new LinkedList<>();
for (RechargeOrder item : iPage.getRecords()) {
RechargeOrderListVo vo = new RechargeOrderListVo();
BeanUtils.copyProperties(item, vo);
List<RechargeOrderItem> itemList = rechargeOrderItemMapper.selectList(new QueryWrapper<RechargeOrderItem>().eq("order_id", item.getOrderId()));
vo.setItem(itemList);
list.add(vo);
}
return PageResult.build(page, limit, iPage.getTotal()).setData(list);
}
/**
* 充值订单详情
*
* @param orderId
* @return
*/
@Override
public RechargeOrderInfoVo detail(Integer orderId) {
QueryWrapper<RechargeOrder> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_id", orderId);
queryWrapper.eq("site_id", RequestUtils.siteId());
queryWrapper.eq("member_id", RequestUtils.memberId());
RechargeOrder order = rechargeOrderMapper.selectOne(queryWrapper);
if (order == null) return null;
RechargeOrderInfoVo vo = new RechargeOrderInfoVo();
BeanUtils.copyProperties(order, vo);
List<RechargeOrderItem> itemList = rechargeOrderItemMapper.selectList(new QueryWrapper<RechargeOrderItem>().eq("order_id", order.getOrderId()));
vo.setItem(itemList);
return vo;
}
}

View File

@ -0,0 +1,70 @@
package com.niu.recharge.service.api.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.niu.core.common.utils.RequestUtils;
import com.niu.core.service.core.app.helper.EventAndSubscribeOfPublisher;
import com.niu.recharge.entity.Recharge;
import com.niu.recharge.event.RechargeGiftContentEvent;
import com.niu.recharge.mapper.RechargeMapper;
import com.niu.recharge.service.api.IRechargeService;
import com.niu.recharge.service.api.vo.RechargeListVo;
import com.niu.recharge.service.core.ICoreRechargeConfigService;
import com.niu.recharge.service.core.vo.RechargeConfigVo;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class RechargeServiceImpl implements IRechargeService {
private final RechargeMapper rechargeMapper;
private final ICoreRechargeConfigService coreRechargeConfigService;
@Override
public RechargeConfigVo getRechargeConfig() {
return coreRechargeConfigService.getRechargeConfig(RequestUtils.siteId());
}
@Override
public List<RechargeListVo> getRechargeLists() {
QueryWrapper<Recharge> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("site_id", RequestUtils.siteId())
.eq("status", 1)
.orderByDesc("sort")
.orderByDesc("create_time");
List<Recharge> list = rechargeMapper.selectList(queryWrapper);
if (ObjectUtil.isEmpty(list)) {
return Collections.emptyList();
}
return list.stream()
.map(r -> {
RechargeListVo vo = new RechargeListVo();
BeanUtils.copyProperties(r, vo);
vo.setBuyPrice(Optional.ofNullable(r.getBuyPrice()).map(BigDecimal::toString).orElse(""));
vo.setFaceValue(Optional.ofNullable(r.getFaceValue()).map(BigDecimal::toString).orElse(""));
JSONObject gift = JSONUtil.parseObj(r.getGiftJson());
for (Map.Entry<String, Object> entry : gift.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
RechargeGiftContentEvent event = new RechargeGiftContentEvent();
event.setKey(key);
event.setValue(JSONUtil.parseObj(value).get("value"));
vo.setGiftContent(EventAndSubscribeOfPublisher.publishAndCallback(event));
}
return vo;
})
.toList();
}
}

View File

@ -0,0 +1,13 @@
package com.niu.recharge.service.api.param;
import lombok.Data;
import java.io.Serializable;
@Data
public class RechargeOrderSearchParam implements Serializable {
private static final long serialVersionUID = 1L;
private Integer orderStatus;
}

View File

@ -0,0 +1,23 @@
package com.niu.recharge.service.api.vo;
import cn.hutool.json.JSONObject;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class RechargeListInfo {
private Integer rechargeId;
private Integer siteId;
private String rechargeName;
private BigDecimal faceValue;
private BigDecimal buyPrice;
private Integer point;
private Integer growth;
private String couponId;
private Integer saleNum;
private Integer sort;
private Integer status;
private String createTime;
private JSONObject giftJson;
}

View File

@ -0,0 +1,26 @@
package com.niu.recharge.service.api.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@Data
public class RechargeListVo {
@JsonProperty("recharge_id")
private Integer rechargeId;
@JsonProperty("recharge_name")
private String rechargeName;
@JsonProperty("face_value")
private String faceValue;
@JsonProperty("buy_price")
private String buyPrice;
@JsonProperty("point")
private Integer point;
@JsonProperty("growth")
private Integer growth;
@JsonProperty("gift_content")
private List<?> giftContent;
}

View File

@ -0,0 +1,43 @@
package com.niu.recharge.service.api.vo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.niu.core.common.domain.BeanJsonSerializer;
import com.niu.recharge.entity.RechargeOrder;
import com.niu.recharge.entity.RechargeOrderItem;
import com.niu.recharge.enums.OrderStatusEnum;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@Data
public class RechargeOrderInfoVo extends RechargeOrder implements Serializable {
private static final long serialVersionUID = 1L;
private List<RechargeOrderItem> item;
private Status orderStatusInfo;
@JsonSerialize(using = BeanJsonSerializer.BigDecimalFormatSerializer.class)
private BigDecimal orderMoney;
@JsonSerialize(using = BeanJsonSerializer.LongDateToStringSerializer.class)
private Long createTime;
@Data
private static class Status {
private String name;
private Integer status;
}
public Status getOrderStatusInfo() {
Status status = new Status();
status.setStatus(this.getOrderStatus());
status.setName(OrderStatusEnum.getNameByStatus(this.getOrderStatus()));
return status;
}
}

View File

@ -0,0 +1,42 @@
package com.niu.recharge.service.api.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.niu.core.common.domain.BeanJsonSerializer;
import com.niu.recharge.entity.RechargeOrder;
import com.niu.recharge.entity.RechargeOrderItem;
import com.niu.recharge.enums.OrderStatusEnum;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@Data
public class RechargeOrderListVo extends RechargeOrder implements Serializable {
private static final long serialVersionUID = 1L;
private List<RechargeOrderItem> item;
private Status orderStatusInfo;
@JsonSerialize(using = BeanJsonSerializer.BigDecimalFormatSerializer.class)
private BigDecimal orderMoney;
@JsonSerialize(using = BeanJsonSerializer.LongDateToStringSerializer.class)
private Long createTime;
@Data
private static class Status {
private String name;
private Integer status;
}
public Status getOrderStatusInfo() {
Status status = new Status();
status.setStatus(this.getOrderStatus());
status.setName(OrderStatusEnum.getNameByStatus(this.getOrderStatus()));
return status;
}
}

View File

@ -0,0 +1,10 @@
package com.niu.recharge.service.core;
import com.niu.recharge.service.core.param.RechargeSetConfigParam;
import com.niu.recharge.service.core.vo.RechargeConfigVo;
public interface ICoreRechargeConfigService {
void setRechargeConfig(Integer siteId, RechargeSetConfigParam param);
RechargeConfigVo getRechargeConfig(Integer siteId);
}

View File

@ -0,0 +1,17 @@
package com.niu.recharge.service.core;
import com.niu.core.entity.pay.Pay;
import com.niu.recharge.entity.RechargeOrder;
import com.niu.recharge.service.core.param.RechargeOrderCreateParam;
import com.niu.recharge.service.core.vo.RechargeOrderCreateVo;
public interface ICoreRechargeOrderService {
RechargeOrderCreateVo create(RechargeOrderCreateParam param);
void close(Integer orderId);
RechargeOrder orderInfo(Integer orderId);
void pay(Pay pay);
}

View File

@ -0,0 +1,13 @@
package com.niu.recharge.service.core;
public interface ICoreRechargeRefundService {
/**
* 发起退款
*
* @param orderId
*/
void create(Integer orderId);
void refundComplete(String refundNo);
}

View File

@ -0,0 +1,42 @@
package com.niu.recharge.service.core.impl;
import cn.hutool.json.JSONUtil;
import com.niu.core.service.core.sys.ICoreConfigService;
import com.niu.core.service.core.sys.vo.CoreSysConfigVo;
import com.niu.recharge.service.core.ICoreRechargeConfigService;
import com.niu.recharge.service.core.model.RechargeConfigModel;
import com.niu.recharge.service.core.param.RechargeSetConfigParam;
import com.niu.recharge.service.core.vo.RechargeConfigVo;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class CoreRechargeConfigServiceImpl implements ICoreRechargeConfigService {
private static final String KEY = "RECHARGE_CONFIG";
private final ICoreConfigService coreConfigService;
@Override
public void setRechargeConfig(Integer siteId, RechargeSetConfigParam param) {
RechargeConfigModel model = new RechargeConfigModel();
BeanUtils.copyProperties(param, model);
coreConfigService.setConfig(siteId, KEY, JSONUtil.parseObj(model));
}
@Override
public RechargeConfigVo getRechargeConfig(Integer siteId) {
CoreSysConfigVo config = coreConfigService.getConfig(siteId, KEY);
RechargeConfigVo bean = JSONUtil.toBean(config.getValue(), RechargeConfigVo.class);
bean.setIsUse(Optional.ofNullable(bean.getIsUse()).orElse(1));
bean.setRechargeExplain(Optional.ofNullable(bean.getRechargeExplain()).orElse("充值后帐户的金额不能提现,可用于商城消费使用账户充值出现问题可联系商城客服"));
bean.setMinPrice(Optional.ofNullable(bean.getMinPrice()).orElse(new BigDecimal("0.01")));
bean.setCloseLength(Optional.ofNullable(bean.getCloseLength()).orElse(10));
return bean;
}
}

View File

@ -0,0 +1,219 @@
package com.niu.recharge.service.core.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.niu.core.common.exception.CommonException;
import com.niu.core.common.utils.NoticeUtils;
import com.niu.core.common.utils.RequestUtils;
import com.niu.core.entity.pay.Pay;
import com.niu.core.enums.member.AccountTypeEnum;
import com.niu.core.service.core.app.helper.EventAndSubscribeOfPublisher;
import com.niu.core.service.core.member.ICoreMemberAccountService;
import com.niu.core.service.core.pay.ICorePayService;
import com.niu.recharge.entity.Recharge;
import com.niu.recharge.entity.RechargeOrder;
import com.niu.recharge.entity.RechargeOrderItem;
import com.niu.recharge.enums.OrderStatusEnum;
import com.niu.recharge.event.RechargeAfterEvent;
import com.niu.recharge.mapper.RechargeMapper;
import com.niu.recharge.mapper.RechargeOrderItemMapper;
import com.niu.recharge.mapper.RechargeOrderMapper;
import com.niu.recharge.service.core.ICoreRechargeOrderService;
import com.niu.recharge.service.core.param.RechargeOrderCreateParam;
import com.niu.recharge.service.core.vo.RechargeOrderCreateVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
public class CoreRechargeOrderServiceImpl implements ICoreRechargeOrderService {
@Resource
RechargeOrderMapper rechargeOrderMapper;
@Resource
RechargeOrderItemMapper rechargeOrderItemMapper;
@Resource
ICorePayService corePayService;
@Resource
ICoreMemberAccountService coreMemberAccountService;
@Resource
RechargeMapper rechargeMapper;
/**
* @param param
* @return
*/
@Override
@Transactional
public RechargeOrderCreateVo create(RechargeOrderCreateParam param) {
try {
RechargeOrderCreateVo vo = new RechargeOrderCreateVo();
vo.setTradeType("recharge");
RechargeOrder order = new RechargeOrder();
order.setOrderNo(createOrderNo());
order.setOrderStatus(OrderStatusEnum.WAIT_PAY.getStatus());
order.setOrderFrom(RequestUtils.channel());
order.setSiteId(param.getSiteId());
order.setMemberId(param.getMemberId());
order.setIp(RequestUtils.ip());
order.setMemberMessage(ObjectUtil.defaultIfNull(param.getMemberMessage(), ""));
order.setOrderItemMoney(param.getRechargeMoney());
order.setOrderMoney(param.getRechargeMoney());
order.setOrderDiscountMoney(BigDecimal.ZERO);
order.setOrderType(vo.getTradeType());
order.setCreateTime(System.currentTimeMillis() / 1000);
this.rechargeOrderMapper.insert(order);
RechargeOrderItem orderItem = new RechargeOrderItem();
orderItem.setOrderId(order.getOrderId());
orderItem.setItemId(param.getRechargeId());
orderItem.setSiteId(param.getSiteId());
orderItem.setMemberId(param.getMemberId());
orderItem.setItemType(vo.getTradeType());
orderItem.setItemName("会员充值");
orderItem.setItemImage("/static/image/icon.png");
orderItem.setNum(1);
orderItem.setPrice(param.getRechargeMoney());
orderItem.setItemMoney(param.getRechargeMoney());
orderItem.setCreateTime(System.currentTimeMillis() / 1000);
this.rechargeOrderItemMapper.insert(orderItem);
vo.setTradeId(order.getOrderId());
return vo;
} catch (Exception e) {
throw new CommonException(e.getMessage());
}
}
/**
* 创建交易号
*
* @return
*/
private String createOrderNo() {
Snowflake snowflake = IdUtil.getSnowflake(1, 1);
long id = snowflake.nextId();
return DateUtil.format(DateUtil.date(System.currentTimeMillis()), "yyyyMMdd") + id;
}
/**
* 关闭支付
*
* @param orderId
*/
@Override
@Transactional
public void close(Integer orderId) {
RechargeOrder order = orderInfo(orderId);
if (order == null) return;
if (order.getOrderStatus().equals(OrderStatusEnum.WAIT_PAY.getStatus())) {
corePayService.closeByTrade(order.getSiteId(), "recharge", orderId);
}
order.setOrderStatus(OrderStatusEnum.CLOSE.getStatus());
this.rechargeOrderMapper.updateById(order);
}
/**
* 获取订单信息
*
* @param orderId
* @return
*/
public RechargeOrder orderInfo(Integer orderId) {
return this.rechargeOrderMapper.selectById(orderId);
}
/**
* 订单支付
*
* @param pay
*/
@Transactional
public void pay(Pay pay) {
try {
RechargeOrder order = this.orderInfo(pay.getTradeId());
log.info("pay order:{}", JSONUtil.toJsonStr(order));
if (order == null) throw new CommonException("订单不存在");
order.setPayTime(System.currentTimeMillis() / 1000);
order.setOrderStatus(OrderStatusEnum.FINISH.getStatus());
order.setIsEnableRefund(1);
order.setOutTradeNo(pay.getOutTradeNo());
this.rechargeOrderMapper.updateById(order);
QueryWrapper<RechargeOrderItem> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_id", pay.getTradeId());
List<RechargeOrderItem> items = rechargeOrderItemMapper.selectList(queryWrapper);
if (ObjectUtil.isEmpty(items)) {
return;
}
Recharge recharge = rechargeMapper.selectById(items.get(0).getItemId());
if (ObjectUtil.isEmpty(recharge)) {
return;
}
recharge.setSaleNum(recharge.getSaleNum() + 1);
rechargeMapper.updateById(recharge);
// 增加会员余额
if (recharge.getFaceValue().compareTo(BigDecimal.ZERO) > 0) {
coreMemberAccountService.addLog(order.getSiteId(), order.getMemberId(), AccountTypeEnum.BALANCE.getType(), recharge.getFaceValue().doubleValue(), "recharge", "会员充值", order.getOrderId().toString());
}
// 会员充值发放积分开始
if (recharge.getPoint() > 0) {
coreMemberAccountService.addLog(order.getSiteId(), order.getMemberId(), AccountTypeEnum.POINT.getType(), recharge.getPoint().doubleValue(), "recharge", "会员充值", order.getOrderId().toString());
}
// 会员充值发放成长值开始
if (recharge.getGrowth() > 0) {
coreMemberAccountService.addLog(order.getSiteId(), order.getMemberId(), AccountTypeEnum.GROWTH.getType(), recharge.getGrowth().doubleValue(), "recharge", "会员充值", order.getOrderId().toString());
}
if (ObjectUtil.isNotEmpty(recharge.getGiftJson())) {
EventAndSubscribeOfPublisher.publishAll(new RechargeAfterEvent(order.getMemberId(), recharge.getGiftJson()).setSiteId(order.getSiteId()));
}
// 发送消息
sendRechargeSuccessNotice(order);
log.info("pay success");
} catch (Exception e) {
log.info("pay fail: {}", e.getMessage());
System.out.println("充值订单支付异常");
throw new CommonException(e.getMessage());
}
}
/**
* 发送充值成功消息
*
* @param order
*/
@Async
protected void sendRechargeSuccessNotice(RechargeOrder order) {
Map<String, Object> param = new HashMap<String, Object>();
param.put("order", order);
NoticeUtils.send(order.getSiteId(), "recharge_success", param);
}
}

View File

@ -0,0 +1,155 @@
package com.niu.recharge.service.core.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.niu.core.common.exception.CommonException;
import com.niu.core.common.utils.RequestUtils;
import com.niu.core.entity.member.Member;
import com.niu.core.enums.member.AccountTypeEnum;
import com.niu.core.mapper.member.MemberMapper;
import com.niu.core.service.core.member.ICoreMemberAccountService;
import com.niu.core.service.core.pay.ICorePayService;
import com.niu.core.service.core.pay.ICoreRefundService;
import com.niu.core.service.core.pay.param.PayRefundCreateParam;
import com.niu.core.service.core.pay.param.PayRefundTransferParam;
import com.niu.recharge.entity.RechargeOrder;
import com.niu.recharge.entity.RechargeOrderItem;
import com.niu.recharge.entity.RechargeOrderItemRefund;
import com.niu.recharge.enums.OrderStatusEnum;
import com.niu.recharge.enums.RefundStatusEnum;
import com.niu.recharge.mapper.RechargeOrderItemMapper;
import com.niu.recharge.mapper.RechargeOrderItemRefundMapper;
import com.niu.recharge.mapper.RechargeOrderMapper;
import com.niu.recharge.service.core.ICoreRechargeOrderService;
import com.niu.recharge.service.core.ICoreRechargeRefundService;
import com.niu.recharge.service.core.param.RechargeOrderCreateParam;
import com.niu.recharge.service.core.vo.RechargeOrderCreateVo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
@Service
public class CoreRechargeRefundServiceImpl implements ICoreRechargeRefundService {
@Resource
RechargeOrderItemMapper rechargeOrderItemMapper;
@Resource
RechargeOrderMapper rechargeOrderMapper;
@Resource
MemberMapper memberMapper;
@Resource
ICoreRefundService coreRefundService;
@Resource
RechargeOrderItemRefundMapper rechargeOrderItemRefundMapper;
@Resource
ICoreRechargeOrderService coreRechargeOrderService;
@Resource
ICoreMemberAccountService coreMemberAccountService;
/**
*
* @return
*/
@Override
@Transactional
public void create(Integer orderId) {
RechargeOrder order = rechargeOrderMapper.selectOne(new QueryWrapper<RechargeOrder>().eq("order_id", orderId));
if (order == null) throw new CommonException("订单不存在");
if (!order.getIsEnableRefund().equals(1)) throw new CommonException("该订单不允许退款");
if (!order.getOrderStatus().equals(OrderStatusEnum.FINISH.getStatus())) throw new CommonException("订单尚未支付不能进行退款");
RechargeOrderItem orderItem = rechargeOrderItemMapper.selectOne(new QueryWrapper<RechargeOrderItem>().eq("order_id", orderId));
if (!orderItem.getRefundStatus().equals(0)) throw new CommonException("订单已申请退款");
Member member = memberMapper.selectOne(new QueryWrapper<Member>().select("balance").eq("member_id", order.getMemberId()));
if (member == null) throw new CommonException("会员不存在");
if (member.equals(0)) throw new CommonException("会员账户金额为0不允许进行退款");
try {
BigDecimal money = member.getBalance().compareTo(order.getOrderMoney()) == 1 ? order.getOrderMoney() : member.getBalance();
// 创建支付单据
PayRefundCreateParam refundParam = new PayRefundCreateParam();
refundParam.setOutTradeNo(order.getOutTradeNo());
refundParam.setSiteId(order.getSiteId());
refundParam.setTradeType("recharge");
refundParam.setTradeId(orderId.toString());
refundParam.setMoney(money);
String refundNo = coreRefundService.create(refundParam);
// 添加退款记录
RechargeOrderItemRefund model = new RechargeOrderItemRefund();
model.setRefundNo(refundNo);
model.setOrderId(orderId);
model.setOrderItemId(orderItem.getOrderItemId());
model.setNum(orderItem.getNum());
model.setMoney(money);
model.setItemType(orderItem.getItemType());
model.setSiteId(order.getSiteId());
model.setMemberId(order.getMemberId());
model.setCreateTime(System.currentTimeMillis() / 1000);
model.setStatus(RefundStatusEnum.REFUNDING.getStatus());
rechargeOrderItemRefundMapper.insert(model);
order.setRefundStatus(RefundStatusEnum.REFUNDING.getStatus());
rechargeOrderMapper.updateById(order);
orderItem.setRefundNo(refundNo);
orderItem.setRefundStatus(RefundStatusEnum.REFUNDING.getStatus());
rechargeOrderItemMapper.updateById(orderItem);
this.refund(model);
} catch (Exception e) {
throw new CommonException(e.getMessage());
}
}
@Transactional
public void refund(RechargeOrderItemRefund refund) {
// 扣除账户
coreMemberAccountService.addLog(refund.getSiteId(), refund.getMemberId(), AccountTypeEnum.BALANCE.getType(), refund.getMoney().negate().doubleValue(), "recharge_refund", "充值订单退款", refund.getOrderId().toString());
PayRefundTransferParam param = new PayRefundTransferParam();
param.setSiteId(refund.getSiteId());
param.setRefundNo(refund.getRefundNo());
coreRefundService.refund(param);
}
/**
* 退款完成
*
* @param refundNo
*/
@Transactional
public void refundComplete(String refundNo) {
RechargeOrderItemRefund refund = rechargeOrderItemRefundMapper.selectOne(new QueryWrapper<RechargeOrderItemRefund>().eq("refund_no", refundNo));
if (refund == null) return;
if (!refund.getStatus().equals(RefundStatusEnum.REFUNDING.getStatus())) return;
refund.setStatus(RefundStatusEnum.REFUND_COMPLETED.getStatus());
rechargeOrderItemRefundMapper.updateById(refund);
RechargeOrder order = new RechargeOrder();
order.setOrderId(refund.getOrderId());
order.setRefundStatus(RefundStatusEnum.REFUND_COMPLETED.getStatus());
rechargeOrderMapper.updateById(order);
RechargeOrderItem orderItem = new RechargeOrderItem();
orderItem.setOrderItemId(refund.getOrderItemId());
orderItem.setRefundStatus(RefundStatusEnum.REFUND_COMPLETED.getStatus());
rechargeOrderItemMapper.updateById(orderItem);
coreRechargeOrderService.close(refund.getOrderId());
}
}

View File

@ -0,0 +1,13 @@
package com.niu.recharge.service.core.model;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class RechargeConfigModel {
private Integer isUse;
private String rechargeExplain;
private BigDecimal minPrice;
private Integer closeLength;
}

View File

@ -0,0 +1,42 @@
package com.niu.recharge.service.core.notice.driver;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.niu.core.common.loader.notice.BaseNotice;
import com.niu.core.common.loader.notice.NoticeDataVo;
import com.niu.core.common.component.context.SpringContext;
import com.niu.core.entity.member.Member;
import com.niu.core.mapper.member.MemberMapper;
import com.niu.recharge.entity.RechargeOrder;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class RechargeSuccess extends BaseNotice {
public MemberMapper memberMapper() {
return SpringContext.bean(MemberMapper.class);
}
public NoticeDataVo noticeData(Map<String, Object> vars) {
RechargeOrder order = (RechargeOrder) vars.get("order");
Member member = memberMapper().selectOne(new QueryWrapper<Member>().select("balance").eq("member_id", order.getMemberId()));
Map<String, Object> param = new HashMap<String, Object>();
param.put("balance", member.getBalance());
param.put("price", order.getOrderMoney());
param.put("trade_no", order.getOrderNo());
param.put("time", DateUtil.date(order.getPayTime() * 1000));
param.put("wechat_page", "/addon/recharge/pages/recharge_record_detail?id=" + order.getOrderId());
param.put("weapp_page", "addon/recharge/pages/recharge_record_detail?id=" + order.getOrderId());
NoticeDataVo vo = new NoticeDataVo();
vo.setMemberId(order.getMemberId());
vo.setVars(param);
return vo;
}
}

View File

@ -0,0 +1,22 @@
package com.niu.recharge.service.core.param;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class RechargeOrderCreateParam implements Serializable {
private static final long serialVersionUID = 1L;
private String memberMessage;
private BigDecimal rechargeMoney;
private Integer rechargeId;
private Integer siteId;
private Integer memberId;
}

View File

@ -0,0 +1,13 @@
package com.niu.recharge.service.core.param;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class RechargeSetConfigParam {
private Integer isUse;
private String rechargeExplain;
private BigDecimal minPrice;
private Integer closeLength;
}

View File

@ -0,0 +1,13 @@
package com.niu.recharge.service.core.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class RechargeConfigVo {
private Integer isUse;
private String rechargeExplain;
private BigDecimal minPrice;
private Integer closeLength;
}

View File

@ -0,0 +1,15 @@
package com.niu.recharge.service.core.vo;
import com.niu.core.common.component.context.event.EventResult;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class RechargeGiftContentVo extends EventResult {
private List<String> detail;
private String info;
private String label;
}

View File

@ -0,0 +1,17 @@
package com.niu.recharge.service.core.vo;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class RechargeOrderCreateVo implements Serializable {
private static final long serialVersionUID = 1L;
private String tradeType;
private Integer tradeId;
}

View File

@ -0,0 +1,11 @@
{
"title": "会员充值",
"desc": "在线充值到会员账户",
"key": "recharge",
"version": "1.0.0",
"author": "niucloud",
"type": "addon",
"support_app": "",
"jar": "recharge.jar",
"mainClass": "com.niu.recharge.WebApplication"
}

Some files were not shown because too many files have changed in this diff Show More