分销功能提交
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,590 @@
|
||||
<?php
|
||||
|
||||
namespace app\cunkebao\controller\distribution;
|
||||
|
||||
use app\cunkebao\model\DistributionChannel;
|
||||
use app\cunkebao\model\DistributionWithdrawal;
|
||||
use app\common\util\JwtUtil;
|
||||
use think\Controller;
|
||||
use think\Db;
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* 分销渠道用户端控制器
|
||||
* 用户通过渠道编码访问,无需JWT认证
|
||||
*/
|
||||
class ChannelUserController extends Controller
|
||||
{
|
||||
/**
|
||||
* 初始化方法,设置跨域响应头
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
// 处理OPTIONS预检请求
|
||||
if ($this->request->method(true) == 'OPTIONS') {
|
||||
$origin = $this->request->header('origin', '*');
|
||||
header("Access-Control-Allow-Origin: " . $origin);
|
||||
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization, Cookie");
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, PATCH');
|
||||
header("Access-Control-Allow-Credentials: true");
|
||||
header("Access-Control-Max-Age: 86400");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置跨域响应头
|
||||
* @param \think\response\Json $response
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
protected function setCorsHeaders($response)
|
||||
{
|
||||
$origin = $this->request->header('origin', '*');
|
||||
$response->header([
|
||||
'Access-Control-Allow-Origin' => $origin,
|
||||
'Access-Control-Allow-Headers' => 'Origin, X-Requested-With, Content-Type, Accept, Authorization, Cookie',
|
||||
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS, PATCH',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Max-Age' => '86400',
|
||||
]);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渠道登录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
try {
|
||||
// 获取参数
|
||||
$phone = $this->request->param('phone', '');
|
||||
$password = $this->request->param('password', '');
|
||||
|
||||
// 参数验证
|
||||
if (empty($phone)) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '手机号不能为空',
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
|
||||
if (empty($password)) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '密码不能为空',
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
|
||||
// 查询渠道信息(通过手机号)
|
||||
$channel = Db::name('distribution_channel')
|
||||
->where([
|
||||
['phone', '=', $phone],
|
||||
['deleteTime', '=', 0]
|
||||
])
|
||||
->find();
|
||||
|
||||
if (!$channel) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 404,
|
||||
'success' => false,
|
||||
'msg' => '渠道不存在',
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
|
||||
// 检查渠道状态
|
||||
if ($channel['status'] !== DistributionChannel::STATUS_ENABLED) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 403,
|
||||
'success' => false,
|
||||
'msg' => '渠道已被禁用',
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
|
||||
// 验证密码(MD5加密)
|
||||
$passwordMd5 = md5($password);
|
||||
if ($channel['password'] !== $passwordMd5) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 401,
|
||||
'success' => false,
|
||||
'msg' => '密码错误',
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
|
||||
// 准备token载荷(不包含密码)
|
||||
$payload = [
|
||||
'id' => $channel['id'],
|
||||
'channelId' => $channel['id'],
|
||||
'channelCode' => $channel['code'],
|
||||
'channelName' => $channel['name'],
|
||||
'companyId' => $channel['companyId'],
|
||||
'type' => 'channel', // 标识这是渠道登录
|
||||
];
|
||||
|
||||
// 生成JWT令牌(30天有效期)
|
||||
$expire = 86400 * 30;
|
||||
$token = JwtUtil::createToken($payload, $expire);
|
||||
$tokenExpired = time() + $expire;
|
||||
|
||||
// 更新最后登录时间(可选)
|
||||
Db::name('distribution_channel')
|
||||
->where('id', $channel['id'])
|
||||
->update([
|
||||
'updateTime' => time()
|
||||
]);
|
||||
|
||||
// 返回数据(不包含密码)
|
||||
$data = [
|
||||
'token' => $token,
|
||||
'tokenExpired' => $tokenExpired,
|
||||
'channelInfo' => [
|
||||
'id' => (string)$channel['id'],
|
||||
'channelCode' => $channel['code'],
|
||||
'channelName' => $channel['name'],
|
||||
'phone' => $channel['phone'] ?: '',
|
||||
'wechatId' => $channel['wechatId'] ?: '',
|
||||
'status' => $channel['status'],
|
||||
'totalCustomers' => (int)$channel['totalCustomers'],
|
||||
'todayCustomers' => (int)$channel['todayCustomers'],
|
||||
'totalFriends' => (int)$channel['totalFriends'],
|
||||
'todayFriends' => (int)$channel['todayFriends'],
|
||||
'withdrawableAmount' => round(($channel['withdrawableAmount'] ?? 0) / 100, 2), // 分转元
|
||||
]
|
||||
];
|
||||
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 200,
|
||||
'success' => true,
|
||||
'msg' => '登录成功',
|
||||
'data' => $data
|
||||
]));
|
||||
|
||||
} catch (Exception $e) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => $e->getCode() ?: 500,
|
||||
'success' => false,
|
||||
'msg' => '登录失败:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取渠道首页数据
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
try {
|
||||
// 获取参数
|
||||
$channelCode = $this->request->param('channelCode', '');
|
||||
|
||||
// 参数验证
|
||||
if (empty($channelCode)) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '渠道编码不能为空',
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
|
||||
// 查询渠道信息
|
||||
$channel = Db::name('distribution_channel')
|
||||
->where([
|
||||
['code', '=', $channelCode],
|
||||
['status', '=', DistributionChannel::STATUS_ENABLED],
|
||||
['deleteTime', '=', 0]
|
||||
])
|
||||
->find();
|
||||
|
||||
if (!$channel) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 404,
|
||||
'success' => false,
|
||||
'msg' => '渠道不存在或已被禁用',
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
|
||||
$channelId = $channel['id'];
|
||||
$companyId = $channel['companyId'];
|
||||
|
||||
// 1. 渠道基本信息
|
||||
$channelInfo = [
|
||||
'channelName' => $channel['name'] ?? '',
|
||||
'channelCode' => $channel['code'] ?? '',
|
||||
];
|
||||
|
||||
// 2. 财务统计
|
||||
// 当前可提现金额
|
||||
$withdrawableAmount = intval($channel['withdrawableAmount'] ?? 0);
|
||||
|
||||
// 已提现金额(已打款的提现申请)
|
||||
$withdrawnAmount = Db::name('distribution_withdrawal')
|
||||
->where([
|
||||
['companyId', '=', $companyId],
|
||||
['channelId', '=', $channelId],
|
||||
['status', '=', DistributionWithdrawal::STATUS_PAID]
|
||||
])
|
||||
->sum('amount');
|
||||
$withdrawnAmount = intval($withdrawnAmount ?? 0);
|
||||
|
||||
// 待审核金额(待审核的提现申请)
|
||||
$pendingReviewAmount = Db::name('distribution_withdrawal')
|
||||
->where([
|
||||
['companyId', '=', $companyId],
|
||||
['channelId', '=', $channelId],
|
||||
['status', '=', DistributionWithdrawal::STATUS_PENDING]
|
||||
])
|
||||
->sum('amount');
|
||||
$pendingReviewAmount = intval($pendingReviewAmount ?? 0);
|
||||
|
||||
// 总收益(所有收益记录的总和)
|
||||
$totalRevenue = Db::name('distribution_revenue_record')
|
||||
->where([
|
||||
['companyId', '=', $companyId],
|
||||
['channelId', '=', $channelId]
|
||||
])
|
||||
->sum('amount');
|
||||
$totalRevenue = intval($totalRevenue ?? 0);
|
||||
|
||||
$financialStats = [
|
||||
'withdrawableAmount' => round($withdrawableAmount / 100, 2), // 当前可提现金额(元)
|
||||
'totalRevenue' => round($totalRevenue / 100, 2), // 总收益(元)
|
||||
'pendingReview' => round($pendingReviewAmount / 100, 2), // 待审核(元)
|
||||
'withdrawn' => round($withdrawnAmount / 100, 2), // 已提现(元)
|
||||
];
|
||||
|
||||
// 3. 客户和好友统计
|
||||
$customerStats = [
|
||||
'totalFriends' => (int)($channel['totalFriends'] ?? 0), // 总加好友数
|
||||
'todayFriends' => (int)($channel['todayFriends'] ?? 0), // 今日加好友数
|
||||
'totalCustomers' => (int)($channel['totalCustomers'] ?? 0), // 总获客数
|
||||
'todayCustomers' => (int)($channel['todayCustomers'] ?? 0), // 今日获客数
|
||||
];
|
||||
|
||||
// 返回数据
|
||||
$data = [
|
||||
'channelInfo' => $channelInfo,
|
||||
'financialStats' => $financialStats,
|
||||
'customerStats' => $customerStats,
|
||||
];
|
||||
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 200,
|
||||
'success' => true,
|
||||
'msg' => '获取成功',
|
||||
'data' => $data
|
||||
]));
|
||||
|
||||
} catch (Exception $e) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => $e->getCode() ?: 500,
|
||||
'success' => false,
|
||||
'msg' => '获取数据失败:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取收益明细列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function revenueRecords()
|
||||
{
|
||||
try {
|
||||
// 获取参数
|
||||
$channelCode = $this->request->param('channelCode', '');
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$type = $this->request->param('type', 'all'); // all, customer_acquisition, add_friend, order, poster, phone, other
|
||||
$date = $this->request->param('date', ''); // 日期筛选,格式:Y-m-d
|
||||
|
||||
// 参数验证
|
||||
if (empty($channelCode)) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '渠道编码不能为空',
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
|
||||
$page = max(1, intval($page));
|
||||
$limit = max(1, min(100, intval($limit)));
|
||||
|
||||
// 查询渠道信息
|
||||
$channel = Db::name('distribution_channel')
|
||||
->where([
|
||||
['code', '=', $channelCode],
|
||||
['status', '=', DistributionChannel::STATUS_ENABLED],
|
||||
['deleteTime', '=', 0]
|
||||
])
|
||||
->find();
|
||||
|
||||
if (!$channel) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 404,
|
||||
'success' => false,
|
||||
'msg' => '渠道不存在或已被禁用',
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
|
||||
$channelId = $channel['id'];
|
||||
$companyId = $channel['companyId'];
|
||||
|
||||
// 构建查询条件
|
||||
$where = [
|
||||
['companyId', '=', $companyId],
|
||||
['channelId', '=', $channelId]
|
||||
];
|
||||
|
||||
// 类型筛选
|
||||
if ($type !== 'all') {
|
||||
$where[] = ['type', '=', $type];
|
||||
}
|
||||
|
||||
// 日期筛选
|
||||
if (!empty($date)) {
|
||||
$dateStart = strtotime($date . ' 00:00:00');
|
||||
$dateEnd = strtotime($date . ' 23:59:59');
|
||||
if ($dateStart && $dateEnd) {
|
||||
$where[] = ['createTime', 'between', [$dateStart, $dateEnd]];
|
||||
}
|
||||
}
|
||||
|
||||
// 查询总数
|
||||
$total = Db::name('distribution_revenue_record')
|
||||
->where($where)
|
||||
->count();
|
||||
|
||||
// 查询列表(按创建时间倒序)
|
||||
$list = Db::name('distribution_revenue_record')
|
||||
->where($where)
|
||||
->order('createTime DESC')
|
||||
->page($page, $limit)
|
||||
->select();
|
||||
|
||||
// 从活动表(customer_acquisition_task)获取类型标签映射(使用 sourceId 关联活动ID)
|
||||
$formattedList = [];
|
||||
if (!empty($list)) {
|
||||
// 收集本页涉及到的活动ID
|
||||
$taskIds = [];
|
||||
foreach ($list as $row) {
|
||||
if (!empty($row['sourceId'])) {
|
||||
$taskIds[] = (int)$row['sourceId'];
|
||||
}
|
||||
}
|
||||
$taskIds = array_values(array_unique($taskIds));
|
||||
|
||||
// 获取活动名称映射:taskId => name
|
||||
$taskNameMap = [];
|
||||
if (!empty($taskIds)) {
|
||||
$taskNameMap = Db::name('customer_acquisition_task')
|
||||
->whereIn('id', $taskIds)
|
||||
->column('name', 'id');
|
||||
}
|
||||
|
||||
// 格式化数据
|
||||
foreach ($list as $item) {
|
||||
$taskId = !empty($item['sourceId']) ? (int)$item['sourceId'] : 0;
|
||||
$taskName = $taskId && isset($taskNameMap[$taskId]) ? $taskNameMap[$taskId] : null;
|
||||
|
||||
$formattedItem = [
|
||||
'id' => (string)$item['id'],
|
||||
'sourceType' => $item['sourceType'] ?? '其他',
|
||||
'type' => $item['type'] ?? 'other',
|
||||
// 类型标签优先取活动名称,没有则回退为 sourceType 或 “其他”
|
||||
'typeLabel' => $taskName ?: (!empty($item['sourceType']) ? $item['sourceType'] : '其他'),
|
||||
'amount' => round($item['amount'] / 100, 2), // 分转元
|
||||
'remark' => isset($item['remark']) && $item['remark'] !== '' ? $item['remark'] : null,
|
||||
'createTime' => !empty($item['createTime']) ? date('Y-m-d H:i', $item['createTime']) : '',
|
||||
];
|
||||
$formattedList[] = $formattedItem;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 200,
|
||||
'success' => true,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'list' => $formattedList,
|
||||
'total' => (int)$total,
|
||||
'page' => $page,
|
||||
'limit' => $limit
|
||||
]
|
||||
]));
|
||||
|
||||
} catch (Exception $e) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => $e->getCode() ?: 500,
|
||||
'success' => false,
|
||||
'msg' => '获取收益明细失败:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提现明细列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function withdrawalRecords()
|
||||
{
|
||||
try {
|
||||
// 获取参数
|
||||
$channelCode = $this->request->param('channelCode', '');
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$status = $this->request->param('status', 'all'); // all, pending, approved, rejected, paid
|
||||
$payType = $this->request->param('payType', 'all'); // all, wechat, alipay, bankcard
|
||||
$date = $this->request->param('date', ''); // 日期筛选,格式:Y-m-d
|
||||
|
||||
// 参数验证
|
||||
if (empty($channelCode)) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '渠道编码不能为空',
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
|
||||
$page = max(1, intval($page));
|
||||
$limit = max(1, min(100, intval($limit)));
|
||||
|
||||
// 校验到账方式参数
|
||||
$validPayTypes = ['all', 'wechat', 'alipay', 'bankcard'];
|
||||
if (!in_array($payType, $validPayTypes)) {
|
||||
$payType = 'all';
|
||||
}
|
||||
|
||||
// 查询渠道信息
|
||||
$channel = Db::name('distribution_channel')
|
||||
->where([
|
||||
['code', '=', $channelCode],
|
||||
['status', '=', DistributionChannel::STATUS_ENABLED],
|
||||
['deleteTime', '=', 0]
|
||||
])
|
||||
->find();
|
||||
|
||||
if (!$channel) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 404,
|
||||
'success' => false,
|
||||
'msg' => '渠道不存在或已被禁用',
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
|
||||
$channelId = $channel['id'];
|
||||
$companyId = $channel['companyId'];
|
||||
|
||||
// 构建查询条件
|
||||
$where = [
|
||||
['companyId', '=', $companyId],
|
||||
['channelId', '=', $channelId]
|
||||
];
|
||||
|
||||
// 状态筛选
|
||||
if ($status !== 'all') {
|
||||
$where[] = ['status', '=', $status];
|
||||
}
|
||||
|
||||
// 到账方式筛选
|
||||
if ($payType !== 'all') {
|
||||
$where[] = ['payType', '=', $payType];
|
||||
}
|
||||
|
||||
// 日期筛选
|
||||
if (!empty($date)) {
|
||||
$dateStart = strtotime($date . ' 00:00:00');
|
||||
$dateEnd = strtotime($date . ' 23:59:59');
|
||||
if ($dateStart && $dateEnd) {
|
||||
$where[] = ['applyTime', 'between', [$dateStart, $dateEnd]];
|
||||
}
|
||||
}
|
||||
|
||||
// 查询总数
|
||||
$total = Db::name('distribution_withdrawal')
|
||||
->where($where)
|
||||
->count();
|
||||
|
||||
// 查询列表(按申请时间倒序)
|
||||
$list = Db::name('distribution_withdrawal')
|
||||
->where($where)
|
||||
->order('applyTime DESC')
|
||||
->page($page, $limit)
|
||||
->select();
|
||||
|
||||
// 格式化数据
|
||||
$formattedList = [];
|
||||
foreach ($list as $item) {
|
||||
// 状态标签映射
|
||||
$statusLabels = [
|
||||
'pending' => '待审核',
|
||||
'approved' => '已通过',
|
||||
'rejected' => '已拒绝',
|
||||
'paid' => '已打款'
|
||||
];
|
||||
|
||||
// 支付类型标签映射
|
||||
$payTypeLabels = [
|
||||
'wechat' => '微信',
|
||||
'alipay' => '支付宝',
|
||||
'bankcard' => '银行卡'
|
||||
];
|
||||
|
||||
$payType = !empty($item['payType']) ? $item['payType'] : null;
|
||||
|
||||
$formattedItem = [
|
||||
'id' => (string)$item['id'],
|
||||
'amount' => round($item['amount'] / 100, 2), // 分转元
|
||||
'status' => $item['status'] ?? 'pending',
|
||||
'statusLabel' => $statusLabels[$item['status'] ?? 'pending'] ?? '待审核',
|
||||
'payType' => $payType,
|
||||
'payTypeLabel' => $payType && isset($payTypeLabels[$payType]) ? $payTypeLabels[$payType] : null,
|
||||
'applyTime' => !empty($item['applyTime']) ? date('Y-m-d H:i', $item['applyTime']) : '',
|
||||
'reviewTime' => !empty($item['reviewTime']) ? date('Y-m-d H:i', $item['reviewTime']) : null,
|
||||
'reviewer' => !empty($item['reviewer']) ? $item['reviewer'] : null,
|
||||
'remark' => !empty($item['remark']) ? $item['remark'] : null,
|
||||
];
|
||||
$formattedList[] = $formattedItem;
|
||||
}
|
||||
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => 200,
|
||||
'success' => true,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'list' => $formattedList,
|
||||
'total' => (int)$total,
|
||||
'page' => $page,
|
||||
'limit' => $limit
|
||||
]
|
||||
]));
|
||||
|
||||
} catch (Exception $e) {
|
||||
return $this->setCorsHeaders(json([
|
||||
'code' => $e->getCode() ?: 500,
|
||||
'success' => false,
|
||||
'msg' => '获取提现明细失败:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,670 @@
|
||||
<?php
|
||||
|
||||
namespace app\cunkebao\controller\distribution;
|
||||
|
||||
use app\cunkebao\controller\BaseController;
|
||||
use app\cunkebao\model\DistributionWithdrawal;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* 分销渠道提现申请控制器
|
||||
*/
|
||||
class WithdrawalController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取提现申请列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
try {
|
||||
// 获取参数
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 20);
|
||||
$status = $this->request->param('status', 'all');
|
||||
$date = $this->request->param('date', '');
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
// 参数验证
|
||||
$page = max(1, intval($page));
|
||||
$limit = max(1, min(100, intval($limit))); // 限制最大100
|
||||
|
||||
// 验证状态参数
|
||||
$validStatuses = ['all', DistributionWithdrawal::STATUS_PENDING, DistributionWithdrawal::STATUS_APPROVED, DistributionWithdrawal::STATUS_REJECTED, DistributionWithdrawal::STATUS_PAID];
|
||||
if (!in_array($status, $validStatuses)) {
|
||||
$status = 'all';
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
$where = [];
|
||||
$where[] = ['w.companyId', '=', $companyId];
|
||||
|
||||
// 状态筛选
|
||||
if ($status !== 'all') {
|
||||
$where[] = ['w.status', '=', $status];
|
||||
}
|
||||
|
||||
// 日期筛选(格式:YYYY/MM/DD)
|
||||
if (!empty($date)) {
|
||||
// 转换日期格式 YYYY/MM/DD 为时间戳范围
|
||||
$dateParts = explode('/', $date);
|
||||
if (count($dateParts) === 3) {
|
||||
$dateStr = $dateParts[0] . '-' . $dateParts[1] . '-' . $dateParts[2];
|
||||
$dateStart = strtotime($dateStr . ' 00:00:00');
|
||||
$dateEnd = strtotime($dateStr . ' 23:59:59');
|
||||
if ($dateStart && $dateEnd) {
|
||||
$where[] = ['w.applyTime', 'between', [$dateStart, $dateEnd]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 关键词搜索(模糊匹配渠道名称、渠道编码)
|
||||
if (!empty($keyword)) {
|
||||
$keyword = trim($keyword);
|
||||
// 需要关联渠道表进行搜索
|
||||
}
|
||||
|
||||
// 构建查询(关联渠道表获取渠道名称和编码,只关联未删除的渠道)
|
||||
$query = Db::name('distribution_withdrawal')
|
||||
->alias('w')
|
||||
->join('distribution_channel c', 'w.channelId = c.id AND c.deleteTime = 0', 'left')
|
||||
->where($where);
|
||||
|
||||
// 关键词搜索(如果有关键词,添加渠道表关联条件)
|
||||
if (!empty($keyword)) {
|
||||
$query->where(function ($query) use ($keyword) {
|
||||
$query->where('c.name', 'like', '%' . $keyword . '%')
|
||||
->whereOr('c.code', 'like', '%' . $keyword . '%');
|
||||
});
|
||||
}
|
||||
|
||||
// 查询总数
|
||||
$total = $query->count();
|
||||
|
||||
// 查询列表(按申请时间倒序)
|
||||
$list = $query->field([
|
||||
'w.id',
|
||||
'w.channelId',
|
||||
'w.amount',
|
||||
'w.status',
|
||||
'w.payType',
|
||||
'w.applyTime',
|
||||
'w.reviewTime',
|
||||
'w.reviewer',
|
||||
'w.remark',
|
||||
'c.name as channelName',
|
||||
'c.code as channelCode'
|
||||
])
|
||||
->order('w.applyTime DESC')
|
||||
->page($page, $limit)
|
||||
->select();
|
||||
|
||||
// 格式化数据
|
||||
$formattedList = [];
|
||||
foreach ($list as $item) {
|
||||
// 格式化申请日期为 YYYY/MM/DD
|
||||
$applyDate = '';
|
||||
if (!empty($item['applyTime'])) {
|
||||
$applyDate = date('Y/m/d', $item['applyTime']);
|
||||
}
|
||||
|
||||
// 格式化审核日期
|
||||
$reviewDate = null;
|
||||
if (!empty($item['reviewTime'])) {
|
||||
$reviewDate = date('Y-m-d H:i:s', $item['reviewTime']);
|
||||
}
|
||||
|
||||
$formattedItem = [
|
||||
'id' => (string)$item['id'],
|
||||
'channelId' => (string)$item['channelId'],
|
||||
'channelName' => $item['channelName'] ?? '',
|
||||
'channelCode' => $item['channelCode'] ?? '',
|
||||
'amount' => round($item['amount'] / 100, 2), // 分转元,保留2位小数
|
||||
'status' => $item['status'] ?? DistributionWithdrawal::STATUS_PENDING,
|
||||
'payType' => !empty($item['payType']) ? $item['payType'] : null, // 支付类型
|
||||
'applyDate' => $applyDate,
|
||||
'reviewDate' => $reviewDate,
|
||||
'reviewer' => !empty($item['reviewer']) ? $item['reviewer'] : null,
|
||||
'remark' => !empty($item['remark']) ? $item['remark'] : null,
|
||||
];
|
||||
$formattedList[] = $formattedItem;
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
return json([
|
||||
'code' => 200,
|
||||
'success' => true,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'list' => $formattedList,
|
||||
'total' => (int)$total
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
return json([
|
||||
'code' => $e->getCode() ?: 500,
|
||||
'success' => false,
|
||||
'msg' => '获取提现申请列表失败:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建提现申请
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
try {
|
||||
// 获取参数(接口接收的金额单位为元)
|
||||
// 原先使用 channelId,现在改为使用渠道编码 channelCode
|
||||
$channelCode = $this->request->param('channelCode', '');
|
||||
$amount = $this->request->param('amount', 0); // 金额单位:元
|
||||
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
// 参数验证
|
||||
if (empty($channelCode)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '渠道编码不能为空',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 验证金额(转换为浮点数进行验证)
|
||||
$amount = floatval($amount);
|
||||
if (empty($amount) || $amount <= 0) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '提现金额必须大于0',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 验证金额格式(最多2位小数)
|
||||
if (!preg_match('/^\d+(\.\d{1,2})?$/', (string)$amount)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '提现金额格式不正确,最多保留2位小数',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 检查渠道是否存在且属于当前公司(通过渠道编码查询)
|
||||
$channel = Db::name('distribution_channel')
|
||||
->where([
|
||||
['code', '=', $channelCode],
|
||||
['companyId', '=', $companyId],
|
||||
['deleteTime', '=', 0]
|
||||
])
|
||||
->find();
|
||||
|
||||
if (!$channel) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'success' => false,
|
||||
'msg' => '渠道不存在或没有权限',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 统一使用渠道ID变量,后续逻辑仍然基于 channelId
|
||||
$channelId = $channel['id'];
|
||||
|
||||
// 检查渠道状态
|
||||
if ($channel['status'] !== 'enabled') {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '渠道已禁用,无法申请提现',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 检查可提现金额
|
||||
// 数据库存储的是分,接口接收的是元,需要统一单位进行比较
|
||||
$withdrawableAmountInFen = intval($channel['withdrawableAmount'] ?? 0); // 数据库中的分
|
||||
$withdrawableAmountInYuan = round($withdrawableAmountInFen / 100, 2); // 转换为元用于提示
|
||||
$amountInFen = intval(round($amount * 100)); // 将接口接收的元转换为分
|
||||
|
||||
if ($amountInFen > $withdrawableAmountInFen) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '提现金额不能超过可提现金额(' . number_format($withdrawableAmountInYuan, 2) . '元)',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 检查是否有待审核的申请
|
||||
$pendingWithdrawal = Db::name('distribution_withdrawal')
|
||||
->where([
|
||||
['channelId', '=', $channelId],
|
||||
['companyId', '=', $companyId],
|
||||
['status', '=', DistributionWithdrawal::STATUS_PENDING]
|
||||
])
|
||||
->find();
|
||||
|
||||
if ($pendingWithdrawal) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '该渠道已有待审核的提现申请,请等待审核完成后再申请',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 创建提现申请(金额以分存储)
|
||||
$withdrawalData = [
|
||||
'companyId' => $companyId,
|
||||
'channelId' => $channelId,
|
||||
'amount' => $amountInFen, // 存储为分
|
||||
'status' => DistributionWithdrawal::STATUS_PENDING,
|
||||
'applyTime' => time(),
|
||||
'createTime' => time(),
|
||||
'updateTime' => time(),
|
||||
];
|
||||
|
||||
$withdrawalId = Db::name('distribution_withdrawal')->insertGetId($withdrawalData);
|
||||
|
||||
if (!$withdrawalId) {
|
||||
throw new Exception('创建提现申请失败');
|
||||
}
|
||||
|
||||
// 扣除渠道可提现金额(以分为单位)
|
||||
Db::name('distribution_channel')
|
||||
->where('id', $channelId)
|
||||
->setDec('withdrawableAmount', $amountInFen);
|
||||
|
||||
// 提交事务
|
||||
Db::commit();
|
||||
|
||||
// 获取创建的申请数据
|
||||
$withdrawal = Db::name('distribution_withdrawal')
|
||||
->alias('w')
|
||||
->join('distribution_channel c', 'w.channelId = c.id', 'left')
|
||||
->where('w.id', $withdrawalId)
|
||||
->field([
|
||||
'w.id',
|
||||
'w.channelId',
|
||||
'w.amount',
|
||||
'w.status',
|
||||
'w.payType',
|
||||
'w.applyTime',
|
||||
'c.name as channelName',
|
||||
'c.code as channelCode'
|
||||
])
|
||||
->find();
|
||||
|
||||
// 格式化返回数据(分转元)
|
||||
$result = [
|
||||
'id' => (string)$withdrawal['id'],
|
||||
'channelId' => (string)$withdrawal['channelId'],
|
||||
'channelName' => $withdrawal['channelName'] ?? '',
|
||||
'channelCode' => $withdrawal['channelCode'] ?? '',
|
||||
'amount' => round($withdrawal['amount'] / 100, 2), // 分转元,保留2位小数
|
||||
'status' => $withdrawal['status'],
|
||||
'payType' => !empty($withdrawal['payType']) ? $withdrawal['payType'] : null, // 支付类型:wechat、alipay、bankcard(创建时为null)
|
||||
'applyDate' => !empty($withdrawal['applyTime']) ? date('Y/m/d', $withdrawal['applyTime']) : '',
|
||||
'reviewDate' => null,
|
||||
'reviewer' => null,
|
||||
'remark' => null,
|
||||
];
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'success' => true,
|
||||
'msg' => '提现申请提交成功',
|
||||
'data' => $result
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
return json([
|
||||
'code' => $e->getCode() ?: 500,
|
||||
'success' => false,
|
||||
'msg' => '提交提现申请失败:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核提现申请(通过/拒绝)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function review()
|
||||
{
|
||||
try {
|
||||
// 获取参数
|
||||
$id = $this->request->param('id', 0);
|
||||
$action = $this->request->param('action', ''); // approve 或 reject
|
||||
$remark = $this->request->param('remark', '');
|
||||
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$reviewer = $this->getUserInfo('username') ?: $this->getUserInfo('account') ?: '系统管理员';
|
||||
|
||||
// 参数验证
|
||||
if (empty($id)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '申请ID不能为空',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
if (!in_array($action, ['approve', 'reject'])) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '审核操作参数错误,必须为 approve 或 reject',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 如果是拒绝,备注必填
|
||||
if ($action === 'reject' && empty($remark)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '拒绝申请时,拒绝理由不能为空',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 检查申请是否存在且属于当前公司
|
||||
$withdrawal = Db::name('distribution_withdrawal')
|
||||
->where([
|
||||
['id', '=', $id],
|
||||
['companyId', '=', $companyId]
|
||||
])
|
||||
->find();
|
||||
|
||||
if (!$withdrawal) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'success' => false,
|
||||
'msg' => '提现申请不存在或没有权限',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 检查申请状态
|
||||
if ($withdrawal['status'] !== DistributionWithdrawal::STATUS_PENDING) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '该申请已审核,无法重复审核',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
Db::startTrans();
|
||||
try {
|
||||
$updateData = [
|
||||
'reviewTime' => time(),
|
||||
'reviewer' => $reviewer,
|
||||
'remark' => $remark ?: '',
|
||||
'updateTime' => time(),
|
||||
];
|
||||
|
||||
if ($action === 'approve') {
|
||||
// 审核通过
|
||||
$updateData['status'] = DistributionWithdrawal::STATUS_APPROVED;
|
||||
} else {
|
||||
// 审核拒绝,退回可提现金额(金额以分存储)
|
||||
$updateData['status'] = DistributionWithdrawal::STATUS_REJECTED;
|
||||
|
||||
// 退回渠道可提现金额(以分为单位)
|
||||
Db::name('distribution_channel')
|
||||
->where('id', $withdrawal['channelId'])
|
||||
->setInc('withdrawableAmount', intval($withdrawal['amount']));
|
||||
}
|
||||
|
||||
// 更新申请状态
|
||||
Db::name('distribution_withdrawal')
|
||||
->where('id', $id)
|
||||
->update($updateData);
|
||||
|
||||
// 提交事务
|
||||
Db::commit();
|
||||
|
||||
$msg = $action === 'approve' ? '审核通过成功' : '审核拒绝成功';
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'success' => true,
|
||||
'msg' => $msg,
|
||||
'data' => [
|
||||
'id' => (string)$id,
|
||||
'status' => $updateData['status']
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
return json([
|
||||
'code' => $e->getCode() ?: 500,
|
||||
'success' => false,
|
||||
'msg' => '审核失败:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打款(标记为已打款)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function markPaid()
|
||||
{
|
||||
try {
|
||||
// 获取参数
|
||||
$id = $this->request->param('id', 0);
|
||||
$payType = $this->request->param('payType', ''); // 支付类型:wechat、alipay、bankcard
|
||||
$remark = $this->request->param('remark', '');
|
||||
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
// 参数验证
|
||||
if (empty($id)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '申请ID不能为空',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 验证支付类型
|
||||
$validPayTypes = [
|
||||
DistributionWithdrawal::PAY_TYPE_WECHAT,
|
||||
DistributionWithdrawal::PAY_TYPE_ALIPAY,
|
||||
DistributionWithdrawal::PAY_TYPE_BANKCARD
|
||||
];
|
||||
if (empty($payType) || !in_array($payType, $validPayTypes)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '支付类型不能为空,必须为:wechat(微信)、alipay(支付宝)、bankcard(银行卡)',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 检查申请是否存在且属于当前公司
|
||||
$withdrawal = Db::name('distribution_withdrawal')
|
||||
->where([
|
||||
['id', '=', $id],
|
||||
['companyId', '=', $companyId]
|
||||
])
|
||||
->find();
|
||||
|
||||
if (!$withdrawal) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'success' => false,
|
||||
'msg' => '提现申请不存在或没有权限',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 检查申请状态(只有已通过的申请才能打款)
|
||||
if ($withdrawal['status'] !== DistributionWithdrawal::STATUS_APPROVED) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '只有已通过的申请才能标记为已打款',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 更新状态为已打款
|
||||
$result = Db::name('distribution_withdrawal')
|
||||
->where('id', $id)
|
||||
->update([
|
||||
'status' => DistributionWithdrawal::STATUS_PAID,
|
||||
'payType' => $payType,
|
||||
'remark' => !empty($remark) ? $remark : $withdrawal['remark'],
|
||||
'updateTime' => time()
|
||||
]);
|
||||
|
||||
if ($result === false) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'success' => false,
|
||||
'msg' => '标记打款失败',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'success' => true,
|
||||
'msg' => '标记打款成功',
|
||||
'data' => [
|
||||
'id' => (string)$id,
|
||||
'status' => DistributionWithdrawal::STATUS_PAID,
|
||||
'payType' => $payType
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
return json([
|
||||
'code' => $e->getCode() ?: 500,
|
||||
'success' => false,
|
||||
'msg' => '标记打款失败:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提现申请详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detail()
|
||||
{
|
||||
try {
|
||||
// 获取参数
|
||||
$id = $this->request->param('id', 0);
|
||||
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
// 参数验证
|
||||
if (empty($id)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'success' => false,
|
||||
'msg' => '申请ID不能为空',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 查询申请详情(关联渠道表)
|
||||
$withdrawal = Db::name('distribution_withdrawal')
|
||||
->alias('w')
|
||||
->join('distribution_channel c', 'w.channelId = c.id AND c.deleteTime = 0', 'left')
|
||||
->where([
|
||||
['w.id', '=', $id],
|
||||
['w.companyId', '=', $companyId]
|
||||
])
|
||||
->field([
|
||||
'w.id',
|
||||
'w.channelId',
|
||||
'w.amount',
|
||||
'w.status',
|
||||
'w.payType',
|
||||
'w.applyTime',
|
||||
'w.reviewTime',
|
||||
'w.reviewer',
|
||||
'w.remark',
|
||||
'c.name as channelName',
|
||||
'c.code as channelCode'
|
||||
])
|
||||
->find();
|
||||
|
||||
if (!$withdrawal) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'success' => false,
|
||||
'msg' => '提现申请不存在或没有权限',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 格式化返回数据(分转元)
|
||||
$result = [
|
||||
'id' => (string)$withdrawal['id'],
|
||||
'channelId' => (string)$withdrawal['channelId'],
|
||||
'channelName' => $withdrawal['channelName'] ?? '',
|
||||
'channelCode' => $withdrawal['channelCode'] ?? '',
|
||||
'amount' => round($withdrawal['amount'] / 100, 2), // 分转元,保留2位小数
|
||||
'status' => $withdrawal['status'],
|
||||
'payType' => !empty($withdrawal['payType']) ? $withdrawal['payType'] : null, // 支付类型:wechat、alipay、bankcard
|
||||
'applyDate' => !empty($withdrawal['applyTime']) ? date('Y/m/d', $withdrawal['applyTime']) : '',
|
||||
'reviewDate' => !empty($withdrawal['reviewTime']) ? date('Y-m-d H:i:s', $withdrawal['reviewTime']) : null,
|
||||
'reviewer' => !empty($withdrawal['reviewer']) ? $withdrawal['reviewer'] : null,
|
||||
'remark' => !empty($withdrawal['remark']) ? $withdrawal['remark'] : null,
|
||||
];
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'success' => true,
|
||||
'msg' => '获取成功',
|
||||
'data' => $result
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
return json([
|
||||
'code' => $e->getCode() ?: 500,
|
||||
'success' => false,
|
||||
'msg' => '获取详情失败:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user