Commit f1995e5d authored by 董先生's avatar 董先生

Merge branch 'dev' of http://rungit.jxdsy.cn:10000/sugar/jwhx into dev

parents 40b72715 46183d5f
......@@ -13,7 +13,7 @@ use App\Models\BaseModel;
class GoodsSku extends BaseModel {
protected $table = 'goods_sku';
protected $fillable = ['goods_id', 'sku_value', 'sku', 'price', 'integral', 'stock', 'sales', 'cover', 'created_at', 'updated_at'];
protected $fillable = ['goods_id', 'sku_value', 'sku', 'price', 'integral', 'stock', 'sales', 'cover', 'created_at', 'updated_at','goods_sn'];
// protected $hidden = ['deleted_at'];
protected function casts(): array {
......
......@@ -119,7 +119,7 @@ class GoodsService {
foreach ($goods->setFilterFields($request->all()) as $key => $value) {
$goods->$key = $value;
}
$goods->goods_sn = date('YmdHis') ;
$goods->goods_sn = "G".date('YmdHis') ;
$goods->save();
//更新栏目
......@@ -131,6 +131,7 @@ class GoodsService {
foreach ($sku as $key => $value) {
$sku[$key]['sku_value'] = isset($value['sku_value']) ? $value['sku_value'] : Arr::except($value, ['price', 'stock', 'cover', 'original_price', 'sales']);
$sku[$key]['sku'] = implode(';', $value['sku_value']);
$sku[$key]['goods_sn'] = "S".date('YmdHis') ;
}
$goods->sku()->createMany($sku);
}
......@@ -176,6 +177,7 @@ class GoodsService {
foreach ($sku as $key => $value) {
$value['sku_value'] = isset($value['sku_value']) ? $value['sku_value'] : Arr::except($value, ['price', 'stock', 'cover', 'original_price', 'sales']);
$value['sku'] = implode(';', $value['sku_value']);
$value['goods_sn'] = empty($value['goods_sn'])?"S".date('YmdHi').rand(1000,9999):$value['goods_sn'];
$goods->sku()->updateOrCreate(['id' => $value['id'] ?? 0], $value);
}
}else{
......
......@@ -289,6 +289,7 @@ class AccountService {
if(isset($result['code'])){
throw new \Exception('异常:'.$result['code'].' '.$result['message'], 0);
}
MemberAccount::where(['id'=>$account->id])->update(['status'=>1,'out_batch_no'=>$result]);
}else if($account->pay_type=="bank"){
throw new \Exception("银行卡在开发中!", 0);
}else {
......
......@@ -44,4 +44,14 @@ class Notify extends BaseController {
}
return 'success';
}
public function transfer(Request $request, NotifyService $service){
try {
return $service->transferNotify($request);
} catch (\Throwable $th) {
$this->data['message'] = $th->getMessage();
$this->data['code'] = 0;
}
return 'success';
}
}
\ No newline at end of file
......@@ -9,8 +9,12 @@
namespace Modules\Wechat\Services;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use EasyWeChat\Pay\Application;
use Modules\Member\Models\Member;
use Modules\Member\Models\MemberAccount;
use Modules\Order\Services\OrderService;
use Modules\Store\Services\PaymentService;
use Modules\Order\Services\RefundService;
......@@ -55,6 +59,36 @@ class NotifyService {
app(RefundService::class)->refundSuccess($message);
});
return $server->serve();
}
public function transferNotify($request){
$config = Config::get('wechat.miniapp');
$app = new Application($config);
$server = $app->getServer();
Log::info("转账到零钱异步返回通知:");
// 处理退款结果事件
//DB::connection()->enableQueryLog();
$data= MemberAccount::where(['status'=>1,'detail_status'=>null])->whereNotNull("out_batch_no")->select()->get();
//print_r(DB::getQueryLog());
foreach ($data as $key=>$val){
$result = TransferService::transferQuery($val['out_batch_no'],$val['request_no']);
if(isset($result['code']) && !empty($result['code'])){
MemberAccount::where(['id'=>$val['id']])->update(['status'=>-2,'reason'=>$result['message'],'detail_status'=>$result['fail_reason']]);
//提现失败金额退回
/*$user = Member::find($val['member_id']);
$user->money = bcadd($user->money, $val['amount'], 2);
$user->save();*/
}else{
MemberAccount::where(['id'=>$val['id']])->update(['detail_status'=>'SUCCESS']);
}
}
return $server->serve();
}
}
\ No newline at end of file
......@@ -59,8 +59,7 @@ class TransferService
return ['code'=>$response['code'],'message'=>$response['message']];
}
return $response;
$out_batch_no = $response->toArray()['out_batch_no'];
return $response['out_batch_no'];
}
//获取微信支付平台证书序列号与生成公钥文件
......@@ -87,15 +86,13 @@ class TransferService
}
/**
* 查询商家转账到零钱订单状态
* @param $batch_name
* @param $out_trade_no
* @param $money
* @param $openid
* @throws BaseException
/**查询商家转账到零钱订单状态
* @param $out_batch_no
* @param $out_detail_no
* @param string $http_method
* @return array|mixed
*/
public function transferQuery($out_batch_no,$out_detail_no,$http_method ="GET")
public static function transferQuery($out_batch_no,$out_detail_no,$http_method ="GET")
{
$url = 'https://api.mch.weixin.qq.com/v3/transfer/batches/out-batch-no/'.$out_batch_no.'/details/out-detail-no/'.$out_detail_no;
......@@ -150,7 +147,7 @@ class TransferService
}
}
return $result_arr;
return true;
}
}
\ No newline at end of file
......@@ -26,4 +26,5 @@ Route::name('wechat.')->prefix('wechat')->middleware(['auth.check:api'])->group(
Route::post('/wechat/notify/order', [Modules\Wechat\Controllers\Api\Notify::class, 'order']);
Route::post('/wechat/notify/payment', [Modules\Wechat\Controllers\Api\Notify::class, 'payment']);
Route::post('/wechat/notify/refund', [Modules\Wechat\Controllers\Api\Notify::class, 'refund']);
\ No newline at end of file
Route::post('/wechat/notify/refund', [Modules\Wechat\Controllers\Api\Notify::class, 'refund']);
Route::get('/wechat/notify/transfer', [Modules\Wechat\Controllers\Api\Notify::class, 'transfer']);
\ No newline at end of file
.input {
position: fixed;
left: 0;
transform: translateX(-200%);
}
.pos_order {
background-color: #f9fafc;
border-right: 1px solid #c0ccda;
}
.pos_btn {
margin-top: 20px;
}
.often_title {
border-bottom: 1px solid #d3dce6;
background-color: #f9fafc;
padding: 10px;
text-align: left;
cursor: pointer;
}
.often_goodsList ul li {
list-style: none;
float: left;
border: 1px solid #d3dce6;
padding: 10px;
margin: 10px;
background-color: #ffffff;
cursor: pointer;
}
.often_price {
color: #63b8ff;
}
.goodsType {
clear: both;
height: auto;
overflow: hidden;
border-top: 1px solid #d3dce6;
}
.cookList li {
list-style: none;
width: 23%;
border: 1px solid #e5e9f2;
height: auot;
overflow: hidden;
background-color: #fff;
padding: 2px;
float: left;
margin: 2px;
cursor: pointer;
}
.cookList_div{
width: 60%;
display: inline-block;
float: left;
}
.cookList li span {
display: block;
float: left;
}
.foodImg {
width: 40%;
height: 120px;
}
.foodImg img{
/* width: 100%; */
width: 100%;
height: auto;
}
.foodName {
font-size: 16px;
padding-left: 10px;
color: brown;
}
.foodPrice {
font-size: 16px;
padding-left: 10px;
padding-top: 10px;
color: #F64F15;
}
.total {
background: #ffffff;
padding: 10px;
border-bottom: 1px solid #d3dce6;
color: red;
font-size: 18px;
}
.total small {
padding: 0 10px;
text-align: center;
}
\ No newline at end of file
<template>
<el-container>
<el-main class="nopadding">
<!-- <input class="input" id="inputsmq" ref="inputRef" type="password" /> -->
<input class="input" id="inputsmq" ref="scannerInput" type="text" @input="handleScannerInput" @focus="onFocus" @blur="blurfun" />
<div class="pos">
<el-row>
<el-col :span='9' class="pos_order" id="order_list">
<el-tabs style="padding-left:10px;">
<el-tab-pane label="购物单">
<!-- <p v-if="scannedCode">{{ scannedCode }}</p> -->
<el-table :data="tableData" border style="width:100%;">
<el-table-column prop="title" label="商品名称"> </el-table-column>
<el-table-column prop="count" label="数量">
<template slot-scope='scope' #default="scope">
<el-input-number :min="1" @change="(e)=>changenums(e, scope.$index)" size="small" v-model="scope.row.count"></el-input-number>
</template>
</el-table-column>
<el-table-column prop="price" width="80" label="金额"> </el-table-column>
<el-table-column label="操作" width="80" fixed='right'>
<template slot-scope='scope' #default="scope">
<!-- <el-button size="small" @click="addOrderList(scope.row)">增加</el-button> -->
<!-- <el-button size="small" @click="addOrderList(scope.row)">减少</el-button> -->
<el-button type="danger" size="small" @click="delGoods(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="total">
<small>数量:</small>{{ totalCount }}<small>金额:</small>¥{{ totalMoney }}
</div>
<div class="pos_btn">
<el-button size="large" type="primary" @click="focusfun" >监听扫码枪识别</el-button>
<el-button size="large" type="danger" @click="delAllGoods()">清空购物单</el-button>
<el-button size="large" type="success" @click="checkOut()">结算</el-button>
</div>
</el-tab-pane>
</el-tabs>
</el-col>
<el-col :span="15">
<el-header>
<div class="left-panel">
<el-input v-model="search.keyword" placeholder="关键词" style="width:150px; margin-right: 10px;" clearable />
<sc-select-tree v-model="search.category_id" :apiObj="$API.goods.category.list" :props="{label: 'title', value: 'id'}" style="width: 240px; margin-right: 10px;" />
<el-select v-model="search.status" placeholder="状态" clearable style="width: 100px;">
<el-option v-for="item in [{value: 0, title: '下架'}, {value: 1, title: '上架'}]" :key="item.value" :label="item.title" :value="item.value" />
</el-select>
<el-button-group style="margin-left: 10px;">
<el-button type="primary" icon="el-icon-search" @click="upsearch">搜索</el-button>
</el-button-group>
</div>
</el-header>
<scTable tableName="member" ref="table" :apiObj="list.apiObj" :column="list.column" row-key="id" @selection-change="selectionChange" border stripe>
<el-table-column type="selection" width="50"></el-table-column>
<template #cover="scope">
<img :src="scope.row.cover" width="40" height="40" v-if="scope.row.cover" />
</template>
<template #status="scope">
<el-tag :type="scope.row.status == 1 ? 'success' : 'danger'">{{scope.row.status == 1 ? '上架' : '下架'}}</el-tag>
</template>
<el-table-column label="操作" fixed="right" align="center" width="140">
<template #default="scope">
<el-button-group>
<el-button type="primary" @click="addOrderList(scope.row, scope.$index)">添加</el-button>
</el-button-group>
</template>
</el-table-column>
</scTable>
</el-col>
</el-row>
</div>
</el-main>
</el-container>
</template>
<script >
import { ElLoading, ElMessage } from "element-plus";
export default {
name: "ucenter.cashierhome",
components: {},
data() {
return {
dialog: {search: false, import: false, print: false},
list: {
apiObj: this.$API.goods.lists.list,
column: [
{prop: 'id', label: 'ID', width: 80},
{prop: 'goods_sn', label: '商品编号'},
{prop: 'title', label: '商品名称'},
{prop: 'cover', label: '封面图', width: 120},
{prop: 'price', label: '售价', width: 120},
{prop: 'original_price', label: '原价', width: 120},
// {prop: 'status', label: '状态', width: 80},
],
},
selection: [],
search: {},
tableData: [],
totalMoney: 0,
totalCount: 0,
loading:null,
scannedCode: '', // 存储扫码后的结果 结果
isTyping: false, // 用户是否正在手动输入
typingTimeout: null, // 用于判断输入间隔的定时器
};
},
mounted(){
var orderHeight = document.body.clientHeight;
document.getElementById("order_list").style.height = orderHeight + "px";
},
created: function() {
},
methods: {
focusfun(){
// 自动聚焦到输入框,方便直接扫描
this.loading = ElLoading.service({
lock: true,
text: "请开始扫码识别",
background: "rgba(255, 255, 255, 0.7)",
});
this.$nextTick(() => {
this.$refs.scannerInput.focus();
});
// this.loading.close();
},
handleScannerInput(event) {
const input = event.target;
clearTimeout(this.typingTimeout);
// 如果用户手动输入,则设置一个短暂的延迟
if (this.isTyping) {
this.typingTimeout = setTimeout(() => {
this.isTyping = false;
// 扫描完成后的处理逻辑
this.scannedCode = input.value;
console.log('Scanned code:', this.scannedCode);
console.log('input.value:', input.value );
// 清空输入框以便下一次扫描
input.value = '';
// 处理扫码后的业务逻辑...
this.blurfun();
}, 500); // 假设用户不会连续输入超过500ms
} else {
clearTimeout(this.typingTimeout);
// 扫描完成后的处理逻辑
this.scannedCode = input.value;
console.log('Scanned code:', this.scannedCode);
console.log('input.value:', input.value );
// 清空输入框以便下一次扫描
input.value = '';
this.blurfun();
// 处理扫码后的业务逻辑...
}
},
onFocus() {
// 当输入框获得焦点时,认为用户可能开始手动输入
this.isTyping = true;
},
//失去焦点
blurfun(){
this.isTyping = false;
this.loading.close();
},
changenums(e,index){
this.$nextTick(()=>{
this.getAllMoney();
})
},
// 添加和计算商品价格
addOrderList(goods,index) {
// 商品是否已经存在于订单列表中
let isHave = false;
for (let i = 0; i < this.tableData.length; i++) {
if (this.tableData[i].id == goods.id) {
isHave = true;
}
}
if (isHave) {
// 改变列表中商品数量
let arr = this.tableData.filter(o => o.id == goods.id);
arr[0].count++;
} else {
let newGoods = {
id: goods.id,
title: goods.title,
price: goods.price,
count: 1
};
this.tableData.push(newGoods);
}
this.getAllMoney();
},
// 删除单个商品
delGoods(goods) {
console.log(goods.id, "删除了" + goods.id);
this.tableData = this.tableData.filter(o => o.id != goods.id);
this.getAllMoney();
},
// 全部删除商品
delAllGoods() {
this.tableData = [];
this.totalMoney = 0;
this.totalCount = 0;
},
// 数量和价格的汇总计算
getAllMoney() {
this.totalMoney = 0;
this.totalCount = 0;
if (this.tableData) {
let totalMoney = 0;
console.info('tableData',this.tableData);
this.tableData.forEach(element => {
this.totalCount += element.count;
console.info('element.price',element.price,element.count);
totalMoney += element.price * element.count;
});
console.info(totalMoney,'totalMoney');
this.totalMoney = parseFloat(totalMoney).toFixed(2);
}
},
// 模拟结账
checkOut() {
if (this.totalCount != 0) {
this.tableData = [];
this.totalMoney = 0;
this.totalCount = 0;
this.$message({
message: "结账成功,辛苦啦~",
type: "success"
});
} else {
this.$message.error("店员,没有东西,怎么结账呀~");
}
},
upsearch(){
this.$refs.table.reload(this.search);
},
moreUpsearch(search){
this.search = search;
this.upsearch();
},
moreSearch(){
this.dialog.search = true
this.$nextTick(() => {
this.$refs.searchBox.open().setData(this.search)
})
},
//表格选择后回调事件
selectionChange(selection){
this.selection = selection;
},
},
};
</script>
<style scoped>
@import url('./cashierhome.scss');
</style>
\ No newline at end of file
<template>
<el-container>
<!-- 大使账单 -->
<el-header>
<div class="left-panel">
<el-select v-model="search.type" placeholder="请选择类型" style="width: 180px; margin-right: 10px;" clearable>
......@@ -8,7 +9,7 @@
<el-input v-model="search.store_title" placeholder="请输入店铺名称" style="width: 180px; margin-right: 10px;" clearable />
<el-input v-model="search.username" placeholder="请输入用户名" style="width: 180px; margin-right: 10px;" clearable />
<el-select v-model="search.status" placeholder="请选择状态" style="width: 180px;" clearable v-if="search.type == 'withdraw'">
<el-option :label="item.label" :value="item.value" v-for="(item, index) in [{label: '已提现', value: 1}, {label: '待提现', value: 0}, {label: '已拒绝', value: 2}]" :key="index"></el-option>
<el-option :label="item.label" :value="item.value" v-for="(item, index) in [{label: '已提现', value: 1}, {label: '待提现', value: 0}, {label: '已拒绝', value: 2},{label: '提现失败', value: -2}]" :key="index"></el-option>
</el-select>
<el-select v-model="search.status" placeholder="请选择状态" style="width: 180px;" clearable v-if="search.type == 'income'">
<el-option :label="item.label" :value="item.value" v-for="(item, index) in [{label: '已到账', value: 1}, {label: '未入账', value: 0}]" :key="index"></el-option>
......@@ -43,7 +44,13 @@
</template>
<template #status="scope">
<el-tag v-if="scope.row.type == 'income'" :type="scope.row.status == 1 ? 'success' : 'danger'">{{ scope.row.status == 1 ? '已到账' : '未入账' }}</el-tag>
<el-tag v-else :type="scope.row.status == 1 ? 'success' : 'danger'">{{ scope.row.status == 1 ? '已提现' : '待确认' }}</el-tag>
<el-tag v-else :type="scope.row.status == 1 ? 'success' : 'danger'">
<span v-show="scope.row.status == 0">待提现</span>
<span v-show="scope.row.status == 1">已提现</span>
<span v-show="scope.row.status == 2">已拒绝</span>
<span v-show="scope.row.status == -2">提现失败</span>
</el-tag>
</template>
<el-table-column label="操作" fixed="right" align="center" width="100">
<template #default="scope">
......@@ -76,10 +83,12 @@ export default{
column: [
{prop: 'member', label: '头像'},
{prop: 'mobile', label: '手机号码', width: 120},
{prop: 'bank_realname', label: '真实姓名', width: 50},
{prop: 'type', label: '类型', width: 120, align: 'center'},
{prop: 'amount', label: '额度', width: 120},
{prop: 'af_amount', label: '余额', width: 120},
{prop: 'remark', label: '备注', width: 120},
{prop: 'reason', label: '失败理由', width: 60},
{prop: 'status', label: '状态', width: 120},
{prop: 'created_at', label: '添加时间', width: 140},
{prop: 'updated_at', label: '更新时间', width: 140},
......
<template>
<el-container>
<!-- 我的收益 -->
<el-header>
<div class="left-panel">
<el-select v-model="search.type" placeholder="请选择类型" style="width: 180px; margin-right: 10px;" clearable>
......@@ -8,7 +9,7 @@
<el-input v-model="search.store_title" placeholder="请输入店铺名称" style="width: 180px; margin-right: 10px;" clearable />
<el-input v-model="search.username" placeholder="请输入用户名" style="width: 180px; margin-right: 10px;" clearable />
<el-select v-model="search.status" placeholder="请选择状态" style="width: 180px;" clearable v-if="search.type == 'withdraw'">
<el-option :label="item.label" :value="item.value" v-for="(item, index) in [{label: '已提现', value: 1}, {label: '待提现', value: 0}, {label: '已拒绝', value: 2}]" :key="index"></el-option>
<el-option :label="item.label" :value="item.value" v-for="(item, index) in [{label: '已提现', value: 1}, {label: '待提现', value: 0}, {label: '已拒绝', value: 2},{label: '提现失败', value: -2}]" :key="index"></el-option>
</el-select>
<el-select v-model="search.status" placeholder="请选择状态" style="width: 180px;" clearable v-if="search.type == 'income'">
<el-option :label="item.label" :value="item.value" v-for="(item, index) in [{label: '已到账', value: 1}, {label: '未入账', value: 0}]" :key="index"></el-option>
......@@ -37,7 +38,14 @@
</template>
<template #status="scope">
<el-tag v-if="scope.row.type == 'service_income'" :type="scope.row.status == 1 ? 'success' : 'danger'">{{ scope.row.status == 1 ? '已到账' : '未入账' }}</el-tag>
<el-tag v-else :type="scope.row.status == 1 ? 'success' : 'danger'">{{ scope.row.status == 1 ? '已提现' : '待确认' }}</el-tag>
<!-- <el-tag v-else :type="scope.row.status == 1 ? 'success' : 'danger'">{{ scope.row.status == 1 ? '已提现' : '待确认' }}</el-tag> -->
<el-tag v-else :type="scope.row.status == 1 ? 'success' : 'danger'">
<span v-show="scope.row.status == 0">待提现</span>
<span v-show="scope.row.status == 1">已提现</span>
<span v-show="scope.row.status == 2">已拒绝</span>
<span v-show="scope.row.status == -2">提现失败</span>
</el-tag>
</template>
<el-table-column label="操作" fixed="right" align="center" width="100">
<template #default="scope">
......@@ -71,6 +79,7 @@ export default{
{prop: 'af_amount', label: '余额', width: 120},
{prop: 'remark', label: '备注', width: 120},
{prop: 'status', label: '状态', width: 120},
{prop: 'reason', label: '提败理由', width: 120},
{prop: 'created_at', label: '添加时间', width: 140},
{prop: 'updated_at', label: '更新时间', width: 140},
],
......
......@@ -20,7 +20,7 @@ export default defineConfig({
__INTLIFY_PROD_DEVTOOLS__: false
},
server: {
port: 8080,
port: 8081,
host: true,
open: false,
proxy: {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment