feat(fsgx): 完成全部24项开发任务 Phase1-7

Phase1 后端核心:
- 新增 fsgx_v1.sql 迁移脚本(is_queue_goods/frozen_points/available_points/no_assess)
- SystemConfigServices 返佣设置扩展(周期人数/分档比例/范围/时机)
- StoreOrderCreateServices 周期循环佣金计算
- StoreOrderTakeServices 佣金发放后同步冻结积分
- StoreProductServices/StoreProduct 保存 is_queue_goods

Phase2 后端接口:
- GET /api/hjf/brokerage/progress 佣金周期进度
- GET /api/hjf/assets/overview 资产总览
- HjfPointsServices 每日 frozen_points 0.4‰ 释放定时任务
- PUT /adminapi/hjf/member/{uid}/no_assess 不考核接口
- GET /adminapi/hjf/points/release_log 积分日志接口

Phase3 前端清理:
- hjfCustom.js 路由精简(仅保留 points/log)
- hjfQueue.js/hjfMember.js API 清理/重定向至 CRMEB 原生接口
- pages.json 公排→推荐佣金/佣金记录/佣金规则

Phase4-5 前端改造:
- queue/status.vue 推荐佣金进度页整体重写
- 商品详情/订单确认/支付结果页文案与逻辑改造
- 个人中心/资产页/引导页/规则页文案改造
- HjfQueueProgress/HjfRefundNotice/HjfAssetCard 组件改造
- 推广中心嵌入佣金进度摘要
- hjfMockData.js 全量更新(公排字段→佣金字段)

Phase6 Admin 增强:
- 用户列表新增 frozen_points/available_points 列及不考核操作按钮
- hjfPoints.js USE_MOCK=false 对接真实积分日志接口

Phase7 配置文档:
- docs/fsgx-phase7-config-checklist.md 后台配置与全链路验收清单

Made-with: Cursor
This commit is contained in:
apple
2026-03-23 22:32:19 +08:00
parent 788ee0c0c0
commit 434aa8c69d
13098 changed files with 2008990 additions and 961 deletions

View File

@@ -0,0 +1,250 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\supplier;
use app\Request;
use app\services\BaseServices;
use app\dao\supplier\SystemSupplierDao;
use app\services\system\SystemMenusServices;
use app\services\system\SystemRoleServices;
use crmeb\exceptions\AdminException;
use app\dao\system\admin\SystemAdminDao;
use crmeb\exceptions\AuthException;
use crmeb\services\CacheService;
use crmeb\utils\ApiErrorCode;
use crmeb\utils\JwtAuth;
use Firebase\JWT\ExpiredException;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Cache;
/**
*
* Class LoginServices
* @package app\services\supplier
* @mixin SystemSupplierDao
*/
class LoginServices extends BaseServices
{
/**
* 权限缓存前缀
*/
const SUPPLIER_RULES_LEVEL = 'store_supplier_rules_level_';
/**
* @var SystemAdminDao
*/
#[Inject]
protected SystemAdminDao $adminDao;
/**
* @var SystemSupplierDao
*/
#[Inject]
protected SystemSupplierDao $dao;
/**
* 获取登陆前的login等信息
* @return array
*/
public function getLoginInfo()
{
return [
'slide' => sys_data('admin_login_slide') ?? [],
'logo_square' => sys_config('site_logo_square'),//透明
'logo_rectangle' => sys_config('site_logo'),//方形
'login_logo' => sys_config('login_logo'),//登陆
'site_name' => sys_config('site_name'),
'site_url' => sys_config('site_url'),
'upload_file_size_max' => config('upload.filesize'),//文件上传大小kb
];
}
/**
* H5账号登陆
* @param $account
* @param $password
* @param $type
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function login($account, $password, $type)
{
$supplierInfo = $this->adminDao->getOne(['admin_type' => 4, 'account' => $account, 'is_del' => 0], '*', ['supplier']);
$key = 'supplier_login_captcha_' . $account;
if (!$supplierInfo) {
Cache::inc($key);
throw new AdminException('账号不存在!');
}
if ($password) {//平台还可以登录
if (!$supplierInfo->status || !$supplierInfo->is_show) {
Cache::inc($key);
throw new AdminException('您已被禁止登录!');
}
if (!password_verify($password, $supplierInfo->pwd)) {
Cache::inc($key);
throw new AdminException('账号或密码错误,请重新输入');
}
}
$supplierInfo->last_time = time();
$supplierInfo->last_ip = app('request')->ip();
$supplierInfo->login_count++;
$supplierInfo->save();
$tokenInfo = $this->createToken($supplierInfo['id'], $type, $supplierInfo->pwd);
/** @var SystemMenusServices $services */
$services = app()->make(SystemMenusServices::class);
[$menus, $uniqueAuth] = $services->getMenusList($supplierInfo->roles, 0, 4);
return [
'token' => $tokenInfo['token'],
'expires_time' => $tokenInfo['params']['exp'],
'menus' => $menus,
'unique_auth' => $uniqueAuth,
'user_info' => [
'id' => $supplierInfo->getData('id'),
'account' => $supplierInfo->getData('account'),
'avatar' => $supplierInfo->getData('head_pic'),
],
'logo' => sys_config('site_logo'),
'logo_square' => sys_config('site_logo_square'),
'version' => get_crmeb_version(),
'newOrderAudioLink' => get_file_link(sys_config('new_order_audio_link', ''))
];
}
/**
* 重置密码
* @param $account
* @param $password
*/
public function reset($account, $password)
{
$user = $this->dao->getOne(['account|phone' => $account]);
if (!$user) {
throw new ValidateException('用户不存在');
}
if (!$this->dao->update($user['id'], ['pwd' => md5((string)$password)])) {
throw new ValidateException('修改密码失败');
}
return true;
}
/**
* 获取Admin授权信息
* @param string $token
* @return array
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function parseToken(string $token): array
{
/** @var CacheService $cacheService */
$cacheService = app()->make(CacheService::class);
if (!$token || $token === 'undefined') {
throw new AuthException(ApiErrorCode::ERR_LOGIN);
}
//检测token是否过期
$md5Token = md5($token);
if (!$cacheService->hasToken($md5Token) || !($cacheToken = $cacheService->getTokenBucket($md5Token))) {
throw new AuthException(ApiErrorCode::ERR_LOGIN);
}
//是否超出有效次数
if (isset($cacheToken['invalidNum']) && $cacheToken['invalidNum'] >= 3) {
if (!request()->isCli()) {
$cacheService->clearToken($md5Token);
}
throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
}
/** @var JwtAuth $jwtAuth */
$jwtAuth = app()->make(JwtAuth::class);
//设置解析token
[$id, $type, $auth] = $jwtAuth->parseToken($token);
//验证token
try {
$jwtAuth->verifyToken();
$cacheService->setTokenBucket($md5Token, $cacheToken, $cacheToken['exp']);
} catch (ExpiredException $e) {
$cacheToken['invalidNum'] = isset($cacheToken['invalidNum']) ? $cacheToken['invalidNum']++ : 1;
$cacheService->setTokenBucket($md5Token, $cacheToken, $cacheToken['exp']);
} catch (\Throwable $e) {
if (!request()->isCli()) {
$cacheService->clearToken($md5Token);
}
throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
}
//获取管理员信息
$adminInfo = $this->adminDao->getOne(['id' => $id, 'is_del' => 0, 'status' => 1]);
if (!$adminInfo) {
throw new AuthException(ApiErrorCode::ERR_ADMINID_VOID);
}
if ($auth !== md5($adminInfo->pwd)) {
throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
}
$supplierInfo = $this->dao->getOne(['id' => (int)$adminInfo->relation_id, 'is_del' => 0], '*', ['admin']);
if (!$supplierInfo || !$supplierInfo->account || $supplierInfo->admin_is_del) {
if (!request()->isCli()) {
$cacheService->clearToken($md5Token);
}
throw new AuthException(ApiErrorCode::ERR_LOGIN_STATUS);
}
$supplierInfo->type = $type;
return $supplierInfo->hidden(['pwd', 'is_del', 'status'])->toArray();
}
/**
* 后台验证权限
* @param Request $request
* @return bool
*/
public function verifiAuth(Request $request)
{
// TODO: 供应商不做验证
return true;
$rule = str_replace('supplierapi/', '', trim(strtolower($request->rule()->getRule())));
if (in_array($rule, ['supplier/logout', 'menuslist'])) {
return true;
}
$method = trim(strtolower($request->method()));
/** @var SystemRoleServices $roleServices */
$roleServices = app()->make(SystemRoleServices::class);
$auth = $roleServices->getAllRoles(2, 4, self::SUPPLIER_RULES_LEVEL);
//验证访问接口是否存在
if ($auth && !in_array($method . '@@' . $rule, array_map(function ($item) {
return trim(strtolower($item['methods'])) . '@@' . trim(strtolower(str_replace(' ', '', $item['api_url'])));
}, $auth))) {
return true;
}
$auth = $roleServices->getRolesByAuth($request->supplierInfo()['roles'], 2, 4, self::SUPPLIER_RULES_LEVEL);
//验证访问接口是否有权限
if ($auth && empty(array_filter($auth, function ($item) use ($rule, $method) {
if (trim(strtolower($item['api_url'])) === $rule && $method === trim(strtolower($item['methods'])))
return true;
}))) {
throw new AuthException(ApiErrorCode::ERR_AUTH);
}
}
}

View File

@@ -0,0 +1,82 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\supplier;
use app\dao\supplier\SupplierTicketPrintDao;
use app\services\BaseServices;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 小票打印
* Class SupplierTicketPrintServices
* @package app\services\supplier
* @mixin SupplierTicketPrintDao
*/
class SupplierTicketPrintServices extends BaseServices
{
/**
* @var SupplierTicketPrintDao
*/
#[Inject]
protected SupplierTicketPrintDao $dao;
/**
* 获取打印配置
* @param int $supplierId
* @param string $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getTicketInfo(int $supplierId, string $field = '*')
{
$info = $this->dao->getOne(['supplier_id' => $supplierId], $field);
if ($info) {
$data = $info->toArray();
} else {
$data = [
'id' => 0,
'supplier_id' => $supplierId,
'develop_id' => 0,
'api_key' => '',
'client_id' => '',
'terminal_number' => '',
'status' => 0,
];
}
return $data;
}
/**
* 更新打印配置
* @param int $supplierId
* @param $data
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function savePrintData(int $supplierId, $data)
{
$info = $this->dao->getOne(['supplier_id' => $supplierId], 'id, supplier_id');
if ($info) {
$res = $this->dao->update($info['id'], $data);
} else {
$data['supplier_id'] = $supplierId;
$res = $this->dao->save($data);
}
if (!$res) throw new ValidateException('保存失败!');
return true;
}
}

View File

@@ -0,0 +1,352 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\supplier;
use app\dao\supplier\SystemSupplierDao;
use app\dao\system\admin\SystemAdminDao;
use app\services\BaseServices;
use app\services\order\StoreOrderServices;
use app\services\order\StoreOrderRefundServices;
use app\services\product\branch\StoreBranchProductServices;
use app\services\system\admin\SystemAdminServices;
use app\services\system\attachment\SystemAttachmentServices;
use app\services\system\SystemUserApplyServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 供应商
* Class SystemSupplierServices
* @package app\services\supplier
* @mixin SystemSupplierDao
*/
class SystemSupplierServices extends BaseServices
{
/**
* @var SystemAdminDao
*/
#[Inject]
protected SystemAdminDao $adminDao;
/**
* @var SystemSupplierDao
*/
#[Inject]
protected SystemSupplierDao $dao;
/**
* 获取供应商
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\DataNotFoundException
*/
public function getSupplierInfo(int $id, string $field = '*', array $with = [])
{
$info = $this->dao->getOne(['id' => $id, 'is_del' => 0], $field, $with);
if (!$info) {
throw new ValidateException('供应商不存在');
}
return $info;
}
/**
* 供应商列表
* @param array $where
* @param array $field
* @return void
*/
public function getSupplierList(array $where, array $field = ['*'])
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getSupplierList($where, $field, $page, $limit);
if (array_search('add_time', $field)) {
foreach ($list as &$item) {
$item['_add_time'] = date('Y-m-d H:i:s', $item['add_time']);
}
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 保存供应商
* @param array $data
* @return mixed
*/
public function create(array $data)
{
if ($this->adminDao->count(['account' => $data['account'], 'admin_type' => 4, 'is_del' => 0])) {
throw new AdminException('管理员账号已存在');
}
return $this->transaction(function () use ($data) {
$adminData = [
'pwd' => $this->passwordHash($data['pwd']),
'admin_type' => 4,
'account' => $data['account'],
'roles' => '',
'real_name' => $data['name'],
'phone' => $data['phone'],
'add_time' => time(),
'level' => 0
];
unset($data['pwd'], $data['conf_pwd'], $data['account']);
// 创建管理员
$res = $this->adminDao->save($adminData);
if (!$res) throw new AdminException('管理员添加失败');
$data['admin_id'] = (int)$res->id;
$data['add_time'] = time();
// 创建供应商
$relation_id = $this->dao->save($data)->id;
if (!$relation_id) throw new AdminException('供应商添加失败');
$this->adminDao->update($res->id, ['relation_id' => $relation_id]);
return $relation_id;
});
}
/**
* 修改管理员
* @param array $data
* @return mixed
*/
public function save(int $id, array $data)
{
return $this->transaction(function () use ($id, $data) {
if (!$supplierInfo = $this->dao->get($id)) {
throw new AdminException('供应商不存在,无法修改');
}
if ($supplierInfo->is_del) {
throw new AdminException('供应商已经删除');
}
if (!$adminInfo = $this->adminDao->get($supplierInfo['admin_id'])) {
throw new AdminException('管理员不存在,无法修改');
}
if ($adminInfo->is_del) {
throw new AdminException('管理员已经删除');
}
//修改账号
if (isset($data['account']) && $data['account'] != $adminInfo->account && $this->adminDao->isAccountUsable($data['account'], $supplierInfo['admin_id'], 4)) {
throw new AdminException('管理员账号已存在');
}
if (isset($data['phone']) && $data['phone'] != $adminInfo->phone && $this->adminDao->count(['phone' => $data['phone'], 'admin_type' => 4, 'is_del' => 0])) {
throw new AdminException('管理员电话已存在');
}
if ($data['pwd']) {
if (!$data['conf_pwd']) {
throw new AdminException('请输入确认密码');
}
if ($data['conf_pwd'] != $data['pwd']) {
throw new AdminException('上次输入的密码不相同');
}
/** @var SystemAdminServices $adminServices */
$adminServices = app()->make(SystemAdminServices::class);
$adminInfo->pwd = $adminServices->passwordHash($data['pwd']);
}
$adminInfo->real_name = $data['name'] ?? $adminInfo->real_name;
$adminInfo->phone = $data['phone'] ?? $adminInfo->phone;
$adminInfo->account = $data['account'] ?? $adminInfo->account;
unset($data['account']);
unset($data['pwd']);
unset($data['conf_pwd']);
// 修改管理员
$res = $adminInfo->save();
if (!$res) throw new AdminException('管理员修改失败');
$supplierInfo->supplier_name = $data['supplier_name'];
$supplierInfo->name = $data['name'];
$supplierInfo->phone = $data['phone'];
$supplierInfo->email = $data['email'];
$supplierInfo->address = $data['address'];
$supplierInfo->province = $data['province'];
$supplierInfo->city = $data['city'];
$supplierInfo->area = $data['area'];
$supplierInfo->street = $data['street'];
$supplierInfo->detailed_address = $data['detailed_address'];
$supplierInfo->sort = $data['sort'];
$supplierInfo->is_show = $data['is_show'];
$supplierInfo->mark = $data['mark'];
// 修改供应商
$res1 = $supplierInfo->save();
if (!$res1) throw new AdminException('供应商修改失败');
return true;
});
}
/**
* @param int $id
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function delete(int $id)
{
if (!$supplierInfo = $this->dao->get($id)) {
throw new AdminException('供应商不存在,无法修改');
}
if ($supplierInfo->is_del) {
throw new AdminException('供应商已经删除');
}
if (!$adminInfo = $this->adminDao->get($supplierInfo['admin_id'])) {
throw new AdminException('管理员不存在,无法删除');
}
if ($adminInfo->is_del) {
throw new AdminException('管理员已经删除');
}
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
$orderCount = $storeOrderServices->count(['supplier_id' => $id, 'status' => 0]);
if (!$orderCount) {
$orderCount = $storeOrderServices->count(['supplier_id' => $id, 'status' => 1]);
if (!$orderCount) {
$orderCount = $storeOrderServices->count(['supplier_id' => $id, 'status' => 5]);
}
}
if ($orderCount) {
throw new ValidateException('删除失败,该供应商还有待处理订单');
}
return $this->transaction(function () use ($id, $supplierInfo, $adminInfo) {
$adminInfo->status = 0;
$adminInfo->is_del = 1;
// 修改管理员
$res = $adminInfo->save();
if (!$res) throw new AdminException('管理员删除失败');
$supplierInfo->is_show = 0;
$supplierInfo->is_del = 1;
// 修改供应商
$res1 = $supplierInfo->save();
if (!$res1) throw new AdminException('供应商删除失败');
/** @var StoreBranchProductServices $storeBranchProducesServices */
$storeBranchProducesServices = app()->make(StoreBranchProductServices::class);
//删除供应商商品
$storeBranchProducesServices->deleteProducts([], 2, $id);
/** @var SystemAttachmentServices $attach */
$attach = app()->make(SystemAttachmentServices::class);
//删除附件
$attach->delAttachment([], 4, $id);
return true;
});
}
/**
* 平台供应商运营统计
* @param int $supplierId
* @param array $time
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function supplierChart(int $supplierId, array $time)
{
$list = $this->dao->getSupplierList(['is_del' => 0, 'is_show' => 1], ['id', 'supplier_name']);
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
/** @var StoreOrderRefundServices $orderRefundServices */
$orderRefundServices = app()->make(StoreOrderRefundServices::class);
$where = ['time' => $time];
$order_where = ['paid' => 1, 'pid' => 0, 'is_del' => 0, 'is_system_del' => 0, 'refund_status' => [0, 3]];
$refund_where = ['refund_type' => 6];
foreach ($list as &$item) {
$supplier_where = ['supplier_id' => $item['id']];
$item['order_price'] = $orderServices->sum($where + $supplier_where + $order_where, 'pay_price', true);
$item['order_count'] = $orderServices->count($where + $supplier_where + $order_where);
$item['refund_order_price'] = $orderRefundServices->sum($where + $supplier_where + $refund_where, 'refunded_price', true);
$item['refund_order_count'] = $orderRefundServices->count($where + $supplier_where + $refund_where);
}
return $list;
}
/**
* 供应商选择列表
* @param array $where
* @param array $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getSupplierSearch(array $where, array $field = ['*'])
{
return $this->dao->getSupplierList($where, $field, 0, 0);
}
/**
* 供应商入驻审核通过创建数据
* @param int $applyId
* @param array $info
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function verifyAgreeCreate(int $applyId, array $info = [])
{
if (!$applyId) {
throw new ValidateException('缺少申请ID');
}
/** @var SystemUserApplyServices $applyServices */
$applyServices = app()->make(SystemUserApplyServices::class);
if (!$info) {
$info = $applyServices->get($applyId);
if (!$info) {
throw new ValidateException('申请数据不存在');
}
$info = $info->toArray();
}
$data = [
'supplier_name' => $info['system_name'],
'account' => $this->getAccount($info['phone']),
'phone' => $info['phone'],
'name' => $info['name'],
'pwd' => substr($info['phone'], -6)
];
$supplier_id = $this->create($data);
return $this->dao->get($supplier_id)->toArray();
}
/**
* 获取同意申请 创建账号
* @param string $phone
* @return string
*/
public function getAccount(string $phone)
{
$account = '';
if ($phone) {
//当前手机号当作账号是否存在
$adminDCount = $this->adminDao->count(['account' => $phone, 'admin_type' => 4, 'is_del' => 0]);
$account = $phone;
if ($adminDCount) {
$account = $account . '_' . $adminDCount;
}
}
return $account;
}
}

View File

@@ -0,0 +1,232 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\supplier\finance;
use app\dao\supplier\finance\SupplierExtractDao;
use app\services\BaseServices;
use app\services\supplier\SystemSupplierServices;
use app\services\system\admin\SystemAdminServices;
use crmeb\services\FormBuilder as Form;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Route as Url;
/**
* 门店提现
* Class StoreExtractServices
* @package app\services\store\finance
* @mixin SupplierExtractDao
*/
class SupplierExtractServices extends BaseServices
{
/**
* @var SupplierExtractDao
*/
#[Inject]
protected SupplierExtractDao $dao;
/**
* 获取一条提现记录
* @param int $id
* @param array $field
* @return array|\think\Model|null
*/
public function getExtract(int $id, array $field = [])
{
return $this->dao->get($id, $field);
}
/**
* 显示资源列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index(array $where, array $whereData = [])
{
$list = $this->getStoreExtractList($where);
//待审核金额
$where['status'] = 0;
$extract_statistics['price'] = $this->dao->getExtractMoneyByWhere($where, 'extract_price');
//待转账金额
$where['status'] = 1;
$where['pay_status'] = 0;
$extract_statistics['unPayPrice'] = $this->dao->getExtractMoneyByWhere($where, 'extract_price');
//累计提现
$where['status'] = 1;
$where['pay_status'] = 1;
$extract_statistics['paidPrice'] = $this->dao->getExtractMoneyByWhere($where, 'extract_price');
$extract_statistics['price_count'] = 0;
//未提现金额
/** @var SupplierFlowingWaterServices $financeFlowServices */
$financeFlowServices = app()->make(SupplierFlowingWaterServices::class);
$price_not = $financeFlowServices->getSumFinance(['supplier_id' => isset($where['supplier_id']) && $where['supplier_id'] ? $where['supplier_id'] : 0], $whereData);
$extract_statistics['price_not'] = max($price_not, 0);
$extract_statistics['extract_min_price'] = sys_config('supplier_extract_min_price');
$extract_statistics['extract_max_price'] = sys_config('supplier_extract_max_price');
return compact('extract_statistics', 'list');
}
/**
* 获取提现列表
* @param array $where
* @param string $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getStoreExtractList(array $where, string $field = '*')
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getExtractList($where, $field, ['supplier'], $page, $limit);
if ($list) {
/** @var SystemAdminServices $adminServices */
$adminServices = app()->make(SystemAdminServices::class);
$adminIds = array_unique(array_column($list, 'admin_id'));
$adminInfos = [];
if ($adminIds) $adminInfos = $adminServices->getColumn([['id', 'in', $adminIds]], 'id,real_name', 'id');
foreach ($list as &$item) {
$item['add_time'] = $item['add_time'] ? date("Y-m-d H:i:s", $item['add_time']) : '';
$item['admin_name'] = $item['admin_id'] ? ($adminInfos[$item['admin_id']]['real_name'] ?? '') : '';
}
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 提现申请
* @param int $supplierId
* @param array $data
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function cash(int $supplierId, array $data)
{
/** @var SystemSupplierServices $systemSupplier */
$systemSupplier = app()->make(SystemSupplierServices::class);
$supplierInfo = $systemSupplier->getSupplierInfo($supplierId);
$insertData = [];
switch ($data['extract_type']) {
case 'bank':
if (!$supplierInfo['bank_code'] || !$supplierInfo['bank_address']) {
throw new ValidateException('请先设置提现银行与开户行信息');
}
$insertData['bank_code'] = $supplierInfo['bank_code'];
$insertData['bank_address'] = $supplierInfo['bank_address'];
break;
case 'alipay':
if (!$supplierInfo['alipay_account'] || !$supplierInfo['alipay_qrcode_url']) {
throw new ValidateException('请先设置提现支付宝信息');
}
$insertData['alipay_account'] = $supplierInfo['alipay_account'];
$insertData['qrcode_url'] = $supplierInfo['alipay_qrcode_url'];
break;
case 'weixin':
if (!$supplierInfo['wechat'] || !$supplierInfo['wechat_qrcode_url']) {
throw new ValidateException('请先设置提现微信信息');
}
$insertData['wechat'] = $supplierInfo['wechat'];
$insertData['qrcode_url'] = $supplierInfo['wechat_qrcode_url'];
break;
default:
throw new ValidateException('暂不支持该类型提现');
break;
}
$insertData['supplier_id'] = $supplierInfo['id'];
$insertData['extract_type'] = $data['extract_type'];
$insertData['extract_price'] = $data['money'];
$insertData['add_time'] = time();
$insertData['supplier_mark'] = $data['mark'];
$insertData['status'] = 0;
if (!$this->dao->save($insertData)) {
return false;
}
return true;
}
/**
* 拒绝
* @param $id
* @return mixed
*/
public function refuse(int $id, string $message, int $adminId)
{
$extract = $this->getExtract($id);
if (!$extract) {
throw new ValidateException('操作记录不存在!');
}
if ($extract->status == 1) {
throw new ValidateException('已经提现,错误操作');
}
if ($extract->status == -1) {
throw new ValidateException('您的提现申请已被拒绝,请勿重复操作!');
}
if ($this->dao->update($id, ['fail_time' => time(), 'fail_msg' => $message, 'status' => -1, 'admin_id' => $adminId])) {
return true;
} else {
throw new ValidateException('操作失败!');
}
}
/**
* 通过
* @param $id
* @return mixed
*/
public function adopt(int $id, int $adminId)
{
$extract = $this->getExtract($id);
if (!$extract) {
throw new ValidateException('操作记录不存!');
}
if ($extract->status == 1) {
throw new ValidateException('您已提现,请勿重复提现!');
}
if ($extract->status == -1) {
throw new ValidateException('您的提现申请已被拒绝!');
}
if ($this->dao->update($id, ['status' => 1, 'admin_id' => $adminId])) {
return true;
} else {
throw new ValidateException('操作失败!');
}
}
/**
* 转账页面
* @param int $id
* @return string
*/
public function add_transfer(int $id)
{
$field = array();
$title = '转账信息';
$field[] = Form::input('voucher_title', '转账说明', '')->maxlength(30)->required();
$field[] = Form::frameImage('voucher_image', '转账凭证', Url::buildUrl(config('admin.admin_prefix') . '/widget.images/index', array('fodder' => 'voucher_image')), '')->icon('ios-add')->width('960px')->height('505px')->modal(['footer-hide' => true]);
return create_form($title, $field, Url::buildUrl('/supplier/extract/save_transfer/' . $id), 'POST');
}
}

View File

@@ -0,0 +1,250 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\supplier\finance;
use app\dao\supplier\finance\SupplierFlowingWaterDao;
use app\services\BaseServices;
use app\services\order\StoreOrderCreateServices;
use app\services\order\StoreOrderCartInfoServices;
use app\services\order\StoreOrderRefundServices;
use app\services\order\StoreOrderServices;
use think\annotation\Inject;
/**
* 供应商流水
* Class SupplierFlowingWaterServices
* @package app\services\supplier\finance
* @mixin SupplierFlowingWaterDao
*/
class SupplierFlowingWaterServices extends BaseServices
{
/**
* 支付类型
* @var string[]
*/
public array $pay_type = ['weixin' => '微信支付', 'yue' => '余额支付', 'offline' => '线下支付', 'alipay' => '支付宝支付', 'cash' => '现金支付', 'automatic' => '自动转账', 'store' => '微信支付'];
/**
* 交易类型
* @var string[]
*/
public array $type = [
1 => '支付订单',
2 => '退款订单'
];
/**
* @var SupplierFlowingWaterDao
*/
#[Inject]
protected SupplierFlowingWaterDao $dao;
/**
* 显示资源列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, '*', $page, $limit, ['user', 'supplierName']);
foreach ($list as &$item) {
$item['type_name'] = isset($this->type[$item['type']]) ? $this->type[$item['type']] : '其他类型';
$item['pay_type_name'] = isset($this->pay_type[$item['pay_type']]) ? $this->pay_type[$item['pay_type']] : '其他方式';
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '';
$item['finish_time'] = $item['finish_time'] ? date('Y-m-d H:i:s', $item['finish_time']) : '';
$item['trade_time'] = $item['trade_time'] ? date('Y-m-d H:i:s', $item['trade_time']) : $item['add_time'];
$item['user_nickname'] = $item['user_nickname'] ?: '游客';
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 供应商账单
* @param $where
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getFundRecord($where)
{
[$page, $limit] = $this->getPageValue();
$where['is_del'] = 0;
$data = $this->dao->getFundRecord($where, $page, $limit);
$i = 1;
foreach ($data['list'] as &$item) {
$item['id'] = $i;
$i++;
$item['entry_num'] = bcsub($item['income_num'], $item['exp_num'], 2);
switch ($where['timeType']) {
case "day" :
$item['title'] = "日账单";
$item['add_time'] = date('Y-m-d', $item['add_time']);
break;
case "week" :
$item['title'] = "周账单";
$item['add_time'] = '第' . $item['day'] . '周(' . date('m', $item['add_time']) . '月)';
break;
case "month" :
$item['title'] = "月账单";
$item['add_time'] = date('Y-m', $item['add_time']);
break;
}
}
return $data;
}
/**
* 获取百分比
* @param $num
* @return string|null
*/
public function getPercent($num)
{
return bcdiv($num, '100', 4);
}
/**
* 写入流水账单
* @param $oid
* @param $type
* @return bool|void
* @throws \Exception
*/
public function setSupplierFinance($oid, $type = 1)
{
/** @var SupplierTransactionsServices $transactionsServices */
$transactionsServices = app()->make(SupplierTransactionsServices::class);
/** @var StoreOrderCartInfoServices $cartInfoServices */
$cartInfoServices = app()->make(StoreOrderCartInfoServices::class);
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
/** @var StoreOrderRefundServices $storeOrderRefundServices */
$storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
$order = $storeOrderServices->get($oid);
if (!$order) {
return true;
}
if ($order['supplier_id'] <= 0) return true;
$data = $cartInfoServices->getOrderCartInfoSettlePrice($order['id']);
$pay_postage = 0;
if (isset($order['shipping_type']) && !in_array($order['shipping_type'], [2, 4])) {
$pay_postage = floatval($storeOrderRefundServices->getOrderSumPrice($data['info'], 'postage_price', false));
}
if ($order['type'] == 8) {
$order['pay_price'] = $order['total_price'];
}
$append = [
'pay_price' => $order['pay_price'],
'pay_postage' => $pay_postage,
'total_price' => $order['total_price'],
];
switch ($type) {
case 1 ://支付
$number = bcadd((string)$data['settlePrice'], $pay_postage, 2);
//支付订单
$this->savaData($order, $number, 1, 1, $append);
//交易订单记录
$transactionsServices->savaData($order, 1, 1, $append);
break;
case 2://退款
$number = bcadd((string)$data['refundSettlePrice'], $pay_postage, 2);
$this->savaData($order, $number, 0, 2, $append);
//交易订单记录
$transactionsServices->savaData($order, 0, 2, $append);
break;
}
}
/**
* 写入数据
* @param $order
* @param $number
* @param $pm
* @param $type
* @param $trade_type
* @param array $append
* @throws \Exception
*/
public function savaData($order, $number, $pm, $type, array $append = [])
{
/** @var StoreOrderCreateServices $storeOrderCreateServices */
$storeOrderCreateServices = app()->make(StoreOrderCreateServices::class);
$order_id = $storeOrderCreateServices->getNewOrderId('ls');
$data = [
'supplier_id' => $order['supplier_id'] ?? 0,
'uid' => $order['uid'] ?? 0,
'order_id' => $order_id,
'link_id' => $order['order_id'] ?? '',
'pay_type' => $order['pay_type'] ?? '',
'trade_time' => $order['pay_time'] ?? $order['add_time'] ?? '',
'pm' => $pm,
'number' => $number ?: 0,
'type' => $type,
'add_time' => time()
];
if ($type == 2) {
$data['status'] = 1;
$data['finish_time'] = time();
//更改正常订单状态
$this->dao->update(['link_id' => $order['order_id'], 'type' => 1], ['status' => 1, 'finish_time' => time()]);
}
$data = array_merge($data, $append);
$this->dao->save($data);
}
/**
* 关联门店店员
* @param $link_id
* @param int $staff_id
* @return mixed
*/
public function setStaff($link_id, int $staff_id)
{
return $this->dao->update(['link_id' => $link_id], ['staff_id' => $staff_id]);
}
/**
* 可提现金额
* @param array $where
* @return int|string
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getSumFinance(array $where, array $whereData)
{
$field = 'sum(if(pm = 1,number,0)) as income_num,sum(if(pm = 0,number,0)) as exp_num';
$whereData['status'] = 1;
$data = $this->dao->getList($whereData, $field);
if (!$data) return 0;
$income_num = $data[0]['income_num'] ?? 0;
$exp_num = $data[0]['exp_num'] ?? 0;
$number = bcsub($income_num, $exp_num, 2);
//已提现金额
/** @var SupplierExtractServices $extractServices */
$extractServices = app()->make(SupplierExtractServices::class);
$where['not_status'] = -1;
$extract_price = $extractServices->getExtractMoneyByWhere($where, 'extract_price');
$price_not = bcsub((string)$number, (string)$extract_price, 2);
return $price_not;
}
}

View File

@@ -0,0 +1,94 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\supplier\finance;
use app\dao\supplier\finance\SupplierTransactionsDao;
use app\services\BaseServices;
use app\services\order\StoreOrderCreateServices;
use think\annotation\Inject;
/**
* 门店流水
* Class StoreExtractServices
* @package app\services\store\finance
* @mixin SupplierTransactionsDao
*/
class SupplierTransactionsServices extends BaseServices
{
/**
* 支付类型
* @var string[]
*/
public array $pay_type = ['weixin' => '微信支付', 'yue' => '余额支付', 'offline' => '线下支付', 'alipay' => '支付宝支付', 'cash' => '现金支付', 'automatic' => '自动转账', 'store' => '微信支付'];
/**
* 交易类型
* @var string[]
*/
public array $type = [
1 => '支付订单',
2 => '支付订单',
3 => '订单手续费',
4 => '退款订单',
5 => '充值返点',
6 => '付费会员返点',
7 => '充值订单',
8 => '付费订单',
9 => '收银订单',
10 => '核销订单',
11 => '分配订单',
12 => '配送订单',
13 => '同城配送订单',
];
/**
* @var SupplierTransactionsDao
*/
#[Inject]
protected SupplierTransactionsDao $dao;
/**
* 写入数据
* @param $order
* @param $number
* @param $pm
* @param $type
* @param $trade_type
* @param array $append
* @throws \Exception
*/
public function savaData($order, $pm, $type, array $append = [])
{
/** @var StoreOrderCreateServices $storeOrderCreateServices */
$storeOrderCreateServices = app()->make(StoreOrderCreateServices::class);
$order_id = $storeOrderCreateServices->getNewOrderId('ls');
$data = [
'supplier_id' => $order['supplier_id'] ?? 0,
'uid' => $order['uid'] ?? 0,
'order_id' => $order_id,
'link_id' => $order['order_id'] ?? '',
'pay_type' => $order['pay_type'] ?? '',
'trade_time' => $order['pay_time'] ?? $order['add_time'] ?? '',
'pm' => $pm,
'type' => $type,
'add_time' => time()
];
if ($type == 2) {
$data['status'] = 1;
$data['finish_time'] = time();
}
$data = array_merge($data, $append);
$this->dao->save($data);
}
}