mirror of
https://github.com/crmeb/CRMEB.git
synced 2026-10-28 13:31:04 +00:00
refactor(分销): 开源侧下线佣金提现,与私有分支口径对齐
移除佣金/提现控制器与前端页面,订单与用户链路去掉分销佣金逻辑,安装 SQL 与文档同步。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
f07cc1705b
commit
d50837b663
@ -14,6 +14,8 @@ description: 目录与文档规范(全仓库唯一真源)- 三类知识目
|
||||
| `.crmeb/` | AI 开发**约定真源** | 规则、技能、子智能体、各端 scopes | 长期有效,强制约束 |
|
||||
| `tests/` | **开发过程文档 + 测试统一入口** | 过程文档(`plans/` 方案、`reports/` 评审与检查报告、`skills/` 可分发治理模板);测试索引与统一运行编排 | 过程文档有生命周期:实施后转移,失效后删除;索引与模板长期有效 |
|
||||
|
||||
**模块/插件收录边界(强制)**:`help/docs/` 只收录核心能力(`backend/app`、`backend/crmeb`、`backend/driver`)与默认安装清单内的模块/驱动(`required=true` 或 `default_install=true`)。其余可安装模块(分销、交易模式、营销活动、客服、开放平台、文件管理等)的功能、接口与数据表说明**不写入** `help/docs/`,随各自插件目录维护(约定 `modules/{domain}/{name}/README.md`);`help/docs/` 仅保留清单计数类事实(模块/驱动总数、默认安装清单),供 `check-doc-facts.mjs` 数量校验。
|
||||
|
||||
## 二、归属判定规则(强制)
|
||||
|
||||
自问一句:**这是"当前事实",还是"过程产物"?**
|
||||
|
||||
@ -302,7 +302,7 @@ onMounted(() => { getList(); });
|
||||
- **页面标题头统一 52px 紧凑标准**:全局类组合 `.i-layout-page-header.header-title`(白底、`10px 20px`、圆角 5px、下距 16px)+ `.ivu-page-header-title`(18px/500/`#17233d`/line-height 32px);带返回用 `<pages-header :title :back-url>`(组件内部即该结构),右侧说明文字放组件默认 slot。禁止再用 el-card 包页面标题(旧实现总高约 67px)。
|
||||
- **顶部操作按钮与表格同卡**:新增等顶部动作按钮渲染在数据卡内部、表格上方;间距为「上 14px / 下 14px」。实现:按钮行/按钮挂 `.action-row`(全局类,`margin-top: -6px` 抵消卡 20px 上内边距;见 `styles/style.scss`)+ 下距 `mb14` 或表格 `mt14`(等效)。`.action-row` 仅用于卡内首个动作元素;上方有搜索行/tabs 时不可挂。SchemaPage 动作区已内置同款偏移(跟在分类头后自动归零)。禁止按钮独立成卡、禁止下距超过 14px(历史问题见 13.2)。
|
||||
- 搜索表单 `inline` + `label-width="80px"` + `@submit.prevent`;搜索控件宽度统一用 `.form_content_width`(250px)/ `.form_range_content_width`(117px)。
|
||||
- 多条件搜索用 `div.acea-row.search-form` 包裹 + `collapse` 布尔做「展开/收起」。
|
||||
- 多条件搜索的额外筛选项用 `<template v-if="collapse">` 内联追加在常显项之后;查询/重置/展开收起按钮组写成末尾的无 label `el-form-item`,跟随筛选项流式换行,不固定在卡片右侧(2026-09-28 用户裁定,废弃原 `div.acea-row.search-form` + `justify-content: space-between` 右上固定方案)。
|
||||
- 表格默认 **el-table**(样式已全局定制,见 1.6);**仅树形数据用 vxe-table**(`<vxe-table-column ... tree-node>`,插槽 `v-slot="{ row }"`)。
|
||||
- 多选列 `<el-table-column type="selection">`,选中后顶部 `<el-alert show-icon>` 显示「已选择 N 项」。
|
||||
- 页面卡片一律 `shadow="never"`,卡片间距 `.ivu-mt` / `.mt16`(16px)。
|
||||
@ -466,7 +466,7 @@ function del(row, tit, num) {
|
||||
|---|---|
|
||||
| `<pagination>` | 分页(`:total` 必填,`v-model:page/limit`,`@pagination`,默认 pageSizes `[15,20,30,50]`,翻页自动回顶) |
|
||||
| `<pages-header>` | 页面标题头(统一 52px 紧凑标准;props `title` / `backUrl` / `description`):description 渲染于标题右侧、12px 次要色、前置 14px 主色 `InfoFilled` 图标、与标题基线对齐;默认 slot 置于最右侧(动作按钮等);描述文案仅说明性内容,不放操作 |
|
||||
| `<date-range-picker>` | 日期范围搜索统一入口(v-model 值为 `[start, end]`):内置 `libs/timeOptions` 快捷项(今天/昨天/本月/上月/最近N天/本年/去年)、分隔符「至」、默认 320px 对齐筛选控件基准宽并强制 `flex-grow: 0`(EP `.el-input__wrapper` 自带 `flex-grow: 1`,不压制会在 flex 搜索行内被拉伸占满);`format`/`value-format`/`clearable` 等经 attrs 透传可覆盖。时间范围筛选**不要直接裸写 `el-date-picker` daterange**,避免快捷项缺失与宽度不一(快捷项数据唯一真源在 `libs/timeOptions.js`,Element Plus 格式 `{text, value: () => [start, end]}`) |
|
||||
| `<date-range-picker>` | 日期范围搜索统一入口(v-model 值为 `[start, end]`):内置 `libs/timeOptions` 快捷项(今天/昨天/本月/上月/最近N天/本年/去年)、分隔符「至」、默认 220px(弹窗等非筛选场景兜底)并强制 `flex-grow: 0`(EP `.el-input__wrapper` 自带 `flex-grow: 1`,不压制会在 flex 搜索行内被拉伸占满);**筛选行一律显式传 `width` 对齐同行控件**(250px 基准,2026-09-29 起全站 47 处消费方均已显式传宽;SchemaPage 搜索栏随其 200px 体系传 200px、`plugin/virtual/product` 随其 220px 体系传 220px)。组件行内样式的优先级高于 class,给组件挂 `.form_content_width` 等宽度类是死代码,禁止。`format`/`value-format`/`clearable` 等经 attrs 透传可覆盖。时间范围筛选**不要直接裸写 `el-date-picker` daterange**,避免快捷项缺失与宽度不一(快捷项数据唯一真源在 `libs/timeOptions.js`,Element Plus 格式 `{text, value: () => [start, end]}`) |
|
||||
| `<icon>` | iView/EP 兼容图标组件(菜单/面包屑/DIY 历史模板用) |
|
||||
| EP 全部图标 | `@element-plus/icons-vue` 逐个全局注册 |
|
||||
|
||||
@ -503,7 +503,7 @@ function del(row, tit, num) {
|
||||
- [ ] 表格默认 el-table(树形才 vxe-table);状态列 `defineSwitch` + 失败回滚;图片列 `tabBox_img + v-viewer + v-lazy`;超长文本 `line1/line2`
|
||||
- [ ] 字体、颜色、间距和尺寸按规则核对 computed style,未误用参考项目变量或存量尺寸
|
||||
- [ ] 筛选条件按实际行数折叠,窄容器按钮不覆盖字段,重置/搜索分页状态正确
|
||||
- [ ] 时间范围筛选用全局 `<date-range-picker>`(自带快捷项与 320px 基准宽),未裸写 `el-date-picker` daterange
|
||||
- [ ] 时间范围筛选用全局 `<date-range-picker>`(自带快捷项)且显式传 `width` 对齐同行控件(筛选行 250px 基准;组件行内样式优先于 class,宽度类无效),未裸写 `el-date-picker` daterange
|
||||
- [ ] dialog/drawer 在短视口仍能操作,表单宽度适配容器,长内容/错误提示不被裁切
|
||||
- [ ] 新增/编辑优先 `modalForm(api(id)).then(getList)`;复杂表单拆 `edit.vue` 放抽屉(`wrapper-closable=false`,`v-if` 惰性渲染)
|
||||
- [ ] 删除走 `modalSure`(含明确确认文案);dialog 表单类带 `:close-on-click-modal="false"`
|
||||
@ -606,6 +606,15 @@ function del(row, tit, num) {
|
||||
|
||||
- 页签高度重新裁定:用户以「实物订单」页现效果为准,页面页签高度 44px → 54px(`--el-tabs-header-height`),字号 14px / 选中 500 / 激活条 3px 不变,[前端规范 4.4「Tabs」](../../rules/前端规范.md) 定稿值同步更新(此前 2026-09-22 的 44px 定稿记录见上,54px 为当前验收值)。同批清理订单中心(`order/orderCenter/index.vue`)、电子发票(`order/invoice/index.vue`)、实物订单列表(`plugin/normal/order/components/OrderTable.vue`)残留的 54px 局部高度覆盖,页签视觉规格继续只由 `theme/element.scss` 一处提供。
|
||||
|
||||
**已修复**(2026-09-28,筛选展开/收起按钮组位置与筛选行日期宽度):
|
||||
|
||||
- 用户列表(`user/list`)筛选展开后「查询/重置/收起」被 `justify-content: space-between` 钉在卡片右侧(展开态垂直悬在右中、收起态换行沉底),日期范围 320px 与 250px 筛选控件不等宽,页面观感错乱(用户验收否决)。
|
||||
- 结构统一为内联流式:常显筛选项直接平铺在 `el-form` 下,额外项包 `<template v-if="collapse">`,按钮组为末尾无 label `el-form-item`,随筛选内容自然换行;删除 `div.acea-row.search-form` / `div.search-form-box` 包装层、重复的双份表单与配套 scoped CSS。筛选行 `<date-range-picker>` 传 `width="250px"` 对齐 `.form_content_width`。
|
||||
- 同批改造同结构的 `plugin/coupon`、`plugin/cardkey`、`plugin/normal/product`(三页原按钮固定右上,date-range-picker 挂的 `form_range_content_width` 117px 类被组件行内 320px 覆盖、实为死代码,一并换成 `width="250px"`)。`plugin/virtual/product/List` 本就内联流式、控件用自有宽度体系(220/180/120px),未动;`center/product/list` 无展开收起、仍为右侧固定布局,未动。
|
||||
- 规则 [前端规范 4.3「筛选交互」](../../rules/前端规范.md) 同步改为「跟排在筛选项末尾、随换行自然流动」,废弃原「固定搜索卡右上」验收值;第四节要点与第十二节清单同步。
|
||||
- 补充(2026-09-29):实物订单列表(`plugin/normal/order/components/OrderSearch.vue`)漏在上述批次外,筛选控件为 180/120/220px 临时宽度 + 默认宽日期选择器,观感错乱;已统一为 `.form_content_width`(250px)+ `date-range-picker width="250px"`,容器换全局 `.padding-add`(该页无展开收起,仅宽度问题)。虚拟订单列表(`plugin/virtual/order/List.vue`)同批修复:搜索卡 body 自写 `16px 20px` 与表单项 18px margin-bottom 叠加致底部留白 34px(顶部仅 16px),换 `.padding-add` 结构后上下平衡,控件宽度与按钮 `v-db-click` 一并对齐标准。
|
||||
- 遗留清零(2026-09-29,用户裁定全站统一):上条遗留的 43 处默认宽消费方全部显式传宽——筛选行 38 处 + 订单中心死 class 换 `width="250px"`(合计 39 处,`finance/*`、`statistic/*`、营销/分销/秒杀/拼团/砍价/抽奖/积分/发票/售后/线下订单/系统日志/会员记录等);SchemaPage 搜索栏随其 200px 输入体系传 200px;`plugin/virtual/product` 随其 220px 体系传 220px(两处不强行 250px,保持行内一致)。组件默认宽度由用户调整为 220px(弹窗等非筛选场景),文档不再写 320px。校验:`grep` 全站 47 处 `<date-range-picker>` 开标签均含显式 `width="…px"`。
|
||||
|
||||
**待处理**:
|
||||
|
||||
- 成功色不统一:`.el-button--success` 被 `variables.scss` 改成 `#19be6b`,而 `--el-color-success`(标签/文字/进度)仍是 EP 默认 `#67c23a`。需二选一统一。
|
||||
|
||||
@ -17,7 +17,6 @@ use app\services\order\OrderCenterServices;
|
||||
use app\services\product\CenterProductServices;
|
||||
use app\services\product\product\StoreProductReplyServices;
|
||||
use app\services\system\UpgradeServices;
|
||||
use app\services\user\UserExtractServices;
|
||||
use app\services\system\SystemMenusServices;
|
||||
use app\services\user\UserServices;
|
||||
use app\services\system\attachment\SystemAttachmentServices;
|
||||
@ -216,10 +215,7 @@ class CommonController extends AuthController
|
||||
/** @var StoreProductReplyServices $replyServices */
|
||||
$replyServices = app()->make(StoreProductReplyServices::class);
|
||||
$data['commentnum'] = $replyServices->replyCount();
|
||||
/** @var UserExtractServices $extractServices */
|
||||
$extractServices = app()->make(UserExtractServices::class);
|
||||
$data['reflectnum'] = $extractServices->userExtractCount();//提现
|
||||
$data['msgcount'] = intval($data['ordernum']) + intval($data['inventory']) + intval($data['commentnum']) + intval($data['reflectnum']);
|
||||
$data['msgcount'] = intval($data['ordernum']) + intval($data['inventory']) + intval($data['commentnum']);
|
||||
$value = [];
|
||||
if ($data['ordernum'] != 0) {
|
||||
$value[] = [
|
||||
@ -242,13 +238,6 @@ class CommonController extends AuthController
|
||||
'url' => '/' . Config::get('app.admin_prefix', 'admin') . '/product/product_reply?is_reply=0'
|
||||
];
|
||||
}
|
||||
if ($data['reflectnum'] != 0) {
|
||||
$value[] = [
|
||||
'title' => "您有$data[reflectnum]个提现申请待审核",
|
||||
'type' => 4,
|
||||
'url' => '/' . Config::get('app.admin_prefix', 'admin') . '/finance/user_extract/index?status=0',
|
||||
];
|
||||
}
|
||||
return app('json')->success($this->noticeData($value));
|
||||
}
|
||||
|
||||
|
||||
@ -177,7 +177,6 @@ class DiyProController extends AuthController
|
||||
['label' => '订单总数', 'value' => 'order_num'],
|
||||
['label' => '我的积分', 'value' => 'integral'],
|
||||
['label' => '我的余额', 'value' => 'now_money'],
|
||||
['label' => '我的佣金', 'value' => 'brokerage_price'],
|
||||
['label' => '未读消息', 'value' => 'unread_msg_num'],
|
||||
];
|
||||
|
||||
|
||||
@ -76,7 +76,6 @@ class ExportExcelController extends AuthController
|
||||
['nickname', ''],
|
||||
['status', ''],
|
||||
['pay_count', ''],
|
||||
['is_promoter', ''],
|
||||
['order', ''],
|
||||
['data', ''],
|
||||
['user_type', ''],
|
||||
@ -228,26 +227,6 @@ class ExportExcelController extends AuthController
|
||||
return app('json')->success($this->service->userFinance($data['data'] ?? []));
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户佣金
|
||||
* @param UserBillServices $services
|
||||
* @return mixed
|
||||
*/
|
||||
public function userCommission(UserBillServices $services)
|
||||
{
|
||||
$where = $this->request->getMore([
|
||||
['page', 1],
|
||||
['limit', 20],
|
||||
['nickname', ''],
|
||||
['price_max', ''],
|
||||
['price_min', ''],
|
||||
['excel', '1'],
|
||||
['time', ''],
|
||||
]);
|
||||
$data = $services->getCommissionList($where, false);
|
||||
return app('json')->success($this->service->userCommission($data['list'] ?? []));
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户积分
|
||||
* @param UserBillServices $services
|
||||
|
||||
@ -61,47 +61,4 @@ class FinanceController extends AuthController
|
||||
return app('json')->success($this->services->getBillList($where));
|
||||
}
|
||||
|
||||
/**
|
||||
* 佣金记录
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCommissionList()
|
||||
{
|
||||
$where = $this->request->getMore([
|
||||
['nickname', ''],
|
||||
['price_max', ''],
|
||||
['price_min', ''],
|
||||
['sum_number', 'normal'],
|
||||
['brokerage_price', 'normal'],
|
||||
['time', '']
|
||||
]);
|
||||
return app('json')->success($this->services->getCommissionList($where));
|
||||
}
|
||||
|
||||
/**
|
||||
* 佣金详情用户信息
|
||||
* @param $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function userInfo($id)
|
||||
{
|
||||
return app('json')->success($this->services->user_info((int)$id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 佣金提现记录个人列表
|
||||
*/
|
||||
public function getExtractList($id = '')
|
||||
{
|
||||
if (empty($id)) return app('json')->fail('参数错误');
|
||||
$where = $this->request->getMore([
|
||||
['start_time', ''],
|
||||
['end_time', ''],
|
||||
['nickname', '']
|
||||
]);
|
||||
$where['category'] = 'now_money';
|
||||
$where['type'] = ['brokerage', 'brokerage_user'];
|
||||
return app('json')->success($this->services->getBillOneList((int)$id, $where));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,155 +0,0 @@
|
||||
<?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\adminapi\controller\finance;
|
||||
|
||||
use app\adminapi\controller\AuthController;
|
||||
use app\services\user\UserExtractServices;
|
||||
use think\facade\App;
|
||||
use think\Request;
|
||||
|
||||
/**
|
||||
* Class UserExtract
|
||||
* @package app\adminapi\controller\finance
|
||||
*/
|
||||
class UserExtractController extends AuthController
|
||||
{
|
||||
/**
|
||||
* @var UserExtractServices
|
||||
*/
|
||||
protected $services;
|
||||
|
||||
|
||||
/**
|
||||
* UserExtract constructor.
|
||||
* @param App $app
|
||||
* @param UserExtractServices $services
|
||||
*/
|
||||
public function __construct(App $app, UserExtractServices $services)
|
||||
{
|
||||
parent::__construct($app);
|
||||
$this->services = $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示资源列表
|
||||
* @return mixed
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$where = $this->request->getMore([
|
||||
['status', ''],
|
||||
['extract_type', ''],
|
||||
['nireid', '', '', 'like'],
|
||||
['data', '', '', 'time'],
|
||||
]);
|
||||
if (isset($where['extract_type']) && $where['extract_type'] == 'wx') {
|
||||
$where['extract_type'] = 'weixin';
|
||||
}
|
||||
return app('json')->success($this->services->index($where));
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示编辑资源表单页
|
||||
* @param $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
if (!$id) return app('json')->fail('数据不存在');
|
||||
return app('json')->success($this->services->edit((int)$id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存更新的资源
|
||||
* @param Request $request
|
||||
* @param $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
if (!$id) return app('json')->fail('参数错误');
|
||||
$id = (int)$id;
|
||||
$UserExtract = $this->services->getExtract($id);
|
||||
if (!$UserExtract) return app('json')->fail('数据不存在');
|
||||
if ($UserExtract['extract_type'] == 'alipay') {
|
||||
$data = $this->request->postMore([
|
||||
'real_name',
|
||||
'mark',
|
||||
'extract_price',
|
||||
'alipay_code',
|
||||
]);
|
||||
if (!$data['real_name']) return app('json')->fail('请输入姓名');
|
||||
if ($data['extract_price'] <= -1) return app('json')->fail('请输入提现金额');
|
||||
if (!$data['alipay_code']) return app('json')->fail('请输入支付宝账号');
|
||||
} else if ($UserExtract['extract_type'] == 'weixin') {
|
||||
$data = $this->request->postMore([
|
||||
'real_name',
|
||||
'mark',
|
||||
'extract_price',
|
||||
'wechat',
|
||||
]);
|
||||
if ($data['extract_price'] <= -1) return app('json')->fail('请输入提现金额');
|
||||
if (!$data['wechat']) return app('json')->fail('请输入微信账号');
|
||||
} else {
|
||||
$data = $this->request->postMore([
|
||||
'real_name',
|
||||
'extract_price',
|
||||
'mark',
|
||||
'bank_code',
|
||||
'bank_address',
|
||||
]);
|
||||
if (!$data['real_name']) return app('json')->fail('请输入姓名');
|
||||
if ($data['extract_price'] <= -1) return app('json')->fail('请输入提现金额');
|
||||
if (!$data['bank_code']) return app('json')->fail('请输入银行卡号');
|
||||
if (!$data['bank_address']) return app('json')->fail('请输入开户行');
|
||||
}
|
||||
return app('json')->success($this->services->update($id, $data) ? '修改成功' : '修改失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝
|
||||
* @param $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function refuse($id)
|
||||
{
|
||||
if (!$id) app('json')->fail('参数错误');
|
||||
$data = $this->request->postMore([
|
||||
['message', '']
|
||||
]);
|
||||
if ($data['message'] === '') return app('json')->fail('拒绝理由不能为空');
|
||||
return app('json')->success($this->services->refuse((int)$id, $data['message']) ? '设置成功' : '设置失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过
|
||||
* @param $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function adopt($id)
|
||||
{
|
||||
if (!$id) app('json')->fail('参数错误');
|
||||
$res = $this->services->adopt((int)$id);
|
||||
if ($res) {
|
||||
if ($res === 'v3_extract') {
|
||||
return app('json')->success('提现已受理,请以微信最终到账状态为准');
|
||||
} else {
|
||||
return app('json')->success('提现成功');
|
||||
}
|
||||
} else {
|
||||
return app('json')->success('操作失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -47,7 +47,6 @@ class SystemVerifyOrderController extends AuthController
|
||||
$spread = $userServices->getUserInfo((int)$uid);
|
||||
if ($spread) {
|
||||
$spread = $spread->toArray();
|
||||
$spread['brokerage_pric'] = $spread['brokerage_price'];
|
||||
$spread['birthday'] = $spread['birthday'] ? date('Y-m-d', $spread['birthday']) : '';
|
||||
$spread['last_time'] = $spread['last_time'] ? date('Y-m-d H:i:s', $spread['last_time']) : '';
|
||||
}
|
||||
|
||||
@ -152,11 +152,9 @@ class SystemClearDataController extends AuthController
|
||||
'user',
|
||||
'user_address',
|
||||
'user_bill',
|
||||
'user_brokerage',
|
||||
'user_brokerage_frozen',
|
||||
'user_cancel',
|
||||
'user_enter',
|
||||
'user_extract',
|
||||
'user_friends',
|
||||
'user_group',
|
||||
'user_invoice',
|
||||
@ -247,11 +245,9 @@ class SystemClearDataController extends AuthController
|
||||
'user',
|
||||
'user_address',
|
||||
'user_bill',
|
||||
'user_brokerage',
|
||||
'user_brokerage_frozen',
|
||||
'user_cancel',
|
||||
'user_enter',
|
||||
'user_extract',
|
||||
'user_friends',
|
||||
'user_group',
|
||||
'user_invoice',
|
||||
|
||||
@ -45,7 +45,6 @@ class UserController extends AuthController
|
||||
['nickname', ''],
|
||||
['status', ''],
|
||||
['pay_count', ''],
|
||||
['is_promoter', ''],
|
||||
['order', ''],
|
||||
['data', ''],
|
||||
['user_type', ''],
|
||||
@ -66,7 +65,6 @@ class UserController extends AuthController
|
||||
['before_pay_time', ''],
|
||||
['pay_count_num', []],
|
||||
['pay_count_money', []],
|
||||
['agent_level', 0],
|
||||
]);
|
||||
$where['label_id'] = toIntArray($where['label_id']);
|
||||
return app('json')->success($this->services->index($where));
|
||||
|
||||
@ -34,8 +34,6 @@ Route::group('export', function () {
|
||||
Route::get('userAgent', [\app\adminapi\controller\export\ExportExcelController::class, 'userAgent'])->option(['real_name' => '分销员推广列表导出']);
|
||||
//用户资金监控
|
||||
Route::get('userFinance', [\app\adminapi\controller\export\ExportExcelController::class, 'userFinance'])->option(['real_name' => '用户资金导出']);
|
||||
//用户佣金
|
||||
Route::get('userCommission', [\app\adminapi\controller\export\ExportExcelController::class, 'userCommission'])->option(['real_name' => '用户佣金导出']);
|
||||
//用户积分
|
||||
Route::get('userPoint', [\app\adminapi\controller\export\ExportExcelController::class, 'userPoint'])->option(['real_name' => '用户积分导出']);
|
||||
//用户充值
|
||||
|
||||
@ -15,19 +15,6 @@ use think\facade\Route;
|
||||
*/
|
||||
Route::group('finance', function () {
|
||||
|
||||
/** 提现 */
|
||||
Route::group(function () {
|
||||
//申请列表
|
||||
Route::get('extract', [\app\adminapi\controller\finance\UserExtractController::class, 'index'])->option(['real_name' => '提现申请列表']);
|
||||
//编辑表单
|
||||
Route::get('extract/:id/edit', [\app\adminapi\controller\finance\UserExtractController::class, 'edit'])->option(['real_name' => '提现记录修改表单']);
|
||||
//保存修改
|
||||
Route::put('extract/:id', [\app\adminapi\controller\finance\UserExtractController::class, 'update'])->option(['real_name' => '提现记录修改']);
|
||||
//拒绝申请
|
||||
Route::put('extract/refuse/:id', [\app\adminapi\controller\finance\UserExtractController::class, 'refuse'])->option(['real_name' => '拒绝提现申请']);
|
||||
//通过申请
|
||||
Route::put('extract/adopt/:id', [\app\adminapi\controller\finance\UserExtractController::class, 'adopt'])->option(['real_name' => '通过提现申请']);
|
||||
})->option(['parent' => 'finance', 'cate_name' => '提现']);
|
||||
|
||||
/** 资金记录 */
|
||||
Route::group(function () {
|
||||
@ -35,12 +22,6 @@ Route::group('finance', function () {
|
||||
Route::get('finance/bill_type', [\app\adminapi\controller\finance\FinanceController::class, 'billType'])->option(['real_name' => '资金记录类型']);
|
||||
//资金记录
|
||||
Route::get('finance/list', [\app\adminapi\controller\finance\FinanceController::class, 'list'])->option(['real_name' => '资金记录列表']);
|
||||
//佣金记录
|
||||
Route::get('finance/commission_list', [\app\adminapi\controller\finance\FinanceController::class, 'getCommissionList'])->option(['real_name' => '佣金记录列表']);
|
||||
//佣金详情用户信息
|
||||
Route::get('finance/user_info/:id', [\app\adminapi\controller\finance\FinanceController::class, 'userInfo'])->option(['real_name' => '佣金详情用户信息']);
|
||||
//佣金提现记录个人列表
|
||||
Route::get('finance/extract_list/:id', [\app\adminapi\controller\finance\FinanceController::class, 'getExtractList'])->option(['real_name' => '佣金提现记录个人列表']);
|
||||
/** 余额记录 */
|
||||
Route::get('balance/list', [\app\adminapi\controller\finance\UserBalanceController::class, 'balanceList'])->option(['real_name' => '余额记录列表']);
|
||||
Route::post('balance/set_mark/:id', [\app\adminapi\controller\finance\UserBalanceController::class, 'balanceRecordRemark'])->option(['real_name' => '余额记录备注']);
|
||||
|
||||
@ -70,14 +70,6 @@ class CrontabController
|
||||
event('ActivityDataProxy', ['mode' => 'combination', 'sub_service' => 'pink', 'action' => 'statusPink', 'args' => []]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动解绑上级绑定
|
||||
*/
|
||||
public function distributionUnbind()
|
||||
{
|
||||
event('DistributionUnbindCrontab');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新直播商品状态
|
||||
*/
|
||||
|
||||
@ -39,7 +39,6 @@ use app\services\system\lang\LangTypeServices;
|
||||
use app\services\system\store\SystemStoreServices;
|
||||
use app\services\system\store\SystemStoreStaffServices;
|
||||
use app\services\user\UserBillServices;
|
||||
use app\services\user\UserExtractServices;
|
||||
use app\services\user\UserInvoiceServices;
|
||||
use app\services\user\UserServices;
|
||||
use app\Request;
|
||||
@ -144,13 +143,11 @@ class PublicController
|
||||
|
||||
//用户等级开关
|
||||
$vipOpen = sys_config('member_func_status');
|
||||
//分销功能开关
|
||||
$brokerageFuncStatus = sys_config('brokerage_func_status');
|
||||
//余额功能开关
|
||||
$balanceFuncStatus = sys_config('balance_func_status');
|
||||
//付费会员开关
|
||||
$svipOpen = sys_config('member_card_status');
|
||||
$userService = $userOrder = $userVerifyStatus = $deliveryUser = $invoiceStatus = $isUserPromoter = false;
|
||||
$userService = $userOrder = $userVerifyStatus = $deliveryUser = $invoiceStatus = false;
|
||||
if ($uid && $userInfo) {
|
||||
//是否客服
|
||||
$userService = (bool)(event('KefuConfigQuery', ['uid' => $uid, 'customer' => 0])[0] ?? false);
|
||||
@ -160,16 +157,15 @@ class PublicController
|
||||
$deliveryUser = app()->make(ServicePersonnelServices::class)->checkoutIsService($uid);
|
||||
//发票功能开关
|
||||
$invoiceStatus = app()->make(UserInvoiceServices::class)->invoiceFuncStatus(false);
|
||||
//是否分销员
|
||||
$isUserPromoter = app()->make(UserServices::class)->checkUserPromoter($uid, $userInfo);
|
||||
}
|
||||
$auth = [];
|
||||
$auth['/pages/users/user_vip/index'] = $vipOpen;
|
||||
$auth['/pages/users/user_spread_user/index'] = $brokerageFuncStatus && $isUserPromoter;
|
||||
$auth['/pages/annex/settled/index'] = $brokerageFuncStatus && sys_config('store_brokerage_statu') == 1 && !$isUserPromoter;
|
||||
$auth['/pages/users/user_money/index'] = $balanceFuncStatus;
|
||||
$auth['/pages/users/user_invoice_list/index'] = $invoiceStatus;
|
||||
$auth['/pages/annex/vip_paid/index'] = $svipOpen;
|
||||
// 分销中心/代理商入驻已下线,强制隐藏
|
||||
$auth['/pages/users/user_spread_user/index'] = false;
|
||||
$auth['/pages/annex/settled/index'] = false;
|
||||
$auth['/kefu/mobile_list'] = $userService;
|
||||
// 移动端核销页仅对核销员/配送员开放;已删除的商家管理页强制隐藏
|
||||
$auth['/pages/admin/order_cancellation/index'] = $userVerifyStatus || $deliveryUser;
|
||||
@ -180,10 +176,6 @@ class PublicController
|
||||
unset($menusInfo[$key]);
|
||||
continue;
|
||||
}
|
||||
if ($value['url'] == '/pages/users/user_spread_user/index' && $auth['/pages/annex/settled/index']) {
|
||||
$value['name'] = '分销申请';
|
||||
$value['url'] = '/pages/annex/settled/index';
|
||||
}
|
||||
if (isset($auth[$value['url']]) && !$auth[$value['url']]) {
|
||||
unset($menusInfo[$key]);
|
||||
continue;
|
||||
@ -831,16 +823,15 @@ class PublicController
|
||||
|
||||
public function getTransferInfo(Request $request, $order_id, $type)
|
||||
{
|
||||
$extractServices = app()->make(UserExtractServices::class);
|
||||
$uid = (int)$request->uid();
|
||||
if ($type == 1) {
|
||||
return app('json')->success($extractServices->transferInfo($uid, (string)$order_id));
|
||||
} else {
|
||||
//通过事件代理查询抽奖记录,避免 app 层直接引用 modules
|
||||
$results = event('MarketingServiceProxy', ['module' => 'lottery', 'service' => 'LuckLotteryRecordServices', 'action' => 'getRecordByOrderId', 'args' => [$uid, $order_id]]);
|
||||
$info = proxy_event_result($results, []);
|
||||
$info['true_extract_price'] = $info['num'];
|
||||
// 提现已由分销插件接管,主程序不再提供提现转账详情
|
||||
return app('json')->fail('提现功能已下线');
|
||||
}
|
||||
//通过事件代理查询抽奖记录,避免 app 层直接引用 modules
|
||||
$results = event('MarketingServiceProxy', ['module' => 'lottery', 'service' => 'LuckLotteryRecordServices', 'action' => 'getRecordByOrderId', 'args' => [$uid, $order_id]]);
|
||||
$info = proxy_event_result($results, []);
|
||||
$info['true_extract_price'] = $info['num'];
|
||||
if ($info['state'] == 'WAIT_USER_CONFIRM') {
|
||||
$pay = new Pay('v3_wechat_pay');
|
||||
$res = $pay->queryTransferBills($order_id);
|
||||
@ -912,7 +903,6 @@ class PublicController
|
||||
//用户等级开关
|
||||
$levelOpen = (bool)sys_config('member_func_status');
|
||||
//分销功能开关
|
||||
$brokerageOpen = (bool)sys_config('brokerage_func_status');
|
||||
//余额功能开关
|
||||
$balanceOpen = (bool)sys_config('balance_func_status');
|
||||
//付费会员开关
|
||||
@ -921,7 +911,7 @@ class PublicController
|
||||
$invoiceOpen = (bool)sys_config('invoice_func_status');
|
||||
|
||||
// 初始化用户角色标识
|
||||
$userIsService = $userIsOrder = $userIsVerify = $userIsDelivery = $userIsPromoter = false;
|
||||
$userIsService = $userIsOrder = $userIsVerify = $userIsDelivery = false;
|
||||
|
||||
if ($uid && $userInfo) {
|
||||
/** @var OrderCenterDao $orderCenterDao */
|
||||
@ -936,8 +926,6 @@ class PublicController
|
||||
$userIsVerify = (bool)app()->make(SystemStoreStaffServices::class)->verifyStatus($uid);
|
||||
//是否配送员
|
||||
$userIsDelivery = (bool)app()->make(ServicePersonnelServices::class)->checkoutIsService($uid);
|
||||
//是否分销员
|
||||
$userIsPromoter = (bool)app()->make(UserServices::class)->checkUserPromoter($uid, $userInfo);
|
||||
|
||||
// 统计各状态订单数量,用于菜单角标显示(基于 eb_order_center 表,口径与订单列表 tab 一致)
|
||||
$orderAuth = [];
|
||||
@ -969,8 +957,9 @@ class PublicController
|
||||
// 配置各页面的访问权限
|
||||
$auth = [];
|
||||
$auth['/pages/users/user_vip/index'] = $levelOpen;
|
||||
$auth['/pages/users/user_spread_user/index'] = $brokerageOpen && $userIsPromoter;
|
||||
$auth['/pages/annex/settled/index'] = $brokerageOpen && sys_config('store_brokerage_statu') == 1 && !$userIsPromoter;
|
||||
// 分销中心/代理商入驻已下线,强制隐藏
|
||||
$auth['/pages/users/user_spread_user/index'] = false;
|
||||
$auth['/pages/annex/settled/index'] = false;
|
||||
$auth['/pages/users/user_money/index'] = $balanceOpen;
|
||||
$auth['/pages/users/user_invoice_list/index'] = $invoiceOpen;
|
||||
$auth['/pages/annex/vip_paid/index'] = $sVipOpen;
|
||||
@ -1005,11 +994,6 @@ class PublicController
|
||||
}
|
||||
}
|
||||
|
||||
if ($menuDataItem['info'][1]['value'] == '/pages/users/user_spread_user/index' && $brokerageOpen && sys_config('store_brokerage_statu') == 1 && !$userIsPromoter) {
|
||||
$menuDataItem['info'][0]['value'] = '分销申请';
|
||||
$menuDataItem['info'][1]['value'] = '/pages/annex/settled/index';
|
||||
$menuDataItem['show'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1056,7 +1040,6 @@ class PublicController
|
||||
'order_num' => $userInfo['pay_count'],
|
||||
'integral' => $userInfo['integral'],
|
||||
'now_money' => $userInfo['now_money'],
|
||||
'brokerage_price' => $userInfo['brokerage_price'],
|
||||
'unread_msg_num' => app()->make(\app\services\message\NoticeServices::class)->getUnreadCount($userInfo['uid']),
|
||||
];
|
||||
return app('json')->success($user);
|
||||
|
||||
@ -119,7 +119,7 @@ class StoreProductController
|
||||
if ($request->uid()) {
|
||||
$user = $request->user();
|
||||
} else {
|
||||
$user = ['uid' => 0, 'is_promoter' => 0];
|
||||
$user = ['uid' => 0];
|
||||
}
|
||||
$code = $this->services->getCode((int)$id, $request->get('user_type', 'wechat'), $user);
|
||||
return app('json')->success(['code' => $code]);
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
<?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\api\controller\v1\user;
|
||||
|
||||
use app\Request;
|
||||
use app\services\user\UserBrokerageServices;
|
||||
|
||||
class UserBrokerageController
|
||||
{
|
||||
/**
|
||||
* @var UserBrokerageServices
|
||||
*/
|
||||
protected $services;
|
||||
|
||||
/**
|
||||
* UserBrokerageController constructor.
|
||||
* @param UserBrokerageServices $services
|
||||
*/
|
||||
public function __construct(UserBrokerageServices $services)
|
||||
{
|
||||
$this->services = $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* 推广数据 昨天的佣金 累计提现金额 当前佣金
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function commission(Request $request)
|
||||
{
|
||||
$uid = (int)$request->uid();
|
||||
return app('json')->success($this->services->commission($uid));
|
||||
}
|
||||
|
||||
/**
|
||||
* 佣金排行
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function brokerageRank(Request $request)
|
||||
{
|
||||
$data = $request->getMore([
|
||||
['page', ''],
|
||||
['limit'],
|
||||
['type']
|
||||
]);
|
||||
$uid = (int)$request->uid();
|
||||
return app('json')->success($this->services->brokerageRank($uid, $data['type']));
|
||||
}
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
<?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\api\controller\v1\user;
|
||||
|
||||
use app\Request;
|
||||
use app\services\user\UserExtractServices;
|
||||
use crmeb\services\CacheService;
|
||||
use think\facade\Config;
|
||||
use think\exception\ValidateException;
|
||||
|
||||
/**
|
||||
* 提现类
|
||||
* Class UserExtractController
|
||||
* @package app\api\controller\user
|
||||
*/
|
||||
class UserExtractController
|
||||
{
|
||||
/**
|
||||
* @var UserExtractServices
|
||||
*/
|
||||
protected $services;
|
||||
|
||||
/**
|
||||
* UserExtractController constructor.
|
||||
* @param UserExtractServices $services
|
||||
*/
|
||||
public function __construct(UserExtractServices $services)
|
||||
{
|
||||
$this->services = $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提现银行
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function bank(Request $request)
|
||||
{
|
||||
$uid = (int)$request->uid();
|
||||
return app('json')->success($this->services->bank($uid));
|
||||
}
|
||||
|
||||
/**
|
||||
* 收款前核验微信转账状态,过期失败的转账自动返还佣金。
|
||||
* @param Request $request 当前用户请求
|
||||
* @return mixed
|
||||
*/
|
||||
public function checkTransfer(Request $request)
|
||||
{
|
||||
$data = $request->postMore([['order_id', '']]);
|
||||
if (isset($data['order_id']) && !is_string($data['order_id'])) throw new ValidateException('转账单号输入有误');
|
||||
validate(['order_id' => 'require|alphaNum|max:64'])->check($data);
|
||||
return app('json')->success($this->services->transferInfo((int)$request->uid(), $data['order_id'], true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 提现申请
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function cash(Request $request)
|
||||
{
|
||||
$extractInfo = $request->postMore([
|
||||
['alipay_code', ''],
|
||||
['extract_type', ''],
|
||||
['money', 0],
|
||||
['user_name', ''],
|
||||
['name', ''],
|
||||
['bankname', ''],
|
||||
['cardnum', ''],
|
||||
['weixin', ''],
|
||||
['qrcode_url', ''],
|
||||
]);
|
||||
// 每10秒只能提现一次
|
||||
$uid = (int)$request->uid();
|
||||
$cacheKey = 'extract_limit_' . $uid;
|
||||
if (CacheService::has($cacheKey)) {
|
||||
return app('json')->fail('操作过于频繁,请10秒后再试');
|
||||
}
|
||||
CacheService::set($cacheKey, 1, 10);
|
||||
$extractInfo['channel_type'] = $request->getFromType();
|
||||
$extractType = Config::get('pay.extractType', []);
|
||||
if (!in_array($extractInfo['extract_type'], $extractType))
|
||||
return app('json')->fail('提现方式不存在');
|
||||
if (!preg_match('/^[0-9]+(.[0-9]{1,2})?$/', (float)$extractInfo['money'])) return app('json')->fail('提现金额输入有误');
|
||||
if (trim((string)$extractInfo['cardnum']) !== '')
|
||||
if (!preg_match('/^([1-9]{1})(\d{15}|\d{16}|\d{18})$/', $extractInfo['cardnum']))
|
||||
return app('json')->fail('银行卡号输入有误');
|
||||
if ($extractInfo['extract_type'] == 'weixin') {
|
||||
if (trim($extractInfo['user_name']) === '') return app('json')->fail('请填写真实姓名');
|
||||
} elseif ($extractInfo['extract_type'] == 'alipay') {
|
||||
if (trim($extractInfo['alipay_code']) === '') return app('json')->fail('请输入支付宝账号');
|
||||
if (trim($extractInfo['user_name']) === '') return app('json')->fail('请填写真实姓名');
|
||||
} elseif ($extractInfo['extract_type'] == 'bank') {
|
||||
if (!$extractInfo['cardnum']) return app('json')->fail('请输入银行卡账号');
|
||||
if (!$extractInfo['bankname']) return app('json')->fail('请输入开户行信息');
|
||||
}
|
||||
if ($this->services->cash($uid, $extractInfo))
|
||||
return app('json')->success('申请提现成功');
|
||||
else
|
||||
return app('json')->fail('申请提现失败');
|
||||
}
|
||||
}
|
||||
@ -83,9 +83,8 @@ Route::group(function () {
|
||||
Route::post('switch_h5', [\app\api\controller\v1\LoginController::class, 'switchH5'])->name('switch_h5')->option(['real_name' => '切换账号']);// 切换账号
|
||||
//公共类
|
||||
Route::post('upload/image', [\app\api\controller\v1\PublicController::class, 'uploadImage'])->name('uploadImage')->option(['real_name' => '图片上传']);//图片上传
|
||||
// 用户微信转账详情接口
|
||||
// 用户微信转账详情接口(提现已由分销插件接管,type=1 仅返回下线提示)
|
||||
Route::get('transfer/info', [\app\api\controller\v1\PublicController::class, 'getTransferInfo'])->name('getTransferInfo')->option(['real_name' => '用户微信转账详情接口']);// 用户微信转账详情接口
|
||||
Route::post('transfer/info', [\app\api\controller\v1\user\UserExtractController::class, 'checkTransfer'])->name('checkTransfer')->option(['real_name' => '用户微信转账收款核验']);// 核验并同步当前用户的转账终态
|
||||
|
||||
})->option(['mark' => 'common', 'mark_name' => '公共接口']);
|
||||
|
||||
@ -177,21 +176,13 @@ Route::group(function () {
|
||||
//账单类
|
||||
Route::post('spread/people', [\app\api\controller\v1\user\UserController::class, 'spreadPeople'])->name('spreadPeople')->option(['real_name' => '推荐用户']);//推荐用户
|
||||
Route::post('spread/order', [\app\api\controller\v1\user\UserBillController::class, 'spreadOrder'])->name('spreadOrder')->option(['real_name' => '推广订单']);//推广订单
|
||||
Route::get('spread/commission/:type', [\app\api\controller\v1\user\UserBillController::class, 'spreadCommission'])->name('spreadCommission')->option(['real_name' => '推广佣金明细']);//推广佣金明细
|
||||
Route::get('spread/count/:type', [\app\api\controller\v1\user\UserBillController::class, 'spreadCount'])->name('spreadCount')->option(['real_name' => '推广佣金']);//推广 佣金 3/提现 4 总和
|
||||
Route::get('spread/banner', [\app\api\controller\v1\user\UserBillController::class, 'spreadBanner'])->name('spreadBanner')->option(['real_name' => '推广分销二维码海报生成']);//推广分销二维码海报生成
|
||||
Route::get('user/spread_info', [\app\api\controller\v1\user\UserBillController::class, 'getSpreadInfo'])->name('getSpreadInfo')->option(['real_name' => '分享邀新海报素材']);//分享邀新海报素材
|
||||
Route::get('integral/list', [\app\api\controller\v1\user\UserBillController::class, 'integralList'])->name('integralList')->option(['real_name' => '积分记录']);//积分记录
|
||||
Route::get('user/money/list/:type', [\app\api\controller\v1\user\UserBillController::class, 'moneyList'])->name('userMoneyList')->option(['real_name' => '资金明细']);//资金明细
|
||||
Route::get('user/routine_code', [\app\api\controller\v1\user\UserBillController::class, 'getRoutineCode'])->name('getRoutineCode')->option(['real_name' => '小程序二维码']);//小程序二维码
|
||||
Route::get('user/spread_info', [\app\api\controller\v1\user\UserBillController::class, 'getSpreadInfo'])->name('getSpreadInfo')->option(['real_name' => '获取分销背景等信息']);//获取分销背景等信息
|
||||
Route::post('division/order', [\app\api\controller\v1\user\UserBillController::class, 'divisionOrder'])->name('divisionOrder')->option(['real_name' => '事业部推广订单']);//事业部推广订单
|
||||
})->option(['mark' => 'division', 'mark_name' => '账单']);
|
||||
|
||||
Route::group(function () {
|
||||
//提现类
|
||||
Route::get('extract/bank', [\app\api\controller\v1\user\UserExtractController::class, 'bank'])->name('extractBank')->option(['real_name' => '提现银行']);//提现银行/提现最低金额
|
||||
Route::post('extract/cash', [\app\api\controller\v1\user\UserExtractController::class, 'cash'])->name('extractCash')->option(['real_name' => '提现申请']);//提现申请
|
||||
})->option(['mark' => 'extract', 'mark_name' => '提现']);
|
||||
|
||||
Route::group(function () {
|
||||
//会员等级类
|
||||
@ -272,8 +263,6 @@ Route::group(function () {
|
||||
|
||||
Route::group(function () {
|
||||
/** 佣金相关 */
|
||||
Route::get('commission', [\app\api\controller\v1\user\UserBrokerageController::class, 'commission'])->name('commission')->option(['real_name' => '推广数据']);//推广数据 昨天的佣金 累计提现金额 当前佣金
|
||||
Route::get('brokerage_rank', [\app\api\controller\v1\user\UserBrokerageController::class, 'brokerageRank'])->name('brokerageRank')->option(['real_name' => '佣金排行']);//佣金排行
|
||||
/** 用户注销 */
|
||||
Route::get('user_cancel', [\app\api\controller\v1\user\UserController::class, 'SetUserCancel'])->name('SetUserCancel')->option(['real_name' => '用户注销']);
|
||||
/** 用户浏览记录 */
|
||||
@ -421,8 +410,6 @@ Route::group(function () {
|
||||
Route::get('crontab/order_cancel', [\app\api\controller\v1\CrontabController::class, 'orderUnpaidCancel'])->name('orderUnpaidCancel')->option(['real_name' => '未支付自动取消订单']);
|
||||
//拼团到期订单处理
|
||||
Route::get('crontab/pink_expiration', [\app\api\controller\v1\CrontabController::class, 'pinkExpiration'])->name('pinkExpiration')->option(['real_name' => '拼团到期订单处理']);
|
||||
//自动解绑上级绑定
|
||||
Route::get('crontab/distribution_unbind', [\app\api\controller\v1\CrontabController::class, 'distributionUnbind'])->name('distributionUnbind')->option(['real_name' => '自动解绑上级绑定']);
|
||||
//更新直播商品状态
|
||||
Route::get('crontab/live_product_status', [\app\api\controller\v1\CrontabController::class, 'syncGoodStatus'])->name('syncGoodStatus')->option(['real_name' => '更新直播商品状态']);
|
||||
//更新直播间状态
|
||||
|
||||
@ -130,7 +130,6 @@ class Util extends Command
|
||||
"UPDATE `{$prefix}system_store_staff` SET `avatar` = replace(avatar ,'{$siteUrl}','{$url}')",
|
||||
"UPDATE `{$prefix}system_user_level` SET `image` = replace(image ,'{$siteUrl}','{$url}'),`icon` = replace(icon ,'{$siteUrl}','{$url}')",
|
||||
"UPDATE `{$prefix}user` SET `avatar` = replace(avatar ,'{$siteUrl}','{$url}')",
|
||||
"UPDATE `{$prefix}user_extract` SET `qrcode_url` = replace(qrcode_url ,'{$siteUrl}','{$url}')",
|
||||
"UPDATE `{$prefix}wechat_qrcode` SET `image` = replace(image ,'{$siteUrl}','{$url}')",
|
||||
"UPDATE `{$prefix}wechat_user` SET `headimgurl` = replace(headimgurl ,'{$siteUrl}','{$url}')",
|
||||
"UPDATE `{$prefix}theme` SET `home_data` = replace(home_data ,'{$siteUrlJson}','{$urlJson}'),`detail_data` = replace(detail_data ,'{$siteUrlJson}','{$urlJson}'),`user_data` = replace(user_data ,'{$siteUrlJson}','{$urlJson}')",
|
||||
|
||||
@ -760,30 +760,6 @@ if (!function_exists('filter_str')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('is_brokerage_statu')) {
|
||||
|
||||
/**
|
||||
* 是否能成为推广人
|
||||
* @param float $price
|
||||
* @return bool
|
||||
*/
|
||||
function is_brokerage_statu(float $price)
|
||||
{
|
||||
if (!sys_config('brokerage_func_status')) {
|
||||
return false;
|
||||
}
|
||||
$storeBrokerageStatus = sys_config('store_brokerage_statu', 1);
|
||||
if ($storeBrokerageStatus == 1) {
|
||||
return false;
|
||||
} else if ($storeBrokerageStatus == 2) {
|
||||
return true;
|
||||
} else {
|
||||
$storeBrokeragePrice = sys_config('store_brokerage_price', 0);
|
||||
return $price >= $storeBrokeragePrice;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('array_unique_fb')) {
|
||||
/**
|
||||
* 二维数组去掉重复值
|
||||
|
||||
@ -5,7 +5,6 @@ namespace app\dao\order;
|
||||
|
||||
use crmeb\basic\BaseDao;
|
||||
use app\model\order\OrderCenter;
|
||||
use app\model\user\User;
|
||||
|
||||
/**
|
||||
* 订单中台数据访问层
|
||||
@ -331,41 +330,6 @@ class OrderCenterDao extends BaseDao
|
||||
return compact('list', 'count');
|
||||
}
|
||||
|
||||
/**
|
||||
* 按下单人的归属字段分组统计订单(事业部/代理商/员工维度统计)
|
||||
* 新架构订单表无 division_id/agent_id/staff_id 快照字段,归属关系实时取自用户表
|
||||
* @param string $userField 用户表分组字段(division_id/agent_id/staff_id,白名单校验)
|
||||
* @param array $time 时间范围 [开始日期, 结束日期]
|
||||
* @param int $page 页码
|
||||
* @param int $limit 每页数量
|
||||
* @param string $sort 排序字段(为空时默认按订单数倒序)
|
||||
* @param string $order 排序方向
|
||||
* @return array ['list' => 列表, 'count' => 总数]
|
||||
*/
|
||||
public function statisticGroupByUserField(string $userField, array $time = [], int $page = 0, int $limit = 0, string $sort = '', string $order = ''): array
|
||||
{
|
||||
// 分组字段白名单校验,防止 SQL 注入
|
||||
if (!in_array($userField, ['division_id', 'agent_id', 'staff_id'], true)) {
|
||||
$userField = 'division_id';
|
||||
}
|
||||
// 取用户表真实表名(含前缀),拼接关联查询
|
||||
$userTable = app()->make(User::class)->getName();
|
||||
$model = $this->getModel()->alias('o')
|
||||
->join($userTable . ' u', 'o.uid = u.uid')
|
||||
->where('u.' . $userField, '>', 0)
|
||||
->when(!empty($time), function ($query) use ($time) {
|
||||
// 按下单时间范围筛选
|
||||
$query->whereBetween('o.add_time', [strtotime($time[0]), strtotime($time[1] . ' 23:59:59')]);
|
||||
});
|
||||
// 分组后总数:先取全部分组键再计数(分组维度为事业部/代理商/员工,数量有限)
|
||||
$count = count($model->distinct(true)->field('u.' . $userField)->select()->toArray());
|
||||
// 默认按订单数倒序
|
||||
$orderStr = $sort === '' ? 'order_sum desc' : $sort . ' ' . $order;
|
||||
$list = $model->fieldRaw('u.' . $userField . ', COUNT(*) AS order_sum, SUM(o.pay_price) AS order_sum_price')
|
||||
->group('u.' . $userField)->order($orderStr)->page($page, $limit)->select()->toArray();
|
||||
return compact('list', 'count');
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户集合的已支付未退款订单金额与数量(推广订单概况)
|
||||
* @param array $uids 用户ID集合
|
||||
|
||||
@ -203,49 +203,6 @@ class UserBillDao extends BaseDao
|
||||
return $this->getModel()->where('uid', $uid)->where('link_id', $id)->where('type', 'pay_money')->update(['take' => 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $where
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getUserBillList(array $where)
|
||||
{
|
||||
return $this->search($where)->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取佣金排行
|
||||
* @param array $where
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function brokerageRankList(array $where, int $page, int $limit)
|
||||
{
|
||||
return $this->search($where)->field('uid,SUM(IF(pm=1,`number`,-`number`)) as brokerage_price')->with(['user' => function ($query) {
|
||||
$query->field('uid,avatar,nickname');
|
||||
}])->order('brokerage_price desc')->group('uid')->page($page, $limit)->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间分组
|
||||
* @param array $where
|
||||
* @param string $filed
|
||||
* @param string $group
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUserBillListByGroup(array $where, string $filed, string $group, int $page, int $limit)
|
||||
{
|
||||
return $this->search($where)->field($filed)->where('number', '>', 0)->order('add_time desc')->group($group)->page($page, $limit)->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $where
|
||||
* @param int $page
|
||||
|
||||
@ -1,137 +0,0 @@
|
||||
<?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\dao\user;
|
||||
|
||||
use crmeb\basic\BaseDao;
|
||||
use app\model\user\UserBrokerage;
|
||||
|
||||
/**
|
||||
* 用户佣金持久化查询。
|
||||
*/
|
||||
class UserBrokerageDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 按收款人汇总订单佣金及退款冲正,不混入提现流水。
|
||||
* @param int $orderId 中台订单ID
|
||||
* @return array
|
||||
*/
|
||||
public function getOrderBrokerageRewards(int $orderId): array
|
||||
{
|
||||
return $this->getModel()->where('link_id', $orderId)->where('status', 1)
|
||||
->whereIn('type', ['self_brokerage', 'one_brokerage', 'two_brokerage', 'staff_brokerage',
|
||||
'agent_brokerage', 'division_brokerage', 'pink_master_brokerage', 'refund', 'brokerage_refund'])
|
||||
->field('uid, pm, SUM(number) AS number')->group('uid, pm')->order('uid asc')->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款后解除本单原佣金冻结,避免冻结额超过冲正后余额。
|
||||
* @param int $orderId 中台订单ID
|
||||
* @return void
|
||||
*/
|
||||
public function releaseOrderRewardFreeze(int $orderId): void
|
||||
{
|
||||
$this->getModel()->where('link_id', $orderId)->where('pm', 1)
|
||||
->whereIn('type', ['self_brokerage', 'one_brokerage', 'two_brokerage', 'staff_brokerage',
|
||||
'agent_brokerage', 'division_brokerage', 'pink_master_brokerage'])
|
||||
->update(['frozen_time' => 0]);
|
||||
}
|
||||
/**
|
||||
* 设置模型
|
||||
* @return string
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return UserBrokerage::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列表
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @param array $typeWhere
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getList(array $where, string $field = '*', int $page = 0, int $limit = 0, array $typeWhere = [])
|
||||
{
|
||||
return $this->search($where)->when(count($typeWhere) > 0, function ($query) use ($typeWhere) {
|
||||
$query->where($typeWhere);
|
||||
})->field($field)->when($page && $limit, function ($query) use ($page, $limit) {
|
||||
$query->page($page, $limit);
|
||||
})->order('id desc')->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询列表
|
||||
* @param array $where
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getUserBrokerageList(array $where)
|
||||
{
|
||||
return $this->search($where)->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取佣金排行
|
||||
* @param array $where
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function brokerageRankList(array $where, int $page = 0, int $limit = 0)
|
||||
{
|
||||
//SUM(IF(pm=1,`number`,-`number`))
|
||||
if ($where['pm'] == 1) $where['not_type'] = ['extract_fail'];
|
||||
return $this->search($where)->field('uid,SUM(number) as brokerage_price')->with(['user' => function ($query) {
|
||||
$query->field('uid,avatar,nickname');
|
||||
}])->order('brokerage_price desc')->group('uid')->when($page && $limit, function ($query) use ($page, $limit) {
|
||||
$query->page($page, $limit);
|
||||
})->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某些条件的佣金总数
|
||||
* @param array $where
|
||||
* @return mixed
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function getBrokerageSumColumn(array $where)
|
||||
{
|
||||
if ($where['pm'] == 1) $where['not_type'] = ['extract_fail'];
|
||||
if (isset($where['uid']) && is_array($where['uid'])) {
|
||||
return $this->search($where)->group('uid')->column('sum(number) as num', 'uid');
|
||||
} else
|
||||
return $this->search($where)->sum('number');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个账户下的冻结佣金
|
||||
* @param int $uid
|
||||
* @return float
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function getUserFrozenPrice(int $uid)
|
||||
{
|
||||
return $this->search(['uid' => $uid, 'status' => 1, 'pm' => 1])->where('frozen_time', '>', time())->sum('number');
|
||||
}
|
||||
}
|
||||
@ -30,7 +30,7 @@ class UserDao extends BaseDao
|
||||
public function getByUidForUpdate(int $uid)
|
||||
{
|
||||
return $this->getModel()->where('uid', $uid)
|
||||
->field('uid, integral, brokerage_price, pay_count, is_promoter')->lock(true)->find();
|
||||
->field('uid, integral, pay_count')->lock(true)->find();
|
||||
}
|
||||
|
||||
protected function setModel(): string
|
||||
|
||||
@ -1,135 +0,0 @@
|
||||
<?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\dao\user;
|
||||
|
||||
use crmeb\basic\BaseDao;
|
||||
use app\model\user\UserExtract;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class UserExtractDao
|
||||
* @package app\dao\user
|
||||
*/
|
||||
class UserExtractDao extends BaseDao
|
||||
{
|
||||
|
||||
/**
|
||||
* 设置模型
|
||||
* @return string
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return UserExtract::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个条件的提现总和
|
||||
* @param array $where
|
||||
* @return float
|
||||
*/
|
||||
public function getWhereSum(array $where)
|
||||
{
|
||||
return $this->search($where)->sum('extract_price');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某些条件总数组合列表
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function getWhereSumList(array $where, string $field = 'extract_price', string $key = 'uid')
|
||||
{
|
||||
return $this->search($where)->group($key)->column('sum(' . $field . ')', $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提现列表
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getExtractList(array $where = [], string $field = '*', int $page = 0, int $limit = 0)
|
||||
{
|
||||
return $this->search($where)->field($field)->with([
|
||||
'user' => function ($query) {
|
||||
$query->field('uid,nickname');
|
||||
}])->page($page, $limit)->order('id desc')->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个字段总和
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @return float
|
||||
*/
|
||||
public function getWhereSumField(array $where, string $field)
|
||||
{
|
||||
return $this->search($where, false)
|
||||
->when(isset($where['timeKey']), function ($query) use ($where) {
|
||||
$query->whereBetweenTime('add_time', $where['timeKey']['start_time'], $where['timeKey']['end_time']);
|
||||
})
|
||||
->sum($field);
|
||||
}
|
||||
|
||||
/**根据某字段分组查询
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @param string $group
|
||||
* @return mixed
|
||||
*/
|
||||
public function getGroupField(array $where, string $field, string $group)
|
||||
{
|
||||
return $this->search($where, false)
|
||||
->when(isset($where['timeKey']), function ($query) use ($where, $field, $group) {
|
||||
$query->whereBetweenTime('add_time', $where['timeKey']['start_time'], $where['timeKey']['end_time']);
|
||||
$timeUinx = "%H";
|
||||
if ($where['timeKey']['days'] == 1) {
|
||||
$timeUinx = "%H";
|
||||
} elseif ($where['timeKey']['days'] == 30) {
|
||||
$timeUinx = "%Y-%m-%d";
|
||||
} elseif ($where['timeKey']['days'] == 365) {
|
||||
$timeUinx = "%Y-%m";
|
||||
} elseif ($where['timeKey']['days'] > 1 && $where['timeKey']['days'] < 30) {
|
||||
$timeUinx = "%Y-%m-%d";
|
||||
} elseif ($where['timeKey']['days'] > 30 && $where['timeKey']['days'] < 365) {
|
||||
$timeUinx = "%Y-%m";
|
||||
}
|
||||
$query->field("sum($field) as number,FROM_UNIXTIME($group, '$timeUinx') as time");
|
||||
$query->group("FROM_UNIXTIME($group, '$timeUinx')");
|
||||
})
|
||||
->order('add_time ASC')->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @return float
|
||||
*/
|
||||
public function getExtractMoneyByWhere(array $where, string $field)
|
||||
{
|
||||
return $this->search($where)->sum($field);
|
||||
}
|
||||
|
||||
public function getExtractByOrderId($uid, $order_id)
|
||||
{
|
||||
$info = $this->getModel()->where('uid', $uid)->where('wechat_order_id', $order_id)->find();
|
||||
return $info ? $info->toArray() : [];
|
||||
}
|
||||
}
|
||||
@ -1,193 +0,0 @@
|
||||
<?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\dao\user;
|
||||
|
||||
use crmeb\basic\BaseDao;
|
||||
use app\model\user\User;
|
||||
use app\model\user\UserBrokerage;
|
||||
|
||||
|
||||
/**
|
||||
* 用户关联佣金
|
||||
* Class UserUserBrokerageDao
|
||||
* @package app\dao\user
|
||||
*/
|
||||
class UserUserBrokerageDao extends BaseDao
|
||||
{
|
||||
|
||||
/**
|
||||
* 佣金表别名(join 查询使用,PHP 8.2 需显式声明避免动态属性弃用)
|
||||
* @var string
|
||||
*/
|
||||
protected $join_alis = '';
|
||||
|
||||
/**
|
||||
* 设置模型
|
||||
* @return string
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return User::class;
|
||||
}
|
||||
|
||||
public function joinModel(): string
|
||||
{
|
||||
return UserBrokerage::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联模型
|
||||
* @param string $alias
|
||||
* @param string $join_alias
|
||||
* @return \crmeb\basic\BaseModel
|
||||
*/
|
||||
public function getModel(string $alias = 'u', string $join_alias = 'b', $join = 'left')
|
||||
{
|
||||
$this->alias = $alias;
|
||||
$this->join_alis = $join_alias;
|
||||
/** @var UserBrokerage $userBrokerage */
|
||||
$userBrokerage = app()->make($this->joinModel());
|
||||
$table = $userBrokerage->getName();
|
||||
$query = parent::getModel()
|
||||
->join($table . ' ' . $join_alias, $alias . '.uid = ' . $join_alias . '.uid', $join)
|
||||
->alias($alias);
|
||||
if ($table === 'distribution_brokerage') {
|
||||
$query->join('distribution_user au', $alias . '.uid = au.uid', 'left');
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组合条件模型查询列表
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @param string $order
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function getList(array $where, string $field = '', string $order = '', int $page = 0, int $limit = 0)
|
||||
{
|
||||
$time = $where['time'];
|
||||
unset($where['time']);
|
||||
return $this->getModel()->when($time !== '', function ($query) use ($time) {
|
||||
$timeKey = $this->join_alis . '.add_time';
|
||||
switch ($time) {
|
||||
case 'today':
|
||||
case 'week':
|
||||
case 'month':
|
||||
case 'year':
|
||||
case 'yesterday':
|
||||
case 'last year':
|
||||
case 'last week':
|
||||
case 'last month':
|
||||
$query->whereTime($timeKey, $time);
|
||||
break;
|
||||
case 'quarter':
|
||||
[$startTime, $endTime] = $this->getMonth();
|
||||
$query->whereBetween($timeKey, [$startTime, $endTime]);
|
||||
break;
|
||||
case 'lately7':
|
||||
$query->whereBetween($timeKey, [strtotime("-7 day"), time()]);
|
||||
break;
|
||||
case 'lately30':
|
||||
$query->whereBetween($timeKey, [strtotime("-30 day"), time()]);
|
||||
break;
|
||||
default:
|
||||
if (strstr($time, '-') !== false) {
|
||||
[$startTime, $endTime] = explode('-', $time);
|
||||
$startTime = trim($startTime);
|
||||
$endTime = trim($endTime);
|
||||
if ($startTime && $endTime) {
|
||||
$query->whereBetween($timeKey, [strtotime($startTime), $startTime == $endTime ? strtotime($endTime) + 86400 : strtotime($endTime)]);
|
||||
} else if (!$startTime && $endTime) {
|
||||
$query->whereTime($timeKey, '<', strtotime($endTime) + 86400);
|
||||
} else if ($startTime && !$endTime) {
|
||||
$query->whereTime($timeKey, '>=', strtotime($startTime));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
})->where($where)->field($field)->group('u.uid')->order($order)->order('id desc')
|
||||
->when($page && $limit, function ($query) use ($page, $limit) {
|
||||
$query->page($page, $limit);
|
||||
})->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取条数
|
||||
* @param array $where
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCount(array $where)
|
||||
{
|
||||
$time = $where['time'];
|
||||
unset($where['time']);
|
||||
return $this->getModel()->when($time !== '', function ($query) use ($time) {
|
||||
$timeKey = $this->join_alis . '.add_time';
|
||||
switch ($time) {
|
||||
case 'today':
|
||||
case 'week':
|
||||
case 'month':
|
||||
case 'year':
|
||||
case 'yesterday':
|
||||
case 'last year':
|
||||
case 'last week':
|
||||
case 'last month':
|
||||
$query->whereTime($timeKey, $time);
|
||||
break;
|
||||
case 'quarter':
|
||||
[$startTime, $endTime] = $this->getMonth();
|
||||
$query->whereBetween($timeKey, [$startTime, $endTime]);
|
||||
break;
|
||||
case 'lately7':
|
||||
$query->whereBetween($timeKey, [strtotime("-7 day"), time()]);
|
||||
break;
|
||||
case 'lately30':
|
||||
$query->whereBetween($timeKey, [strtotime("-30 day"), time()]);
|
||||
break;
|
||||
default:
|
||||
if (strstr($time, '-') !== false) {
|
||||
[$startTime, $endTime] = explode('-', $time);
|
||||
$startTime = trim($startTime);
|
||||
$endTime = trim($endTime);
|
||||
if ($startTime && $endTime) {
|
||||
$query->whereBetween($timeKey, [strtotime($startTime), $startTime == $endTime ? strtotime($endTime) + 86400 : strtotime($endTime)]);
|
||||
} else if (!$startTime && $endTime) {
|
||||
$query->whereTime($timeKey, '<', strtotime($endTime) + 86400);
|
||||
} else if ($startTime && !$endTime) {
|
||||
$query->whereTime($timeKey, '>=', strtotime($startTime));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
})->where($where)->group('u.uid')->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本季度 time
|
||||
* @param int $ceil
|
||||
* @return array
|
||||
*/
|
||||
public function getMonth(int $ceil = 0)
|
||||
{
|
||||
if ($ceil != 0) {
|
||||
$season = ceil(date('n') / 3) - $ceil;
|
||||
} else {
|
||||
$season = ceil(date('n') / 3);
|
||||
}
|
||||
$firstday = date('Y-m-01', mktime(0, 0, 0, ($season - 1) * 3 + 1, 1, date('Y')));
|
||||
$lastday = date('Y-m-t', mktime(0, 0, 0, $season * 3, 1, date('Y')));
|
||||
return [$firstday, $lastday];
|
||||
}
|
||||
}
|
||||
@ -310,11 +310,6 @@ class UserWechatUserDao extends BaseDao
|
||||
if (isset($where['status']) && $where['status'] !== '' && $where['status'] !== null) {
|
||||
$model = $model->where($userAlias . 'status', $where['status']);
|
||||
}
|
||||
// 是否为推广员
|
||||
if (isset($where['is_promoter']) && $where['is_promoter'] !== '' && $where['is_promoter'] !== null) {
|
||||
$model = $model->where($userAlias . 'is_promoter', $where['is_promoter']);
|
||||
}
|
||||
|
||||
// --- 标签筛选 ---
|
||||
// label_id: 单个ID或ID数组/逗号分隔字符串
|
||||
if (isset($where['label_id']) && $where['label_id']) {
|
||||
@ -416,11 +411,6 @@ class UserWechatUserDao extends BaseDao
|
||||
$model->whereIn($userAlias . 'uid', $where['ids']);
|
||||
}
|
||||
|
||||
// --- 代理等级筛选 ---
|
||||
if (isset($where['agent_level']) && $where['agent_level'] !== '' && $where['agent_level'] !== null) {
|
||||
$model->where($userAlias . 'agent_level', $where['agent_level']);
|
||||
}
|
||||
|
||||
return $field ? $model->field($field) : $model;
|
||||
}
|
||||
|
||||
|
||||
@ -35,10 +35,8 @@ return [
|
||||
'UserExpChange' => [\app\listener\user\UserExpChangeListener::class],
|
||||
'UserBalanceChange' => [\app\listener\user\UserBalanceChangeListener::class, \app\listener\user\UserBalanceChangeNoticeListener::class],
|
||||
// UserRecharge / UserRechargeRefund 已由 modules/marketing/recharge/event.php 自注册,勿在此重复注册
|
||||
'UserExtract' => [\app\listener\user\UserExtractListener::class],
|
||||
'UserExtractApply' => [\app\listener\user\UserExtractApplyListener::class],
|
||||
// UserExtract / UserExtractApply / UserBrokerageChange 事件随提现与佣金链路下线(归属分销插件)一并移除
|
||||
'UserRevenue' => [\app\listener\user\UserRevenueListener::class, \app\listener\user\UserRevenueNoticeListener::class],
|
||||
'UserBrokerageChange' => [\app\listener\user\UserBrokerageChangeListener::class],
|
||||
'UserCancel' => [\app\listener\user\UserCancelListener::class],
|
||||
|
||||
// ============ 管理员事件 ============
|
||||
|
||||
@ -3,9 +3,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace app\jobs;
|
||||
|
||||
use app\services\user\OfflineReceiptServices;
|
||||
use app\services\order\OrderLifecycleServices;
|
||||
use app\services\user\MemberOrderServices;
|
||||
use app\services\user\member\MemberCardServices;
|
||||
use app\services\user\UserServices;
|
||||
use app\services\user\UserBillServices;
|
||||
@ -47,8 +45,8 @@ class OfflineReceiptJob extends BaseJobs
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户订单数和推广员状态
|
||||
* 增加用户订单计数,并判断是否达到推广员门槛
|
||||
* 更新用户订单数
|
||||
* 推广员升级已由分销插件接管,本方法只保留 pay_count 累加逻辑
|
||||
* @param array $order 订单数据
|
||||
*/
|
||||
public function setUserPayCountAndPromoter($order)
|
||||
@ -58,19 +56,6 @@ class OfflineReceiptJob extends BaseJobs
|
||||
$userInfo = $userServices->get($order['uid']);
|
||||
if ($userInfo) {
|
||||
$userInfo->pay_count = $userInfo->pay_count + 1;
|
||||
if (!$userInfo->is_promoter) {
|
||||
/** @var OfflineReceiptServices $orderServices */
|
||||
$orderServices = app()->make(OfflineReceiptServices::class);
|
||||
/** @var MemberOrderServices $memberOrderServices */
|
||||
$memberOrderServices = app()->make(MemberOrderServices::class);
|
||||
// 累计消费金额 = 会员订单 + 线下收银订单(两表合计)
|
||||
$price = $orderServices->sum(['paid' => 1, 'uid' => $userInfo['uid']], 'paid_price')
|
||||
+ $memberOrderServices->sum(['paid' => 1, 'uid' => $userInfo['uid']], 'pay_price');
|
||||
$status = is_brokerage_statu($price);
|
||||
if ($status) {
|
||||
$userInfo->is_promoter = 1;
|
||||
}
|
||||
}
|
||||
$userInfo->save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,8 +47,8 @@ class PosterJob extends BaseJobs
|
||||
$routineSpreadBanner = sys_data('routine_spread_banner');
|
||||
if (!count($routineSpreadBanner)) return false;
|
||||
//小程序
|
||||
$name_routine = $user['uid'] . '_' . $user['is_promoter'] . '_user_routine.jpg';
|
||||
$name_wap = $user['uid'] . '_' . $user['is_promoter'] . '_user_wap.jpg';
|
||||
$name_routine = $user['uid'] . '_user_routine.jpg';
|
||||
$name_wap = $user['uid'] . '_user_wap.jpg';
|
||||
$imageInfo_routine = $attachment->getInfo(['real_name' => $name_routine, 'module_type' => 2]);
|
||||
$imageInfo_wap = $attachment->getInfo(['real_name' => $name_wap, 'module_type' => 2]);
|
||||
if (isset($imageInfo_routine['att_dir']) && strstr($imageInfo_routine['att_dir'], 'http') !== false && curl_file_exist($imageInfo_routine['att_dir']) === false) {
|
||||
@ -143,7 +143,7 @@ class PosterJob extends BaseJobs
|
||||
),
|
||||
'background' => $item['pic']
|
||||
);
|
||||
$resRoutine = $resRoutine && $posterInfo_routine = PosterServices::setSharePoster($config, 'routine/spread/poster', $user['uid'] . '_' . $user['is_promoter'] . '_user_routine_poster_' . $key . '.jpg');
|
||||
$resRoutine = $resRoutine && $posterInfo_routine = PosterServices::setSharePoster($config, 'routine/spread/poster', $user['uid'] . '_user_routine_poster_' . $key . '.jpg');
|
||||
if (!is_array($posterInfo_routine)) return false;
|
||||
$attachment->attachmentAdd($posterInfo_routine['name'], $posterInfo_routine['size'], $posterInfo_routine['type'], $posterInfo_routine['dir'], $posterInfo_routine['thumb_path'], 1, $posterInfo_routine['image_type'], $posterInfo_routine['time'], 2);
|
||||
if ($resRoutine) {
|
||||
@ -192,7 +192,7 @@ class PosterJob extends BaseJobs
|
||||
),
|
||||
'background' => $item['pic']
|
||||
);
|
||||
$resWap = $resWap && $posterInfo_wap = PosterServices::setSharePoster($config, 'wap/spread/poster', $user['uid'] . '_' . $user['is_promoter'] . '_user_wap_poster_' . $key . '.jpg');
|
||||
$resWap = $resWap && $posterInfo_wap = PosterServices::setSharePoster($config, 'wap/spread/poster', $user['uid'] . '_user_wap_poster_' . $key . '.jpg');
|
||||
if (!is_array($posterInfo_wap)) return false;
|
||||
$attachment->attachmentAdd($posterInfo_wap['name'], $posterInfo_wap['size'], $posterInfo_wap['type'], $posterInfo_wap['dir'], $posterInfo_wap['thumb_path'], 1, $posterInfo_wap['image_type'], $posterInfo_wap['time'], 2);
|
||||
if ($resWap) {
|
||||
|
||||
@ -756,18 +756,13 @@ return [
|
||||
'订单未支付' => 'Order not paid',
|
||||
'佣金转入余额,不能退款' => 'The commission is transferred to the balance and cannot be refunded',
|
||||
'已支付的订单记录无法删除' => 'The paid order record cannot be deleted',
|
||||
'转入金额不能大于可提现佣金' => 'The transfer amount cannot be greater than the commission that can be withdrawn',
|
||||
'写入余额充值失败' => 'Failed to write balance recharge',
|
||||
'您选择的充值方式已下架' => 'The recharge method you selected has been removed',
|
||||
'充值订单生成失败' => 'Failed to generate recharge order',
|
||||
'保存用户信息失败' => 'Failed to save user information',
|
||||
'密码重置失败' => 'Password reset failed',
|
||||
'增加推广人数失败' => 'Failed to increase the number of promotions',
|
||||
'设置登录类型失败' => 'Failed to set login type',
|
||||
'设置推广员失败' => 'Failed to set promoter',
|
||||
'增加用户余额失败' => 'Failed to increase user balance',
|
||||
'减少用户余额失败' => 'Failed to reduce user balance',
|
||||
'减少用户佣金失败' => 'Failed to reduce user commission',
|
||||
'增加用户积分失败' => 'Failed to increase user points',
|
||||
'减少用户积分失败' => 'Failed to reduce user points',
|
||||
'增加用户经验失败' => 'Failure to increase user experience',
|
||||
|
||||
@ -756,18 +756,13 @@ return [
|
||||
'订单未支付' => '订单未支付',
|
||||
'佣金转入余额,不能退款' => '佣金转入余额,不能退款',
|
||||
'已支付的订单记录无法删除' => '已支付的订单记录无法删除',
|
||||
'转入金额不能大于可提现佣金' => '转入金额不能大于可提现佣金',
|
||||
'写入余额充值失败' => '写入余额充值失败',
|
||||
'您选择的充值方式已下架' => '您选择的充值方式已下架',
|
||||
'充值订单生成失败' => '充值订单生成失败',
|
||||
'保存用户信息失败' => '保存用户信息失败',
|
||||
'密码重置失败' => '密码重置失败',
|
||||
'增加推广人数失败' => '增加推广人数失败',
|
||||
'设置登录类型失败' => '设置登录类型失败',
|
||||
'设置推广员失败' => '设置推广员失败',
|
||||
'增加用户余额失败' => '增加用户余额失败',
|
||||
'减少用户余额失败' => '减少用户余额失败',
|
||||
'减少用户佣金失败' => '减少用户佣金失败',
|
||||
'增加用户积分失败' => '增加用户积分失败',
|
||||
'减少用户积分失败' => '减少用户积分失败',
|
||||
'增加用户经验失败' => '增加用户经验失败',
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
<?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\listener\user;
|
||||
|
||||
use crmeb\interfaces\ListenerInterface;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 用户佣金变动事件监听器
|
||||
* Class UserBrokerageChangeListener
|
||||
* @package app\listener\user
|
||||
*/
|
||||
class UserBrokerageChangeListener implements ListenerInterface
|
||||
{
|
||||
public function handle($event): void
|
||||
{
|
||||
[$spreadUid, $userType, $brokeragePrice, $goodsName, $goodsPrice, $addTime] = $event;
|
||||
try {
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('用户佣金变动监听失败,失败原因:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
<?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\listener\user;
|
||||
|
||||
use crmeb\interfaces\ListenerInterface;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 用户提现申请事件监听器
|
||||
* Class UserExtractApplyListener
|
||||
* @package app\listener\user
|
||||
*/
|
||||
class UserExtractApplyListener implements ListenerInterface
|
||||
{
|
||||
/**
|
||||
* 用户提现申请事件处理
|
||||
* @param $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event): void
|
||||
{
|
||||
[$nickname, $amount] = $event;
|
||||
|
||||
try {
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('用户提现申请监听失败,失败原因:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
<?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\listener\user;
|
||||
|
||||
use crmeb\interfaces\ListenerInterface;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 用户提现成功事件监听器
|
||||
* Class UserExtractListener
|
||||
* @package app\listener\user
|
||||
*/
|
||||
class UserExtractListener implements ListenerInterface
|
||||
{
|
||||
/**
|
||||
* 用户提现成功事件处理
|
||||
* @param $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event): void
|
||||
{
|
||||
[$uid, $extractInfo, $amount, $fee, $userType, $nickname, $userPhone] = $event;
|
||||
|
||||
try {
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('用户提现成功监听失败,失败原因:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -40,9 +40,6 @@ class UserRegisterListener implements ListenerInterface
|
||||
/** @var UserBillServices $userBill */
|
||||
$userBill = app()->make(UserBillServices::class);
|
||||
$userBill->inviteUserIncExp((int)$spreadUid);
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$userServices->addBrokeragePrice($uid, $spreadUid);
|
||||
|
||||
event('UserRegisterDistributionJob', ['uid' => $uid]);
|
||||
}
|
||||
@ -69,9 +66,7 @@ class UserRegisterListener implements ListenerInterface
|
||||
'action' => 'saveProfile',
|
||||
'args' => [(int)$uid, ['is_promoter' => 1, 'spread_open' => 1]],
|
||||
]);
|
||||
if (!is_array($handled) || ($handled[0] ?? null) === null) {
|
||||
app()->make(UserServices::class)->update($uid, ['is_promoter' => 1]);
|
||||
}
|
||||
// is_promoter 列已移除:插件未接管时不落用户表
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,15 +136,6 @@ class User extends BaseModel
|
||||
return $this->hasMany(UserAddress::class, 'uid', 'uid');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联提现
|
||||
* @return \think\model\relation\HasMany
|
||||
*/
|
||||
public function extract()
|
||||
{
|
||||
return $this->hasMany(UserExtract::class, 'uid', 'uid');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联订单
|
||||
* @return User|\think\model\relation\HasMany
|
||||
@ -245,16 +236,6 @@ class User extends BaseModel
|
||||
$query->where('group_id', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否推广人搜索器
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchIsPromoterAttr($query, $value)
|
||||
{
|
||||
$query->where('is_promoter', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态搜索器
|
||||
* @param Model $query
|
||||
@ -331,16 +312,6 @@ class User extends BaseModel
|
||||
$query->where('pay_count', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户推广资格
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchSpreadOpenAttr($query, $value)
|
||||
{
|
||||
if ($value !== '' && $value !== null) $query->where('spread_open', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* nickname搜索器
|
||||
* @param $query
|
||||
@ -351,76 +322,6 @@ class User extends BaseModel
|
||||
$query->where('nickname', "like", "%" . $value . "%");
|
||||
}
|
||||
|
||||
/**
|
||||
* division_type搜索器
|
||||
* @param $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchDivisionTypeAttr($query, $value)
|
||||
{
|
||||
if ($value !== '') $query->where('division_type', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* division_id搜索器
|
||||
* @param $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchDivisionIdAttr($query, $value)
|
||||
{
|
||||
if ((int)$value !== 0) $query->where('division_id', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* agent_id搜索器
|
||||
* @param $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchAgentIdAttr($query, $value)
|
||||
{
|
||||
if ($value !== '') $query->where('agent_id', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* staff_id搜索器
|
||||
* @param $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchStaffIdAttr($query, $value)
|
||||
{
|
||||
if ($value !== '') $query->where('staff_id', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* is_division搜索器
|
||||
* @param $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchIsDivisionAttr($query, $value)
|
||||
{
|
||||
if ($value !== '') $query->where('is_division', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* is_agent搜索器
|
||||
* @param $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchIsAgentAttr($query, $value)
|
||||
{
|
||||
if ($value !== '') $query->where('is_agent', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* is_staff搜索器
|
||||
* @param $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchIsStaffAttr($query, $value)
|
||||
{
|
||||
if ($value !== '') $query->where('is_staff', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $query
|
||||
* @param $value
|
||||
|
||||
@ -1,169 +0,0 @@
|
||||
<?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\model\user;
|
||||
|
||||
use app\model\user\concern\UsesDistributionOwnedTable;
|
||||
use app\model\order\OrderCenter;
|
||||
use crmeb\basic\BaseModel;
|
||||
use crmeb\traits\ModelTrait;
|
||||
use think\Model;
|
||||
|
||||
class UserBrokerage extends BaseModel
|
||||
{
|
||||
use ModelTrait;
|
||||
use UsesDistributionOwnedTable;
|
||||
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
/**
|
||||
* 模型名称
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'user_brokerage';
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
$this->useDistributionOwnedTable('distribution_brokerage');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联订单表
|
||||
* @return UserBill|model\relation\HasOne
|
||||
*/
|
||||
public function order()
|
||||
{
|
||||
return $this->hasOne(OrderCenter::class, 'id', 'link_id')->field(['id', 'total_num'])->bind(['total_num']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联用户
|
||||
* @return model\relation\HasOne
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne(User::class, 'uid', 'uid');
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户uid
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchUidAttr($query, $value)
|
||||
{
|
||||
if ($value !== '') {
|
||||
if (is_array($value))
|
||||
$query->whereIn('uid', $value);
|
||||
else
|
||||
$query->where('uid', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联id
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchLinkIdAttr($query, $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
$query->whereIn('link_id', $value);
|
||||
else
|
||||
$query->where('link_id', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支出|获得
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchPmAttr($query, $value)
|
||||
{
|
||||
if ($value !== '') $query->where('pm', $value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 类型
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchTypeAttr($query, $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
$query->whereIn('type', $value);
|
||||
else
|
||||
$query->where('type', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchNotTypeAttr($query, $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
$query->whereNotIn('type', $value);
|
||||
else
|
||||
$query->where('type', '<>', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态 0:带确定 1:有效 -1:无效
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchStatusAttr($query, $value)
|
||||
{
|
||||
$query->where('status', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否收货 0:未收货 1:已收货
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchTakeAttr($query, $value)
|
||||
{
|
||||
$query->where('take', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊搜索
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchLikeAttr($query, $value)
|
||||
{
|
||||
$query->where(function ($query) use ($value) {
|
||||
$query->where('uid|title', 'like', "%$value%")->whereOr('uid', 'in', function ($query) use ($value) {
|
||||
$query->name('user')->whereLike('uid|account|nickname|phone', '%' . $value . '%')->field('uid')->select();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchAddTimeAttr($query, $value)
|
||||
{
|
||||
if (is_string($value)) $query->whereTime($query, $value);
|
||||
if (is_array($value) && count($value) == 2) $query->whereTime('add_time', 'between', $value);
|
||||
}
|
||||
}
|
||||
@ -41,8 +41,7 @@ class UserCancel extends BaseModel
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne(User::class, 'uid', 'uid')->bind([
|
||||
'money' => 'now_money',
|
||||
'brokerage' => 'brokerage_price'
|
||||
'money' => 'now_money'
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@ -1,123 +0,0 @@
|
||||
<?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\model\user;
|
||||
|
||||
use app\model\user\concern\UsesDistributionOwnedTable;
|
||||
use crmeb\basic\BaseModel;
|
||||
use crmeb\traits\ModelTrait;
|
||||
use think\model;
|
||||
|
||||
/**
|
||||
* Class UserExtract
|
||||
* @package app\model\user
|
||||
*/
|
||||
class UserExtract extends BaseModel
|
||||
{
|
||||
use ModelTrait;
|
||||
use UsesDistributionOwnedTable;
|
||||
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
/**
|
||||
* 模型名称
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'user_extract';
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
$this->useDistributionOwnedTable('distribution_extract');
|
||||
}
|
||||
|
||||
//审核中
|
||||
const AUDIT_STATUS = 0;
|
||||
//未通过
|
||||
const FAIL_STATUS = -1;
|
||||
//已提现
|
||||
const SUCCESS_STATUS = 1;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $status = [
|
||||
-1 => '未通过',
|
||||
0 => '审核中',
|
||||
1 => '已提现'
|
||||
];
|
||||
|
||||
/**
|
||||
* 关联user
|
||||
* @return model\relation\HasOne
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne(User::class, 'uid', 'uid');
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户uid
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchUidAttr($query, $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
$query->whereIn('uid', $value);
|
||||
else
|
||||
$query->where('uid', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提现方式
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchExtractTypeAttr($query, $value)
|
||||
{
|
||||
if ($value !== '' && $value !== null) $query->where('extract_type', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核状态
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchStatusAttr($query, $value)
|
||||
{
|
||||
if ($value !== '') {
|
||||
$query->where('status', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊搜索
|
||||
* @param Model $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchLikeAttr($query, $value)
|
||||
{
|
||||
if ($value) {
|
||||
$query->where(function ($query) use ($value) {
|
||||
$query->where('real_name|id|bank_code|alipay_code', 'LIKE', "%$value%")->whereOr('uid', 'in', function ($query) use ($value) {
|
||||
$query->name('user')->whereLike('nickname', '%' . $value . '%')->field('uid')->select();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -43,6 +43,7 @@ class OrderLifecycleServices extends BaseService
|
||||
{
|
||||
/**
|
||||
* 按订单幂等增加购买次数,标记与计数同事务提交。
|
||||
* 推广员升级已由分销插件接管,本方法只保留 pay_count 累加逻辑。
|
||||
* @param array $order 订单任务数据
|
||||
* @return void
|
||||
*/
|
||||
@ -69,14 +70,6 @@ class OrderLifecycleServices extends BaseService
|
||||
if (!$userServices->bcInc((int)$center['uid'], 'pay_count', '1', 'uid', 0)) {
|
||||
throw new \RuntimeException('购买次数更新失败');
|
||||
}
|
||||
if (!(int)$user['is_promoter']) {
|
||||
$price = $centerDao->sum(['pay_status' => 1, 'refund_status' => 0, 'uid' => (int)$center['uid']], 'pay_price');
|
||||
if (is_brokerage_statu($price)) {
|
||||
if (!$userServices->update((int)$center['uid'], ['is_promoter' => 1])) {
|
||||
throw new \RuntimeException('推广资格更新失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$centerDao->update((int)$center['id'], ['pay_counted' => 1])) {
|
||||
throw new \RuntimeException('购买次数幂等标记更新失败');
|
||||
}
|
||||
|
||||
@ -5,15 +5,14 @@ namespace app\services\order;
|
||||
|
||||
use app\dao\order\OrderCenterDao;
|
||||
use app\dao\user\UserBillDao;
|
||||
use app\dao\user\UserBrokerageDao;
|
||||
use app\dao\user\UserDao;
|
||||
use app\services\user\UserBillServices;
|
||||
use app\services\user\UserBrokerageServices;
|
||||
use crmeb\basic\BaseService;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 普通订单退款权益冲正:按累计退款比例计算目标,扣除已冲正流水后只补差额。
|
||||
* 佣金冲正由分销插件自行处理,本服务只负责积分。
|
||||
*/
|
||||
class OrderRefundRewardServices extends BaseService
|
||||
{
|
||||
@ -23,8 +22,6 @@ class OrderRefundRewardServices extends BaseService
|
||||
protected UserDao $userDao;
|
||||
/** @var UserBillDao */
|
||||
protected UserBillDao $billDao;
|
||||
/** @var UserBrokerageDao */
|
||||
protected UserBrokerageDao $brokerageDao;
|
||||
|
||||
/**
|
||||
* 初始化账务依赖。
|
||||
@ -35,11 +32,10 @@ class OrderRefundRewardServices extends BaseService
|
||||
$this->centerDao = $this->app->make(OrderCenterDao::class);
|
||||
$this->userDao = $this->app->make(UserDao::class);
|
||||
$this->billDao = $this->app->make(UserBillDao::class);
|
||||
$this->brokerageDao = $this->app->make(UserBrokerageDao::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对齐普通订单积分与佣金;同订单退款及收货共用订单锁。
|
||||
* 对齐普通订单赠积分;同订单退款及收货共用订单锁。
|
||||
* @param array $order 中台订单
|
||||
* @param string $refundTotal 累计到账退款额
|
||||
* @return void
|
||||
@ -111,48 +107,7 @@ class OrderRefundRewardServices extends BaseService
|
||||
// 短缺也记入已处理目标,保留旧政策:不在重试时追扣后续到账的用户积分。
|
||||
$accounted['integral'] = max($processed, (int)$integralTarget);
|
||||
|
||||
$accounts = [];
|
||||
foreach ($this->brokerageDao->getOrderBrokerageRewards((int)$order['id']) as $bill) {
|
||||
$key = (int)$bill['uid'];
|
||||
$dimension = (int)$bill['pm'] === 1 ? 'earned' : 'returned';
|
||||
$accounts[$key][$dimension] = bcadd($accounts[$key][$dimension] ?? '0.00', (string)$bill['number'], 2);
|
||||
}
|
||||
ksort($accounts);
|
||||
foreach ($accounts as $recipient => $account) {
|
||||
$target = $this->target($account['earned'] ?? '0', $total, $paid, 2);
|
||||
$processed = (string)($accounted['brokerage'][$recipient] ?? '0');
|
||||
if (bccomp((string)($account['returned'] ?? '0'), $processed, 2) > 0) {
|
||||
$processed = (string)$account['returned'];
|
||||
}
|
||||
$due = bcsub($target, $processed, 2);
|
||||
$accounted['brokerage'][$recipient] = bccomp($target, $processed, 2) > 0 ? $target : $processed;
|
||||
if (bccomp($due, '0', 2) <= 0) {
|
||||
continue;
|
||||
}
|
||||
$recipientUser = $this->userDao->getByUidForUpdate($recipient);
|
||||
if (!$recipientUser) {
|
||||
throw new \RuntimeException('退款佣金收款人不存在');
|
||||
}
|
||||
$profile = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices',
|
||||
'action' => 'profile',
|
||||
'args' => [$recipient, ['brokerage_price' => $recipientUser['brokerage_price']]],
|
||||
]);
|
||||
$balance = (string)($profile[0]['brokerage_price'] ?? $recipientUser['brokerage_price']);
|
||||
$deduct = bccomp($due, $balance, 2) > 0 ? $balance : $due;
|
||||
// 保留原规则:不扣成负余额,短缺留日志供人工核对。
|
||||
if (bccomp($deduct, '0', 2) > 0 && !$this->app->make(UserBrokerageServices::class)->brokerageIncome(
|
||||
'brokerage_refund', $recipient, $deduct, (int)$order['id'], '订单退款退佣:' . $order['order_no']
|
||||
)) {
|
||||
throw new \RuntimeException('退款佣金冲正失败');
|
||||
}
|
||||
if (bccomp($due, $deduct, 2) > 0) {
|
||||
Log::warning('[RefundReward] 佣金余额不足 order_no=' . $order['order_no'] . ' uid=' . $recipient
|
||||
. ' shortfall=' . bcsub($due, $deduct, 2));
|
||||
}
|
||||
}
|
||||
$this->billDao->releaseOrderRewardFreeze((int)$order['id'], $uid);
|
||||
$this->brokerageDao->releaseOrderRewardFreeze((int)$order['id']);
|
||||
if ($recorded !== $accounted) {
|
||||
if (!$this->centerDao->update((int)$order['id'], [
|
||||
'refund_reward_accounted' => json_encode($accounted, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR),
|
||||
@ -168,7 +123,7 @@ class OrderRefundRewardServices extends BaseService
|
||||
* @param string $reward 原奖励
|
||||
* @param string $refunded 累计退款
|
||||
* @param string $paid 原实付
|
||||
* @param int $scale 积分为0,佣金为2
|
||||
* @param int $scale 积分为0
|
||||
* @return string
|
||||
*/
|
||||
protected function target(string $reward, string $refunded, string $paid, int $scale): string
|
||||
|
||||
@ -17,7 +17,6 @@ use app\dao\product\CenterProductDao;
|
||||
use crmeb\basic\BaseService;
|
||||
use app\services\user\member\MemberCardServices;
|
||||
use app\services\user\UserBillServices;
|
||||
use app\services\user\UserBrokerageServices;
|
||||
use app\services\user\UserServices;
|
||||
use app\services\order\AfterSaleServices;
|
||||
use app\model\order\OrderCenter;
|
||||
@ -169,7 +168,7 @@ class OrderTakeServices extends BaseService
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$userInfo = $userServices->get((int)$order['uid']);
|
||||
// get() 返回 Model,distributeBrokerage 等签名要求 array
|
||||
// get() 返回 Model,赠送积分/经验等方法签名要求 array
|
||||
if (is_object($userInfo)) {
|
||||
$userInfo = $userInfo->toArray();
|
||||
}
|
||||
@ -219,13 +218,11 @@ class OrderTakeServices extends BaseService
|
||||
throw new ApiException('收货失败');
|
||||
}
|
||||
}
|
||||
//统一返佣(整合一级、二级、员工、代理、事业部、团长返佣)
|
||||
$res2 = $this->distributeBrokerage($order, $userInfo);
|
||||
//赠送积分
|
||||
$res1 = $this->gainUserIntegral($order, $userInfo, $storeTitle);
|
||||
//赠送积分(佣金发放已由分销插件在支付环节处理)
|
||||
$res1 = $this->gainUserIntegral($order, $userInfo, $storeTitle);
|
||||
//经验
|
||||
$res3 = $this->gainUserExp($order, $userInfo);
|
||||
if (!($res1 && $res2 && $res3)) {
|
||||
if (!($res1 && $res3)) {
|
||||
throw new ApiException('收货失败');
|
||||
}
|
||||
app()->make(OrderRefundRewardServices::class)->reconcile($order, (string)($order['refund_price'] ?? '0'));
|
||||
@ -344,321 +341,6 @@ class OrderTakeServices extends BaseService
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一发放佣金(整合一级、二级、员工、代理、事业部、团长返佣)
|
||||
* @param array $orderInfo 订单信息
|
||||
* @param array $userInfo 用户信息
|
||||
* @return bool
|
||||
*/
|
||||
public function distributeBrokerage(array $orderInfo, array $userInfo): bool
|
||||
{
|
||||
if (!$orderInfo || !$userInfo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 分销归属和金额由插件在下单时固化,优先使用快照,避免关系变更影响历史订单。
|
||||
$snapshot = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionOrderServices',
|
||||
'action' => 'snapshot',
|
||||
'args' => [(int)($orderInfo['id'] ?? 0), (string)($orderInfo['order_no'] ?? $orderInfo['order_id'] ?? '')],
|
||||
]);
|
||||
if (is_array($snapshot[0] ?? null) && $snapshot[0]) {
|
||||
$orderInfo = array_replace($orderInfo, $snapshot[0]);
|
||||
}
|
||||
|
||||
// 分销功能未开启
|
||||
if (!sys_config('brokerage_func_status')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 防重复返佣:检查该订单是否已发放过佣金
|
||||
/** @var UserBrokerageServices $userBrokerageServices */
|
||||
$userBrokerageServices = app()->make(UserBrokerageServices::class);
|
||||
$existBrokerage = $userBrokerageServices->count([
|
||||
'link_id' => $orderInfo['id'],
|
||||
'pm' => 1,
|
||||
'type' => ['self_brokerage', 'one_brokerage', 'two_brokerage', 'staff_brokerage', 'agent_brokerage', 'division_brokerage', 'pink_master_brokerage']
|
||||
]);
|
||||
if ($existBrokerage > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 佣金冻结时间
|
||||
$frozenTime = time() + intval(sys_config('extract_time')) * 86400;
|
||||
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
|
||||
// 拼团团长返佣(独立于分销返佣,不受 is_commission 限制)
|
||||
$this->givePinkMasterBrokerage($orderInfo, $userInfo, $frozenTime);
|
||||
|
||||
// 以下分销返佣受 checkCommissionStatus 控制
|
||||
if (!$this->checkCommissionStatus($orderInfo)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 一级返佣
|
||||
$firstLevelUid = $this->getFirstLevelUid($orderInfo, $userInfo);
|
||||
if ($firstLevelUid && ($orderInfo['one_brokerage'] ?? 0) > 0 && $userServices->checkUserPromoter($firstLevelUid)) {
|
||||
// 自购返佣用 get_self_brokerage,推广返佣用 get_brokerage
|
||||
$type = ($firstLevelUid == $orderInfo['uid']) ? 'get_self_brokerage' : 'get_brokerage';
|
||||
$this->giveBrokerage($type, $firstLevelUid, $orderInfo['one_brokerage'], $orderInfo['pay_price'], $userInfo['nickname'], $frozenTime, $orderInfo['id']);
|
||||
$this->sendBackOrderBrokerage($orderInfo, $firstLevelUid, $orderInfo['one_brokerage']);
|
||||
}
|
||||
|
||||
// 二级返佣
|
||||
$secondLevelUid = $this->getSecondLevelUid($orderInfo, $userInfo);
|
||||
if ($secondLevelUid && ($orderInfo['two_brokerage'] ?? 0) > 0 && $userServices->checkUserPromoter($secondLevelUid)) {
|
||||
$this->giveBrokerage('get_two_brokerage', $secondLevelUid, $orderInfo['two_brokerage'], $orderInfo['pay_price'], $userInfo['nickname'], $frozenTime, $orderInfo['id']);
|
||||
$this->sendBackOrderBrokerage($orderInfo, $secondLevelUid, $orderInfo['two_brokerage']);
|
||||
}
|
||||
|
||||
// 员工返佣(无需推广员资格校验)
|
||||
if ($orderInfo['staff_id'] && $orderInfo['staff_brokerage'] > 0) {
|
||||
$this->giveBrokerage('get_staff_brokerage', $orderInfo['staff_id'], $orderInfo['staff_brokerage'], $orderInfo['pay_price'], $userInfo['nickname'], $frozenTime, $orderInfo['id']);
|
||||
}
|
||||
|
||||
// 代理商返佣(无需推广员资格校验)
|
||||
if ($orderInfo['agent_id'] && $orderInfo['agent_brokerage'] > 0) {
|
||||
$this->giveBrokerage('get_agent_brokerage', $orderInfo['agent_id'], $orderInfo['agent_brokerage'], $orderInfo['pay_price'], $userInfo['nickname'], $frozenTime, $orderInfo['id']);
|
||||
}
|
||||
|
||||
// 事业部返佣(无需推广员资格校验)
|
||||
if ($orderInfo['division_id'] && $orderInfo['division_brokerage'] > 0) {
|
||||
$this->giveBrokerage('get_division_brokerage', $orderInfo['division_id'], $orderInfo['division_brokerage'], $orderInfo['pay_price'], $userInfo['nickname'], $frozenTime, $orderInfo['id']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测营销活动是否参与返佣
|
||||
* @param array $orderInfo 订单信息
|
||||
* @return bool true=可返佣 false=不可返佣
|
||||
*/
|
||||
protected function checkCommissionStatus(array $orderInfo): bool
|
||||
{
|
||||
// 推广关系绑定失效
|
||||
if (isset($orderInfo['spread_uid']) && $orderInfo['spread_uid'] == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 拼团:检测是否参与返佣
|
||||
if (isset($orderInfo['combination_id']) && $orderInfo['combination_id']) {
|
||||
// 通过 ActivityDataProxy 事件代理查询拼团是否参与返佣(避免直接引用 modules 层)
|
||||
$_results = event('ActivityDataProxy', ['mode' => 'combination', 'action' => 'value', 'args' => [['id' => $orderInfo['combination_id']], 'is_commission']]);
|
||||
$isCommission = proxy_event_result($_results);
|
||||
if (!$isCommission) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 秒杀:检测是否参与返佣
|
||||
if (isset($orderInfo['seckill_id']) && $orderInfo['seckill_id']) {
|
||||
// 通过 ActivityDataProxy 事件代理查询秒杀是否参与返佣
|
||||
$_results = event('ActivityDataProxy', ['mode' => 'seckill', 'action' => 'value', 'args' => [['id' => $orderInfo['seckill_id']], 'is_commission']]);
|
||||
$seckillCommission = proxy_event_result($_results);
|
||||
if (!$seckillCommission) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 砍价:检测是否参与返佣
|
||||
if (isset($orderInfo['bargain_id']) && $orderInfo['bargain_id']) {
|
||||
// 通过 ActivityDataProxy 事件代理查询砍价是否参与返佣
|
||||
$_results = event('ActivityDataProxy', ['mode' => 'bargain', 'action' => 'value', 'args' => [['id' => $orderInfo['bargain_id']], 'is_commission']]);
|
||||
$bargainCommission = proxy_event_result($_results);
|
||||
if (!$bargainCommission) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一级推广人UID
|
||||
* 兼容旧订单(订单表无spread_uid字段时,从用户信息中获取上级)
|
||||
* @param array $orderInfo 订单信息
|
||||
* @param array $userInfo 用户信息
|
||||
* @return int 一级推广人UID,0表示无推广人
|
||||
*/
|
||||
protected function getFirstLevelUid(array $orderInfo, array $userInfo): int
|
||||
{
|
||||
$isSelfBrokerage = sys_config('is_self_brokerage', 0);
|
||||
|
||||
// 推广关系绑定失效
|
||||
if (isset($orderInfo['spread_uid']) && $orderInfo['spread_uid'] == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 兼容旧订单:订单表无 spread_uid 字段
|
||||
if (!isset($orderInfo['spread_uid']) || !$orderInfo['spread_uid']) {
|
||||
// 未开启自购返佣,且用户无上级或上级是自己,不返佣
|
||||
if (!$isSelfBrokerage && (!$userInfo['spread_uid'] || $userInfo['spread_uid'] == $orderInfo['uid'])) {
|
||||
return 0;
|
||||
}
|
||||
// 开启自购返佣返回自己,否则返回上级
|
||||
return $isSelfBrokerage ? $userInfo['uid'] : $userInfo['spread_uid'];
|
||||
}
|
||||
|
||||
return $orderInfo['spread_uid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二级推广人UID
|
||||
* 兼容旧订单(订单表无spread_two_uid字段时,逐级查找上级的上级)
|
||||
* @param array $orderInfo 订单信息
|
||||
* @param array $userInfo 用户信息
|
||||
* @return int 二级推广人UID,0表示无二级推广人
|
||||
*/
|
||||
protected function getSecondLevelUid(array $orderInfo, array $userInfo): int
|
||||
{
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$isSelfBrokerage = sys_config('is_self_brokerage', 0);
|
||||
|
||||
// 推广关系绑定失效
|
||||
if (isset($orderInfo['spread_two_uid']) && $orderInfo['spread_two_uid'] == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 订单表有二级推广人
|
||||
if (isset($orderInfo['spread_two_uid']) && $orderInfo['spread_two_uid']) {
|
||||
return $orderInfo['spread_two_uid'];
|
||||
}
|
||||
|
||||
// 兼容旧订单:从一级推广人的上级中查找
|
||||
$userInfoTwo = $userServices->get((int)$userInfo['spread_uid']);
|
||||
if (!$userInfoTwo) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 未开启自购返佣,且二级推广人无上级或上级是下单人自己,不返佣
|
||||
if (!$isSelfBrokerage && (!$userInfoTwo['spread_uid'] || $userInfoTwo['spread_uid'] == $orderInfo['uid'])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $isSelfBrokerage ? $userInfoTwo['uid'] : $userInfoTwo['spread_uid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用发放佣金方法
|
||||
* @param string $type 佣金类型标识(对应 UserBrokerageServices::$incomeData 的 key)
|
||||
* @param int $uid 接收佣金的用户ID
|
||||
* @param float $amount 佣金金额
|
||||
* @param float $payPrice 订单支付金额
|
||||
* @param string $nickname 下单用户昵称
|
||||
* @param int $frozenTime 佣金冻结时间
|
||||
* @param int $orderId 关联订单ID
|
||||
* @return bool
|
||||
*/
|
||||
protected function giveBrokerage(string $type, int $uid, $amount, $payPrice, string $nickname, int $frozenTime, int $orderId): bool
|
||||
{
|
||||
/** @var UserBrokerageServices $userBrokerageServices */
|
||||
$userBrokerageServices = app()->make(UserBrokerageServices::class);
|
||||
|
||||
// 事务内自动增加余额并记录流水
|
||||
return $userBrokerageServices->brokerageIncome($type, $uid, [
|
||||
'nickname' => $nickname,
|
||||
'pay_price' => floatval($payPrice),
|
||||
'number' => floatval($amount),
|
||||
'frozen_time' => $frozenTime
|
||||
], $orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发放拼团团长佣金
|
||||
* 仅当下单人是团长且团长是有效推广员时才发放
|
||||
* @param array $orderInfo 订单信息
|
||||
* @param array $userInfo 用户信息
|
||||
* @param int $frozenTime 佣金冻结时间
|
||||
* @return bool
|
||||
*/
|
||||
protected function givePinkMasterBrokerage(array $orderInfo, array $userInfo, int $frozenTime): bool
|
||||
{
|
||||
// 非拼团订单
|
||||
if (!isset($orderInfo['combination_id']) || !$orderInfo['combination_id']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 通过 ActivityDataProxy 事件代理查询拼团团长佣金比例(避免直接引用 modules 层)
|
||||
$_combResults = event('ActivityDataProxy', ['mode' => 'combination', 'action' => 'getOne', 'args' => [['id' => $orderInfo['combination_id']], 'head_commission']]);
|
||||
$combinationInfo = proxy_event_result($_combResults, []);
|
||||
if (!is_array($combinationInfo)) {
|
||||
$combinationInfo = [];
|
||||
}
|
||||
|
||||
// 未设置团长佣金比例
|
||||
if (!$combinationInfo['head_commission']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 通过 ActivityDataProxy 事件代理查询团长 UID(sub_service=pink 切换到 StorePinkServices)
|
||||
$_pinkResults = event('ActivityDataProxy', ['mode' => 'combination', 'sub_service' => 'pink', 'action' => 'value', 'args' => [['id' => $orderInfo['pink_id']], 'uid']]);
|
||||
$pinkMasterUid = proxy_event_result($_pinkResults, null);
|
||||
|
||||
$userServices = app()->make(UserServices::class);
|
||||
// 下单人不是团长 或 团长不是有效推广员,不返佣
|
||||
if ($orderInfo['uid'] != $pinkMasterUid || !$userServices->checkUserPromoter($pinkMasterUid)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 计算团长佣金 = 支付金额 × 团长佣金比例
|
||||
$pinkMasterPrice = bcmul((string)$orderInfo['pay_price'], bcdiv((string)$combinationInfo['head_commission'], 100, 2), 2);
|
||||
|
||||
// 事务内自动增加余额并记录流水
|
||||
/** @var UserBrokerageServices $userBrokerageServices */
|
||||
$userBrokerageServices = app()->make(UserBrokerageServices::class);
|
||||
return $userBrokerageServices->brokerageIncome('get_pink_master_brokerage', $pinkMasterUid, [
|
||||
'number' => floatval($pinkMasterPrice),
|
||||
'frozen_time' => $frozenTime
|
||||
], $orderInfo['id']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 佣金到账发送模板消息
|
||||
* @param $orderInfo
|
||||
* @param $spread_uid
|
||||
* @param $brokeragePrice
|
||||
*/
|
||||
public function sendBackOrderBrokerage($orderInfo, $spread_uid, $brokeragePrice, string $type = 'order')
|
||||
{
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$userType = $userServices->value(['uid' => $spread_uid], 'user_type');
|
||||
$goodsPrice = 0;
|
||||
$goodsName = '推广用户获取佣金';
|
||||
if ($type == 'order') {
|
||||
// 从订单中台 extra_info.items 组装商品名与累计商品价格(旧 cart_info 表已废弃)
|
||||
/** @var OrderCenterDao $centerDao */
|
||||
$centerDao = app()->make(OrderCenterDao::class);
|
||||
$centerOrder = $centerDao->get(['order_no' => $orderInfo['order_id'] ?? ''], ['extra_info']);
|
||||
if ($centerOrder) {
|
||||
$extra = $centerOrder['extra_info'] ?? [];
|
||||
if (is_string($extra)) {
|
||||
$extra = json_decode($extra, true) ?: [];
|
||||
}
|
||||
$items = $extra['items'] ?? [];
|
||||
if (!empty($items)) {
|
||||
$goodsPrice = 0;
|
||||
$goodsName = '';
|
||||
foreach ($items as $item) {
|
||||
$goodsName .= $item['store_name'] ?? '';
|
||||
$goodsPrice += (float)($item['price'] ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$goodsName = '推广用户获取佣金';
|
||||
$goodsPrice = $brokeragePrice;
|
||||
}
|
||||
//触发佣金变动事件(消息发送已移至 UserBrokerageChangeListener)
|
||||
event('UserBrokerageChange', [(int)$spread_uid, $userType, $brokeragePrice, $goodsName, $goodsPrice, $orderInfo['add_time'] ?? time()]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 赠送经验
|
||||
* @param $order
|
||||
|
||||
@ -14,12 +14,10 @@ namespace app\services\order;
|
||||
|
||||
use app\dao\order\OrderCenterDao;
|
||||
use app\dao\product\CenterProductDao;
|
||||
use app\dao\user\UserBrokerageDao;
|
||||
use app\services\activity\coupon\StoreCouponUserServices;
|
||||
use app\services\pay\PayServices;
|
||||
use app\services\statistic\CapitalFlowServices;
|
||||
use app\services\user\UserBillServices;
|
||||
use app\services\user\UserBrokerageServices;
|
||||
use app\services\user\UserMoneyServices;
|
||||
use app\services\user\UserServices;
|
||||
use crmeb\basic\BaseService;
|
||||
@ -30,45 +28,21 @@ use think\facade\Log;
|
||||
* 订单退款执行引擎(D 系列批次 D2c 新增)
|
||||
*
|
||||
* 统一承载退款资金回滚能力(承接自旧退款服务,旧实现已随批次 C 删除)。
|
||||
* 输入为中台订单(eb_order_center)数据,按五维执行回滚:
|
||||
* - 佣金:按分销插件佣金流水 link_id=中台订单 id 反查回退
|
||||
* 输入为中台订单(eb_order_center)数据,按四维执行回滚:
|
||||
* - 积分:收回赠送积分(幂等)+ 返还下单使用积分
|
||||
* - 库存:从中台 extra_info.items 读取快照,经 ProductTypeHandler / ActivityDataProxy 回退
|
||||
* - 优惠券:从中台订单 extra_info 读取 coupon_id 并恢复用户优惠券
|
||||
* - 支付退款:balance 退余额 / yue 余额退款 / weixin、alipay、allinpay 走 PayServices::refund
|
||||
*
|
||||
* 佣金回退由分销插件自行处理,引擎不再负责。
|
||||
*
|
||||
* 引擎只负责资金/资源回滚与流水记录,不负责订单状态流转(状态机由调用方维护)
|
||||
*/
|
||||
class RefundExecutorServices extends BaseService
|
||||
{
|
||||
/**
|
||||
* 佣金流水类型集合(与分佣写入侧一致,用于按 link_id 反查回退)
|
||||
* @var array
|
||||
*/
|
||||
protected const BROKERAGE_REFUND_TYPES = [
|
||||
'self_brokerage', 'one_brokerage', 'two_brokerage',
|
||||
'staff_brokerage', 'agent_brokerage', 'division_brokerage', 'pink_master_brokerage',
|
||||
];
|
||||
|
||||
/**
|
||||
* 佣金流水数据访问
|
||||
* @var UserBrokerageDao
|
||||
*/
|
||||
protected UserBrokerageDao $brokerageDao;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* RefundExecutorServices constructor.
|
||||
* @param UserBrokerageDao $brokerageDao
|
||||
*/
|
||||
public function __construct(UserBrokerageDao $brokerageDao)
|
||||
{
|
||||
$this->brokerageDao = $brokerageDao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行订单资金回滚(引擎主入口)
|
||||
* 依次执行:佣金回退 → 积分回退 → 库存回退 → 优惠券降级 → 支付退款 → 资本流水 → 开票标记
|
||||
* 依次执行:积分回退 → 库存回退 → 优惠券降级 → 支付退款 → 资本流水 → 开票标记
|
||||
* @param array $order 中台订单数据
|
||||
* @param float $refundPrice 现金退款金额(纯积分退款传 0)
|
||||
* @param array $context 上下文:refund_type(original/balance)、refund_trade_no、refund_integral、refund_id
|
||||
@ -82,9 +56,8 @@ class RefundExecutorServices extends BaseService
|
||||
throw new \RuntimeException('订单未支付,无法退款');
|
||||
}
|
||||
|
||||
// 1. 本地资源回滚(事务内:佣金/积分/库存/优惠券均为本地库操作,支付退款为远程通道不放事务)
|
||||
// 1. 本地资源回滚(事务内:积分/库存/优惠券均为本地库操作,支付退款为远程通道不放事务)
|
||||
$this->transaction(function () use ($order, $context) {
|
||||
$this->rollbackBrokerage($order);
|
||||
$this->rollbackIntegral($order, (int)($context['refund_integral'] ?? 0));
|
||||
$this->rollbackStock($order);
|
||||
$this->rollbackCoupon($order);
|
||||
@ -312,55 +285,6 @@ class RefundExecutorServices extends BaseService
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 佣金回退:按分销插件佣金流水 link_id=中台订单 id 反查并回退
|
||||
* @param array $order 中台订单数据
|
||||
* @return void
|
||||
*/
|
||||
protected function rollbackBrokerage(array $order): void
|
||||
{
|
||||
$orderId = (int)($order['id'] ?? 0);
|
||||
if ($orderId <= 0) {
|
||||
return;
|
||||
}
|
||||
$where = [
|
||||
'link_id' => $orderId,
|
||||
'type' => self::BROKERAGE_REFUND_TYPES,
|
||||
'pm' => 1,
|
||||
];
|
||||
$list = $this->brokerageDao->getUserBrokerageList($where);
|
||||
if (empty($list)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
/** @var UserBrokerageServices $brokerageServices */
|
||||
$brokerageServices = app()->make(UserBrokerageServices::class);
|
||||
foreach ($list as $item) {
|
||||
$uid = (int)($item['uid'] ?? 0);
|
||||
$number = (float)($item['number'] ?? 0);
|
||||
if ($uid <= 0 || $number <= 0) {
|
||||
continue;
|
||||
}
|
||||
// 退佣时余额不足则只扣到 0,差额不追回(与旧 orderRefundBrokerageBack 行为一致)
|
||||
$legacyBalance = (float)$userServices->value(['uid' => $uid], 'brokerage_price');
|
||||
$profile = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices',
|
||||
'action' => 'profile',
|
||||
'args' => [$uid, ['brokerage_price' => $legacyBalance]],
|
||||
]);
|
||||
$brokeragePrice = (float)($profile[0]['brokerage_price'] ?? $legacyBalance);
|
||||
$backNumber = min($number, $brokeragePrice);
|
||||
if ($backNumber <= 0) {
|
||||
continue;
|
||||
}
|
||||
$brokerageServices->brokerageIncome('brokerage_refund', $uid, $backNumber, $orderId);
|
||||
}
|
||||
// 解除分佣冻结
|
||||
$this->brokerageDao->update($where, ['frozen_time' => 0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 优惠券回退:恢复订单创建时消费的用户优惠券
|
||||
* @param array $order 中台订单数据
|
||||
|
||||
@ -75,8 +75,16 @@ class QrcodeServices extends BaseService
|
||||
public function createTemporaryQrcode($id, $type, $qrcode_id = '')
|
||||
{
|
||||
//通过事件代理获取微信公众号 qrcode 实例,避免 app 层直接引用 modules
|
||||
$_qrResults = event('ChannelServiceProxy', ['endpoint' => 'wechat_mp', 'service' => 'WechatService', 'action' => 'qrcodeService', 'args' => []]);
|
||||
$qrcode = (is_array($_qrResults) && $_qrResults[0] !== null) ? $_qrResults[0] : null;
|
||||
// 多渠道同时注册 ChannelServiceProxy 时需取首个非 null 结果(勿用 [0])
|
||||
$qrcode = proxy_event_result(event('ChannelServiceProxy', [
|
||||
'endpoint' => 'wechat_mp',
|
||||
'service' => 'WechatService',
|
||||
'action' => 'qrcodeService',
|
||||
'args' => [],
|
||||
]));
|
||||
if (!$qrcode) {
|
||||
throw new AdminException('微信公众号二维码服务不可用,请检查公众号配置与渠道插件');
|
||||
}
|
||||
$data = $qrcode->temporary($id, 30 * 24 * 3600)->toArray();
|
||||
$data['qrcode_url'] = $data['url'];
|
||||
$data['expire_seconds'] = $data['expire_seconds'] + time();
|
||||
@ -122,8 +130,16 @@ class QrcodeServices extends BaseService
|
||||
public function createForeverQrcode($id, $type)
|
||||
{
|
||||
//通过事件代理获取微信公众号 qrcode 实例,避免 app 层直接引用 modules
|
||||
$_qrResults = event('ChannelServiceProxy', ['endpoint' => 'wechat_mp', 'service' => 'WechatService', 'action' => 'qrcodeService', 'args' => []]);
|
||||
$qrcode = (is_array($_qrResults) && $_qrResults[0] !== null) ? $_qrResults[0] : null;
|
||||
// 多渠道同时注册 ChannelServiceProxy 时需取首个非 null 结果(勿用 [0])
|
||||
$qrcode = proxy_event_result(event('ChannelServiceProxy', [
|
||||
'endpoint' => 'wechat_mp',
|
||||
'service' => 'WechatService',
|
||||
'action' => 'qrcodeService',
|
||||
'args' => [],
|
||||
]));
|
||||
if (!$qrcode) {
|
||||
throw new AdminException('微信公众号二维码服务不可用,请检查公众号配置与渠道插件');
|
||||
}
|
||||
$data = $qrcode->forever($id)->toArray();
|
||||
$data['qrcode_url'] = $data['url'];
|
||||
$data['url'] = $qrcode->url($data['ticket']);
|
||||
@ -244,7 +260,7 @@ class QrcodeServices extends BaseService
|
||||
switch ($type) {
|
||||
case 0:
|
||||
$page = 'pages/goods_details/index';
|
||||
$namePath = $id . '_' . $uid . '_' . $param['is_promoter'] . '_product.jpg';
|
||||
$namePath = $id . '_' . $uid . '_product.jpg';
|
||||
break;
|
||||
case 1:
|
||||
$page = 'pages/activity/goods_combination_details/index';
|
||||
@ -321,8 +337,13 @@ class QrcodeServices extends BaseService
|
||||
$siteUrl = sys_config('site_url');
|
||||
if (!$imageInfo) {
|
||||
//通过事件代理获取小程序码,避免 app 层直接引用 modules
|
||||
$_mpResults = event('ChannelServiceProxy', ['endpoint' => 'wechat_mini', 'service' => 'MiniProgramService', 'action' => 'appCodeUnlimitService', 'args' => [$data, $page, 280]]);
|
||||
$res = (is_array($_mpResults) && $_mpResults[0] !== null) ? $_mpResults[0] : null;
|
||||
// 多渠道同时注册 ChannelServiceProxy 时需取首个非 null 结果(勿用 [0])
|
||||
$res = proxy_event_result(event('ChannelServiceProxy', [
|
||||
'endpoint' => 'wechat_mini',
|
||||
'service' => 'MiniProgramService',
|
||||
'action' => 'appCodeUnlimitService',
|
||||
'args' => [$data, $page, 280],
|
||||
]));
|
||||
if (!$res) return false;
|
||||
$res = (string)$res;
|
||||
if (strlen($res) < 100) return 'unpublished';
|
||||
|
||||
@ -550,32 +550,6 @@ class ExportServices extends BaseService
|
||||
return $this->export($header, $title, $export, $filename, $suffix, $is_save);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户佣金导出
|
||||
* @param $data 导出数据
|
||||
*/
|
||||
public function userCommission($data = [])
|
||||
{
|
||||
$export = [];
|
||||
if (!empty($data)) {
|
||||
foreach ($data as &$value) {
|
||||
$export[] = [
|
||||
$value['nickname'],
|
||||
$value['sum_number'],
|
||||
$value['now_money'],
|
||||
$value['brokerage_price'],
|
||||
$value['extract_price'],
|
||||
];
|
||||
}
|
||||
}
|
||||
$header = ['昵称/姓名', '总佣金金额', '账户余额', '账户佣金', '提现到账佣金'];
|
||||
$title = ['佣金记录', '佣金记录' . time(), ' 生成时间:' . date('Y-m-d H:i:s', time())];
|
||||
$filename = '佣金记录_' . date('YmdHis', time());
|
||||
$suffix = 'xlsx';
|
||||
$is_save = true;
|
||||
return $this->export($header, $title, $export, $filename, $suffix, $is_save);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户积分导出
|
||||
* @param $data 导出数据
|
||||
@ -655,7 +629,7 @@ class ExportServices extends BaseService
|
||||
$item['brokerage_money'],
|
||||
$item['extract_count_price'],
|
||||
$item['extract_count_num'],
|
||||
$item['brokerage_price'],
|
||||
$item['brokerage_price'] ?? 0,
|
||||
$item['spread_name'],
|
||||
];
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ use app\dao\order\OrderCenterDao;
|
||||
use app\dao\user\UserRechargeDao;
|
||||
use app\dao\user\MemberOrderDao;
|
||||
use app\dao\user\OfflineReceiptDao;
|
||||
use app\dao\user\UserExtractDao;
|
||||
use app\services\statistic\CapitalFlowServices;
|
||||
use app\services\user\UserMoneyServices;
|
||||
use app\services\user\UserServices;
|
||||
@ -116,34 +115,6 @@ class PayRecordServices extends BaseService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转账前保存原单号及收款身份,接口受理不代表到账。
|
||||
* @param int $id 提现ID
|
||||
* @param string $orderNo 固定转账单号
|
||||
* @param string $amount 实际到账金额
|
||||
* @param array $context 原商户、应用及收款人
|
||||
* @return void
|
||||
*/
|
||||
public function prepareTransfer(int $id, string $orderNo, string $amount, array $context): void
|
||||
{
|
||||
$this->transaction(function () use ($id, $orderNo, $amount, $context) {
|
||||
$source = app()->make(UserExtractDao::class)->getDb()->where('id', $id)->lock(true)->find();
|
||||
if (!$source || (int)$source['status'] !== 0) throw new PayException('提现状态已变化');
|
||||
$original = (string)$source['wechat_order_id'];
|
||||
if ($original !== '' && $original !== $orderNo) throw new PayException('原转账单号已变化');
|
||||
if (bccomp(bcsub((string)$source['extract_price'], (string)$source['extract_fee'], 2), $amount, 2) !== 0
|
||||
|| bccomp($amount, '0', 2) <= 0) throw new PayException('提现金额不一致');
|
||||
$record = $this->dao->byOrder($orderNo, true);
|
||||
if ($record) {
|
||||
if ($record['context'] !== $context || bccomp((string)$record['amount'], $amount, 2) !== 0) throw new PayException('原转账身份已变化');
|
||||
return;
|
||||
}
|
||||
$source->save(['wechat_order_id' => $orderNo, 'out_bill_no' => $orderNo]);
|
||||
$this->dao->save(['order_no' => $orderNo, 'business_type' => 'user_extract', 'channel_key' => $context['channel_key'] ?? 'weixin_v3',
|
||||
'amount' => $amount, 'context' => $context, 'state' => 'prepared', 'add_time' => time(), 'update_time' => time()]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取业务源订单,支付和取消使用同一行锁。
|
||||
* @param string $kind 业务类型
|
||||
@ -154,8 +125,7 @@ class PayRecordServices extends BaseService
|
||||
public function source(string $kind, string $orderNo, bool $lock = false): array
|
||||
{
|
||||
$sources = ['product' => [OrderCenterDao::class, 'order_no'], 'user_recharge' => [UserRechargeDao::class, 'order_id'],
|
||||
'member' => [MemberOrderDao::class, 'order_id'], 'offline_scan' => [OfflineReceiptDao::class, 'order_no'],
|
||||
'user_extract' => [UserExtractDao::class, 'wechat_order_id|out_bill_no']];
|
||||
'member' => [MemberOrderDao::class, 'order_id'], 'offline_scan' => [OfflineReceiptDao::class, 'order_no']];
|
||||
if (!isset($sources[$kind])) throw new PayException('支付业务类型不存在');
|
||||
[$class, $field] = $sources[$kind];
|
||||
$order = app()->make($class)->getDb()->where($field, $orderNo)->lock($lock)->find();
|
||||
@ -444,8 +414,7 @@ class PayRecordServices extends BaseService
|
||||
->where('update_time', $record['update_time'])->update(['state' => 'prepared', 'update_time' => time()]);
|
||||
}
|
||||
if (in_array($record['state'], ['prepared', 'creating', 'closing', 'received'], true) && $record['context']) {
|
||||
if ($record['business_type'] === 'user_extract') app()->make(PayTransferNotifyServices::class)->sync($orderNo);
|
||||
else $this->syncPayment($orderNo);
|
||||
$this->syncPayment($orderNo);
|
||||
}
|
||||
$this->processRefund($orderNo);
|
||||
$this->runEffects($orderNo);
|
||||
|
||||
@ -10,138 +10,11 @@
|
||||
// +----------------------------------------------------------------------
|
||||
namespace app\services\pay;
|
||||
|
||||
use app\services\statistic\CapitalFlowServices;
|
||||
use app\services\user\UserExtractServices;
|
||||
use app\services\user\UserServices;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use app\dao\user\UserExtractDao;
|
||||
use crmeb\services\pay\Pay;
|
||||
use crmeb\services\pay\PayChannelConfig;
|
||||
use think\facade\Log;
|
||||
|
||||
class PayTransferNotifyServices
|
||||
{
|
||||
/**
|
||||
* 提现
|
||||
* @param string|null $order_id 订单id
|
||||
* @return bool
|
||||
*/
|
||||
public function wechatTx(?string $order_id = null, ?string $trade_no = null, ?string $state = null, ?string $fail_reason = null, array $other = [])
|
||||
{
|
||||
try {
|
||||
if (!$order_id || !$trade_no) return false;
|
||||
if (!isset($other['transfer_amount']) || empty($other['appid']) || empty($other['openid'])) return $this->sync($order_id);
|
||||
if (!in_array($state, ['ACCEPTED', 'PROCESSING', 'WAIT_USER_CONFIRM', 'TRANSFERING', 'CANCELING', 'SUCCESS', 'FAIL', 'CANCELLED'], true)) return false;
|
||||
$records = app()->make(PayRecordServices::class);
|
||||
$handler = function (array $userExtractInfo) use ($order_id, $trade_no, $state, $fail_reason, $other, $records) {
|
||||
$record = $records->find($order_id);
|
||||
$context = $record['context'] ?? [];
|
||||
$config = PayChannelConfig::get($record['channel_key'] ?? 'weixin_v3');
|
||||
if (($other['out_bill_no'] ?? '') !== $order_id || ($other['transfer_bill_no'] ?? '') !== $trade_no
|
||||
|| (!empty($userExtractInfo['transfer_bill_no']) && $userExtractInfo['transfer_bill_no'] !== $trade_no)
|
||||
|| !is_int($other['transfer_amount']) || $other['transfer_amount'] !== (int)bcmul(
|
||||
bcsub((string)$userExtractInfo['extract_price'], (string)$userExtractInfo['extract_fee'], 2), '100', 0)
|
||||
|| ($other['mchid'] ?? '') !== ($context['mch_id'] ?? $config['pay_weixin_mchid'] ?? '')) {
|
||||
throw new AdminException('转账通知与原单号、金额或商户不一致');
|
||||
}
|
||||
foreach (['app_id' => 'appid', 'openid' => 'openid'] as $key => $field) {
|
||||
if (!empty($context[$key]) && $context[$key] !== ($other[$field] ?? '')) throw new AdminException('原转账应用或收款人不一致');
|
||||
}
|
||||
if ((int)$userExtractInfo['status'] === 1) return $state === 'SUCCESS';
|
||||
if ((int)$userExtractInfo['status'] === -1) return in_array($state, ['FAIL', 'CANCELLED'], true);
|
||||
$userExtractServices = app()->make(UserExtractServices::class);
|
||||
$update = ['state' => $state, 'fail_reason' => $fail_reason ?? '', 'transfer_bill_no' => $trade_no];
|
||||
if (!empty($other['package_info'])) $update['package_info'] = $other['package_info'];
|
||||
$userExtractServices->update($userExtractInfo['id'], $update);
|
||||
if ($state === 'SUCCESS') {
|
||||
/** @var UserServices $userService */
|
||||
$userService = app()->make(UserServices::class);
|
||||
$user = $userService->getUserInfo($userExtractInfo['uid']);
|
||||
$extractNumber = bcsub($userExtractInfo['extract_price'], $userExtractInfo['extract_fee'], 2);
|
||||
/** @var CapitalFlowServices $capitalFlowServices */
|
||||
$capitalFlowServices = app()->make(CapitalFlowServices::class);
|
||||
$capitalFlowServices->setFlow([
|
||||
'order_id' => $order_id,
|
||||
'uid' => $userExtractInfo['uid'],
|
||||
'price' => bcmul('-1', $extractNumber, 2),
|
||||
'pay_type' => $userExtractInfo['extract_type'],
|
||||
'nickname' => $user['nickname'],
|
||||
'phone' => $user['phone']
|
||||
], 'extract');
|
||||
$userExtractServices->update($userExtractInfo['id'], ['status' => 1]);
|
||||
//触发提现成功事件(消息发送已移至 UserExtractListener)
|
||||
event('UserExtract', [
|
||||
$userExtractInfo['uid'],
|
||||
$userExtractInfo,
|
||||
$extractNumber,
|
||||
0,
|
||||
strtolower($user['user_type']),
|
||||
$user['nickname'],
|
||||
$user['phone'] ?? ''
|
||||
]);
|
||||
event('UserRevenue', [$userExtractInfo['uid'], $order_id, $extractNumber, 1]);
|
||||
} elseif (in_array($state, ['FAIL', 'CANCELLED'], true)) {
|
||||
$userExtractServices->changeFail($userExtractInfo['id'], $userExtractInfo, '提现失败:' . ($fail_reason ?: $state));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (!in_array($state, ['SUCCESS', 'FAIL', 'CANCELLED'], true)) {
|
||||
return app()->make(UserExtractServices::class)->transaction(function () use ($order_id, $handler) {
|
||||
$source = app()->make(UserExtractDao::class)->getDb()->where('wechat_order_id|out_bill_no', $order_id)->lock(true)->find();
|
||||
return $source ? $handler($source->toArray()) : false;
|
||||
});
|
||||
}
|
||||
return $records->complete($order_id, 'user_extract', $trade_no, $handler);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('[PayTransfer] 转账通知待重试 order_no=' . $order_id . ' type=' . get_class($e));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按原商户单号查询转账终态,历史响应缺少商户号时绑定本次请求的商户。
|
||||
* @param string $orderNo 原转账单号
|
||||
* @return bool
|
||||
*/
|
||||
public function sync(string $orderNo): bool
|
||||
{
|
||||
$record = app()->make(PayRecordServices::class)->find($orderNo);
|
||||
$context = $record['context'] ?? [];
|
||||
$config = PayChannelConfig::get($record['channel_key'] ?? 'weixin_v3');
|
||||
if (!empty($context['mch_id']) && $context['mch_id'] !== ($config['pay_weixin_mchid'] ?? '')) {
|
||||
throw new AdminException('转账商户已变化,请恢复原配置');
|
||||
}
|
||||
$pay = app()->make(Pay::class, [($context['mode'] ?? '') === 'transfer_v2' ? 'wechat_pay' : 'v3_wechat_pay']);
|
||||
if (($context['mode'] ?? '') === 'transfer_v2') {
|
||||
$detail = $pay->queryEnterpriseTransfer($orderNo, $context);
|
||||
if (!isset($detail['payment_amount'], $detail['openid']) || empty($detail['detail_id'])) throw new AdminException('企业付款查单缺少核验字段');
|
||||
$result = ['out_bill_no' => $orderNo, 'transfer_bill_no' => $detail['detail_id'],
|
||||
'state' => ($detail['status'] ?? '') === 'FAILED' ? 'FAIL' : ($detail['status'] ?? 'PROCESSING'),
|
||||
'transfer_amount' => ctype_digit((string)$detail['payment_amount']) ? (int)$detail['payment_amount'] : null,
|
||||
'appid' => $context['app_id'], 'openid' => $detail['openid'], 'mchid' => $context['mch_id'], 'fail_reason' => $detail['reason'] ?? ''];
|
||||
} elseif (($context['mode'] ?? '') === 'transfer_batch') {
|
||||
$detail = $pay->queryBatchTransfer($orderNo);
|
||||
if (($detail['out_batch_no'] ?? '') !== $orderNo || ($detail['out_detail_no'] ?? '') !== $orderNo
|
||||
|| empty($detail['detail_id']) || !isset($detail['transfer_amount'], $detail['openid'])) {
|
||||
throw new AdminException('批次转账查单缺少核验字段或单号不一致');
|
||||
}
|
||||
$result = ['out_bill_no' => $orderNo, 'transfer_bill_no' => $detail['detail_id'],
|
||||
'state' => ($detail['detail_status'] ?? '') === 'FAIL' ? 'FAIL' : ($detail['detail_status'] ?? 'PROCESSING'),
|
||||
'appid' => $context['app_id'], 'openid' => $detail['openid'], 'transfer_amount' => $detail['transfer_amount'],
|
||||
'mchid' => $detail['mchid'] ?? $context['mch_id'], 'fail_reason' => $detail['fail_reason'] ?? ''];
|
||||
} else {
|
||||
$result = $pay->queryTransferBills($orderNo);
|
||||
}
|
||||
if (!isset($result['transfer_amount']) || !is_int($result['transfer_amount']) || $result['transfer_amount'] <= 0
|
||||
|| empty($result['appid']) || empty($result['openid']) || empty($result['transfer_bill_no'])) {
|
||||
throw new AdminException('转账查单缺少有效的核验字段');
|
||||
}
|
||||
$result['mchid'] = $result['mchid'] ?? $config['pay_weixin_mchid'] ?? '';
|
||||
return $this->wechatTx($orderNo, $result['transfer_bill_no'] ?? '', $result['state'] ?? '', $result['fail_reason'] ?? '', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 红包
|
||||
* 提现转账通知(wechatTx/sync)已随佣金提现功能下线,由分销插件自行处理。
|
||||
* @param string|null $order_id 订单id
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
@ -511,8 +511,6 @@ class OutCenterProductServices extends BaseService
|
||||
'stock' => $item['stock'] ?? 0,
|
||||
'image' => $item['image'] ?? '',
|
||||
'unique_code' => $unique,
|
||||
'brokerage' => $item['brokerage'] ?? 0,
|
||||
'brokerage_two' => $item['brokerage_two'] ?? 0,
|
||||
'is_default_select' => $item['is_default_select'] ?? ($index === 0 ? 1 : 0),
|
||||
'is_show' => $item['is_show'] ?? 1,
|
||||
'bar_code' => $item['bar_code'] ?? '',
|
||||
|
||||
@ -17,7 +17,6 @@ use app\services\other\export\ExportServices;
|
||||
use app\services\user\OfflineReceiptServices;
|
||||
use app\services\user\MemberOrderServices;
|
||||
use app\dao\order\OrderCenterDao;
|
||||
use app\services\user\UserExtractServices;
|
||||
use app\services\user\UserMoneyServices;
|
||||
use app\services\user\UserRechargeServices;
|
||||
|
||||
@ -354,22 +353,6 @@ class TradeStatisticServices extends BaseService
|
||||
$Chain['out_ye'] = $outYeCurve;
|
||||
|
||||
|
||||
//支付佣金金额
|
||||
$outExtractMoney = $this->getExtractTotalMoney($where, 'sum');
|
||||
$lastOutExtractMoney = $this->getExtractTotalMoney($dateWhere, 'sum', "", $isNum);
|
||||
$OutExtractCurve = $this->getExtractTotalMoney($where, 'group', "add_time");
|
||||
$OutExtractChain = $this->countRate($outExtractMoney, $lastOutExtractMoney);
|
||||
$topData[7] = [
|
||||
'title' => '支付佣金金额',
|
||||
'desc' => '后台给推广员支付的推广佣金,以实际支付为准',
|
||||
'total_money' => $outExtractMoney,
|
||||
'rate' => $OutExtractChain,
|
||||
'value' => $OutExtractCurve['y'],
|
||||
'type' => 0,
|
||||
'sign' => 'yong',
|
||||
];
|
||||
$Chain['extract'] = $OutExtractCurve;
|
||||
|
||||
//商品退款金额(退款统计已归中台:refund_status=已退款 累计 refund_price)
|
||||
$outOrderRefund = $this->getOrderRefundTotalMoney(['time' => $where['time']], 'sum');
|
||||
$lastOutOrderRefund = $this->getOrderRefundTotalMoney(['time' => $dateWhere['time']], 'sum', "", $isNum);
|
||||
@ -387,13 +370,13 @@ class TradeStatisticServices extends BaseService
|
||||
$Chain['refund'] = $outOrderRefundCurve;
|
||||
|
||||
//支出金额
|
||||
$outTotalMoney = bcadd(bcadd($outYeMoney, $outExtractMoney, 2), $outOrderRefund, 2);
|
||||
$lastOutTotalMoney = bcadd(bcadd($lastOutYeMoney, $lastOutExtractMoney, 2), $lastOutOrderRefund, 2);
|
||||
$outTotalCurve = $this->totalArrData([$outYeCurve, $OutExtractCurve, $outOrderRefundCurve]);
|
||||
$outTotalMoney = bcadd($outYeMoney, $outOrderRefund, 2);
|
||||
$lastOutTotalMoney = bcadd($lastOutYeMoney, $lastOutOrderRefund, 2);
|
||||
$outTotalCurve = $this->totalArrData([$outYeCurve, $outOrderRefundCurve]);
|
||||
$outTotalChain = $this->countRate($outTotalMoney, $lastOutTotalMoney);
|
||||
$topData[5] = [
|
||||
'title' => '支出金额',
|
||||
'desc' => '余额支付金额、支付佣金金额、商品退款金额',
|
||||
'desc' => '余额支付金额、商品退款金额',
|
||||
'total_money' => $outTotalMoney,
|
||||
'rate' => $outTotalChain,
|
||||
'value' => $outTotalCurve['y'],
|
||||
@ -638,32 +621,6 @@ class TradeStatisticServices extends BaseService
|
||||
return $totalMoney;
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付佣金
|
||||
* @param $where
|
||||
* @param string $selectType
|
||||
* @param string $group
|
||||
* @param bool $isNum
|
||||
* @return array|float|mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getExtractTotalMoney($where, string $selectType, string $group = "", bool $isNum = false)
|
||||
{
|
||||
/** 普通商品订单支付金额 */
|
||||
/** @var UserExtractServices $extractService */
|
||||
$extractService = app()->make(UserExtractServices::class);
|
||||
$orderSumField = "extract_price";
|
||||
$whereData['status'] = 1;
|
||||
$whereData['timeKey'] = $this->TimeConvert($where['time'], $isNum);
|
||||
$totalMoney = $extractService->getOutMoneyByWhere($whereData, $orderSumField, $selectType, $group);
|
||||
if ($group) {
|
||||
|
||||
$totalMoney = $this->trendYdata($totalMoney, $whereData['timeKey']);
|
||||
|
||||
}
|
||||
return $totalMoney;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户充值营收
|
||||
* @param array $where
|
||||
|
||||
@ -16,8 +16,6 @@ use think\facade\Cache;
|
||||
use crmeb\basic\BaseService;
|
||||
use app\services\user\UserBillServices;
|
||||
use app\services\user\UserMoneyServices;
|
||||
use app\services\user\UserBrokerageServices;
|
||||
use app\services\user\UserBrokerageFrozenServices;
|
||||
use app\services\activity\coupon\StoreCouponIssueServices;
|
||||
use app\services\activity\coupon\StoreCouponProductServices;
|
||||
|
||||
@ -229,61 +227,7 @@ class DataMigrationServices extends BaseService
|
||||
}
|
||||
|
||||
Log::notice(['type' => 'data_migration', 'handler' => 'handleMoney', 'page' => $page, 'count' => count($list)]);
|
||||
|
||||
return ['completed' => false, 'count' => count($list)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理历史佣金数据
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function handleBrokerage(int $page = 1, int $limit = 100): array
|
||||
{
|
||||
/** @var UserBillServices $userBillServices */
|
||||
$userBillServices = app()->make(UserBillServices::class);
|
||||
$where = ['category' => ['', 'now_money'], 'type' => ['brokerage', 'brokerage_user', 'extract', 'refund', 'extract_fail']];
|
||||
$list = $userBillServices->getList($where, '*', $page, $limit, [], 'id asc');
|
||||
|
||||
if (empty($list)) {
|
||||
return ['completed' => true, 'processed' => 0];
|
||||
}
|
||||
|
||||
$allData = [];
|
||||
/** @var UserBrokerageFrozenServices $brokerageFrozenServices */
|
||||
$brokerageFrozenServices = app()->make(UserBrokerageFrozenServices::class);
|
||||
$frozenList = $brokerageFrozenServices->getColumn([['uill_id', 'in', array_column($list, 'id')], ['frozen_time', '>', time()]], 'uill_id,frozen_time', 'uill_id');
|
||||
|
||||
foreach ($list as $item) {
|
||||
if (in_array($item['type'], ['brokerage_user', 'extract', 'refund', 'extract_fail'])) {
|
||||
$type = $item['type'];
|
||||
} else {
|
||||
$type = strpos($item['mark'], '二级') !== false ? 'two_brokerage' : 'one_brokerage';
|
||||
}
|
||||
|
||||
$allData[] = [
|
||||
'uid' => $item['uid'],
|
||||
'link_id' => $item['link_id'],
|
||||
'pm' => $item['pm'],
|
||||
'title' => $item['title'],
|
||||
'type' => $type,
|
||||
'number' => $item['number'],
|
||||
'balance' => $item['balance'],
|
||||
'mark' => $item['mark'],
|
||||
'frozen_time' => $frozenList[$item['id']]['frozen_time'] ?? 0,
|
||||
'add_time' => strtotime($item['add_time']),
|
||||
];
|
||||
}
|
||||
|
||||
if ($allData) {
|
||||
/** @var UserBrokerageServices $userBrokerageServices */
|
||||
$userBrokerageServices = app()->make(UserBrokerageServices::class);
|
||||
$userBrokerageServices->saveAll($allData);
|
||||
}
|
||||
|
||||
Log::notice(['type' => 'data_migration', 'handler' => 'handleBrokerage', 'page' => $page, 'count' => count($list)]);
|
||||
|
||||
return ['completed' => false, 'count' => count($list)];
|
||||
}
|
||||
|
||||
|
||||
@ -136,7 +136,6 @@ class SystemClearServices extends BaseService
|
||||
"UPDATE `{$prefix}system_store_staff` SET `avatar` = replace(avatar ,'{$siteUrl}','{$url}')",
|
||||
"UPDATE `{$prefix}system_user_level` SET `image` = replace(image ,'{$siteUrl}','{$url}'),`icon` = replace(icon ,'{$siteUrl}','{$url}')",
|
||||
"UPDATE `{$prefix}user` SET `avatar` = replace(avatar ,'{$siteUrl}','{$url}')",
|
||||
"UPDATE `{$prefix}user_extract` SET `qrcode_url` = replace(qrcode_url ,'{$siteUrl}','{$url}')",
|
||||
"UPDATE `{$prefix}wechat_qrcode` SET `image` = replace(image ,'{$siteUrl}','{$url}')",
|
||||
"UPDATE `{$prefix}wechat_user` SET `headimgurl` = replace(headimgurl ,'{$siteUrl}','{$url}')",
|
||||
"UPDATE `{$prefix}theme` SET `home_data` = replace(home_data ,'{$siteUrlJson}','{$urlJson}'),`detail_data` = replace(detail_data ,'{$siteUrlJson}','{$urlJson}'),`user_data` = replace(user_data ,'{$siteUrlJson}','{$urlJson}')",
|
||||
|
||||
@ -58,7 +58,7 @@ class SystemCrudServices extends BaseService
|
||||
'system_notification', 'system_role', 'system_route',
|
||||
'system_route_cate', 'upload_storage', 'system_crontab', 'system_user_level',
|
||||
'system_crud', 'user_label_relation', 'user_brokerage_frozen',
|
||||
'user_brokerage', 'store_product_cate', 'shipping_templates_region', 'shipping_templates_no_delivery', 'shipping_templates_free', 'offline_receipt_status', 'lang_code',
|
||||
'store_product_cate', 'shipping_templates_region', 'shipping_templates_no_delivery', 'shipping_templates_free', 'offline_receipt_status', 'lang_code',
|
||||
'offline_receipt', 'member_order', 'member_order_status', 'order_economize', 'store_order_invoice',
|
||||
'agreement', 'article', 'article_category', 'article_content', 'auxiliary', 'cache',
|
||||
'capital_flow', 'category', 'diy', 'express', 'lang_type',
|
||||
@ -80,8 +80,8 @@ class SystemCrudServices extends BaseService
|
||||
'system_log', 'system_notification',
|
||||
'system_role', 'system_route', 'system_route_cate', 'upload_storage', 'system_store',
|
||||
'system_store_staff', 'system_crontab', 'system_user_level', 'template_message', 'upgrade_log',
|
||||
'user', 'user_address', 'user_bill', 'user_brokerage', 'user_brokerage_frozen', 'user_cancel',
|
||||
'user_enter', 'user_extract', 'user_friends', 'user_group', 'user_invoice', 'user_label',
|
||||
'user', 'user_address', 'user_bill', 'user_brokerage_frozen', 'user_cancel',
|
||||
'user_enter', 'user_friends', 'user_group', 'user_invoice', 'user_label',
|
||||
'user_label_relation', 'user_level', 'user_money',
|
||||
'user_search', 'user_spread', 'user_visit',
|
||||
'system_crud_data', 'admins',
|
||||
|
||||
@ -55,10 +55,7 @@ class SystemEventServices extends BaseService
|
||||
'UserBalanceChange' => '用户余额变动事件',
|
||||
'UserRecharge' => '用户充值成功事件',
|
||||
'UserRechargeRefund' => '用户充值退款事件',
|
||||
'UserExtract' => '用户提现成功事件',
|
||||
'UserExtractApply' => '用户提现申请事件',
|
||||
'UserRevenue' => '用户收益事件',
|
||||
'UserBrokerageChange' => '用户佣金变动事件',
|
||||
'UserCancel' => '用户注销事件',
|
||||
// 管理员事件
|
||||
'AdminLogin' => '管理员登录事件',
|
||||
@ -118,7 +115,6 @@ class SystemEventServices extends BaseService
|
||||
'OrderDivisionInfoQuery' => '订单分账信息查询',
|
||||
'UserRegisterDistributionJob' => '用户注册分销任务',
|
||||
'BrokerageBindingStatusChange' => '佣金绑定状态变更',
|
||||
'DistributionUnbindCrontab' => '分销关系到期解绑定时任务',
|
||||
// 通知事件
|
||||
'UnpaidOrderNotice' => '未支付订单通知事件',
|
||||
'MemberExpireNotice' => '会员过期通知事件',
|
||||
|
||||
@ -17,7 +17,6 @@ use app\dao\order\OrderCenterDao;
|
||||
use app\services\product\product\StoreProductReplyServices;
|
||||
use app\services\product\CenterProductServices;
|
||||
use app\services\system\log\SystemFileMd5Services;
|
||||
use app\services\user\UserExtractServices;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use app\dao\system\admin\SystemAdminDao;
|
||||
use app\services\system\SystemMenusServices;
|
||||
@ -453,10 +452,7 @@ class SystemAdminServices extends BaseService
|
||||
/** @var StoreProductReplyServices $replyServices */
|
||||
$replyServices = app()->make(StoreProductReplyServices::class);
|
||||
$data['commentnum'] = $replyServices->count(['is_reply' => 0]);
|
||||
/** @var UserExtractServices $extractServices */
|
||||
$extractServices = app()->make(UserExtractServices::class);
|
||||
$data['reflectnum'] = $extractServices->getCount(['status' => 0]); //提现
|
||||
$data['msgcount'] = intval($data['ordernum']) + intval($data['inventory']) + intval($data['commentnum']) + intval($data['reflectnum']);
|
||||
$data['msgcount'] = intval($data['ordernum']) + intval($data['inventory']) + intval($data['commentnum']);
|
||||
ChannelService::instance()->send('ADMIN_NEW_PUSH', $data);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
@ -220,40 +220,32 @@ class LoginServices extends BaseService
|
||||
event('UserRegister', [$spreadUid, $userInfo['user_type'], $userInfo['nickname'], $userInfo['uid'], 1]);
|
||||
}
|
||||
} else {
|
||||
//永久绑定
|
||||
$store_brokerage_binding_status = sys_config('store_brokerage_binding_status', 1);
|
||||
if ($agentUserInfo['spread_uid'] && $store_brokerage_binding_status == 1 && !isset($user['is_staff'])) {
|
||||
// 已有上级时保持原关系;没有上级时允许建立一次推广关系。
|
||||
if ($agentUserInfo['spread_uid'] && !isset($user['is_staff'])) {
|
||||
$data['login_type'] = $user['login_type'] ?? $userInfo->login_type;
|
||||
} else {
|
||||
//绑定分销关系 = 所有用户
|
||||
if (sys_config('brokerage_bindind', 1) == 1) {
|
||||
//分销绑定类型为时间段且过期 ||临时
|
||||
$store_brokerage_binding_time = sys_config('store_brokerage_binding_time', 30);
|
||||
if (!$agentUserInfo['spread_uid'] || $store_brokerage_binding_status == 3 || ($store_brokerage_binding_status == 2 && ($agentUserInfo['spread_time'] + $store_brokerage_binding_time * 24 * 3600) < time())) {
|
||||
$upperRelation = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionRelationServices',
|
||||
'action' => 'relation',
|
||||
'args' => [(int)$spreadUid, []],
|
||||
]);
|
||||
$upperSpreadUid = (int)($upperRelation[0]['spread_uid'] ?? $this->dao->value(['uid' => $spreadUid], 'spread_uid'));
|
||||
if ($spreadUid && $user['code'] != $userInfo->uid && $userInfo->uid != $upperSpreadUid) {
|
||||
$spreadInfo = $this->dao->get($spreadUid);
|
||||
$spreadProfile = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices',
|
||||
'action' => 'profile',
|
||||
'args' => [(int)$spreadUid, $spreadInfo ? $spreadInfo->toArray() : []],
|
||||
]);
|
||||
$spreadData = is_array($spreadProfile[0] ?? null) ? $spreadProfile[0] : ($spreadInfo ? $spreadInfo->toArray() : []);
|
||||
$spreadUid = (int)$spreadUid;
|
||||
$data['spread_uid'] = $spreadUid;
|
||||
$data['spread_time'] = time();
|
||||
$data['agent_id'] = $spreadData['agent_id'] ?? 0;
|
||||
$data['division_id'] = $spreadData['division_id'] ?? 0;
|
||||
$data['staff_id'] = $spreadData['staff_id'] ?? 0;
|
||||
//绑定用户后置事件(包含消息通知和绑定关系记录)
|
||||
event('UserRegister', [$spreadUid, $userInfo['user_type'], $userInfo['nickname'], $userInfo['uid'], 0]);
|
||||
}
|
||||
}
|
||||
$upperRelation = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionRelationServices',
|
||||
'action' => 'relation',
|
||||
'args' => [(int)$spreadUid, []],
|
||||
]);
|
||||
$upperSpreadUid = (int)($upperRelation[0]['spread_uid'] ?? $this->dao->value(['uid' => $spreadUid], 'spread_uid'));
|
||||
if ($spreadUid && $user['code'] != $userInfo->uid && $userInfo->uid != $upperSpreadUid) {
|
||||
$spreadInfo = $this->dao->get($spreadUid);
|
||||
$spreadProfile = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices',
|
||||
'action' => 'profile',
|
||||
'args' => [(int)$spreadUid, $spreadInfo ? $spreadInfo->toArray() : []],
|
||||
]);
|
||||
$spreadData = is_array($spreadProfile[0] ?? null) ? $spreadProfile[0] : ($spreadInfo ? $spreadInfo->toArray() : []);
|
||||
$spreadUid = (int)$spreadUid;
|
||||
$data['spread_uid'] = $spreadUid;
|
||||
$data['spread_time'] = time();
|
||||
$data['agent_id'] = $spreadData['agent_id'] ?? 0;
|
||||
$data['division_id'] = $spreadData['division_id'] ?? 0;
|
||||
$data['staff_id'] = $spreadData['staff_id'] ?? 0;
|
||||
//绑定用户后置事件(包含消息通知和绑定关系记录)
|
||||
event('UserRegister', [$spreadUid, $userInfo['user_type'], $userInfo['nickname'], $userInfo['uid'], 0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -264,15 +256,14 @@ class LoginServices extends BaseService
|
||||
];
|
||||
$agentData = array_intersect_key($data, array_flip($agentFields));
|
||||
if ($agentData) {
|
||||
$handled = event('DistributionServiceProxy', [
|
||||
// 事业部/代理/员工字段已从用户表移除:插件接管走插件档案,未接管时丢弃,不再写用户表
|
||||
event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices',
|
||||
'action' => 'saveProfile',
|
||||
'args' => [(int)$userInfo['uid'], $agentData],
|
||||
]);
|
||||
if (is_array($handled) && ($handled[0] ?? null) !== null) {
|
||||
foreach ($agentFields as $field) {
|
||||
unset($data[$field]);
|
||||
}
|
||||
foreach ($agentFields as $field) {
|
||||
unset($data[$field]);
|
||||
}
|
||||
}
|
||||
if (array_key_exists('spread_uid', $data)) {
|
||||
@ -339,10 +330,10 @@ class LoginServices extends BaseService
|
||||
]);
|
||||
$pluginAgentEnabled = is_array($spreadProfile[0] ?? null);
|
||||
$profile = $pluginAgentEnabled ? $spreadProfile[0] : ($spreadInfo ? $spreadInfo->toArray() : []);
|
||||
$data['division_id'] = $profile['division_id'] ?? 0;
|
||||
$data['agent_id'] = $profile['agent_id'] ?? 0;
|
||||
$data['staff_id'] = $profile['staff_id'] ?? 0;
|
||||
if ($pluginAgentEnabled) {
|
||||
$data['division_id'] = $profile['division_id'] ?? 0;
|
||||
$data['agent_id'] = $profile['agent_id'] ?? 0;
|
||||
$data['staff_id'] = $profile['staff_id'] ?? 0;
|
||||
$agentData = array_intersect_key($data, array_flip(['division_id', 'agent_id', 'staff_id']));
|
||||
unset($data['spread_uid'], $data['spread_time'], $data['division_id'], $data['agent_id'], $data['staff_id']);
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ use app\services\pay\PayRecordServices;
|
||||
use app\services\statistic\CapitalFlowServices;
|
||||
use app\services\user\member\MemberShipServices;
|
||||
use app\services\user\UserMoneyServices;
|
||||
use app\services\user\UserBrokerageServices;
|
||||
use app\services\user\UserServices;
|
||||
use app\services\user\member\MemberCardServices;
|
||||
use crmeb\exceptions\ApiException;
|
||||
@ -329,15 +328,6 @@ class MemberOrderServices extends BaseService
|
||||
$capitalFlowServices->setFlow($orderInfo, $type);
|
||||
}
|
||||
$res = $res1 && $res2 && $res3 && $res4;
|
||||
//分销佣金处理
|
||||
if (sys_config('member_brokerage', 0) == 1 && sys_config('brokerage_func_status', 0) == 1) {
|
||||
$spread_one = sys_config('is_self_brokerage') ? $orderInfo['uid'] : $userServices->getSpreadUid($orderInfo['uid']);
|
||||
$spread_two = sys_config('brokerage_level', 2) == 2 ? $userServices->getSpreadUid($spread_one, [], false) : 0;
|
||||
$spread_one_price = bcmul((string)$orderInfo['pay_price'], (string)bcdiv((string)sys_config('store_brokerage_ratio', 0), '100', 4), 2);
|
||||
$spread_two_price = bcmul((string)$orderInfo['pay_price'], (string)bcdiv((string)sys_config('store_brokerage_two', 0), '100', 4), 2);
|
||||
if ($spread_one && $spread_one_price > 0 && $userServices->checkUserPromoter($spread_one)) $this->memberBrokerage($spread_one, $spread_one_price, sys_config('is_self_brokerage') ? 'get_self_member_brokerage' : 'get_member_brokerage', $orderInfo);
|
||||
if ($spread_two && $spread_two_price > 0 && $userServices->checkUserPromoter($spread_two)) $this->memberBrokerage($spread_two, $spread_two_price, 'get_two_member_brokerage', $orderInfo);
|
||||
}
|
||||
|
||||
$orderInfo['pay_type'] = $paytype;
|
||||
//触发订单发货事件
|
||||
@ -346,31 +336,6 @@ class MemberOrderServices extends BaseService
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会员分销佣金处理
|
||||
* 处理会员购买产生的分销佣金
|
||||
* @param int $uid 分销员ID
|
||||
* @param float $price 佣金金额
|
||||
* @param string $type 佣金类型
|
||||
* @param array $orderInfo 订单信息
|
||||
*/
|
||||
public function memberBrokerage($uid, $price, $type, $orderInfo)
|
||||
{
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$userInfo = $userServices->get($uid);
|
||||
$broken_time = intval(sys_config('extract_time'));
|
||||
$frozen_time = time() + $broken_time * 86400;
|
||||
/** @var UserBrokerageServices $userBrokerageServices */
|
||||
$userBrokerageServices = app()->make(UserBrokerageServices::class);
|
||||
$userBrokerageServices->brokerageIncome($type, $uid, [
|
||||
'nickname' => $userInfo['nickname'],
|
||||
'pay_price' => floatval($orderInfo['pay_price']),
|
||||
'number' => floatval($price),
|
||||
'frozen_time' => $frozen_time
|
||||
], $orderInfo['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证支付会员类型
|
||||
* 根据会员类型验证支付参数,计算会员价格、过期时间等
|
||||
|
||||
@ -49,8 +49,8 @@ class OutUserServices extends BaseService
|
||||
{
|
||||
/** @var UserWechatuserServices $userWechatUser */
|
||||
$userWechatUser = app()->make(UserWechatuserServices::class);
|
||||
$fields = 'u.uid, u.real_name, u.mark, u.nickname, u.avatar, u.phone, u.now_money, u.brokerage_price, u.integral, u.exp, u.sign_num, u.user_type,
|
||||
u.status, u.level, u.agent_level, u.spread_open, u.spread_uid, u.spread_time, u.user_type, u.is_promoter, u.pay_count, u.is_ever_level, u.is_money_level,
|
||||
$fields = 'u.uid, u.real_name, u.mark, u.nickname, u.avatar, u.phone, u.now_money, u.integral, u.exp, u.sign_num, u.user_type,
|
||||
u.status, u.level, u.spread_uid, u.spread_time, u.user_type, u.pay_count, u.is_ever_level, u.is_money_level,
|
||||
u.overdue_time, u.add_time';
|
||||
[$list, $count] = $userWechatUser->getWhereUserList($where, $fields);
|
||||
if ($list) {
|
||||
@ -95,14 +95,12 @@ class OutUserServices extends BaseService
|
||||
public function userInfo($uid)
|
||||
{
|
||||
$userType = ['h5' => 'H5', 'wechat' => '公众号', 'routine' => '小程序', 'app' => 'APP'];
|
||||
$fields = ['uid', 'real_name', 'mark', 'nickname', 'avatar', 'phone', 'now_money', 'brokerage_price', 'integral', 'exp', 'sign_num', 'user_type', 'status', 'level',
|
||||
'agent_level', 'spread_open', 'spread_uid', 'spread_time', 'user_type', 'is_promoter', 'pay_count', 'is_ever_level', 'is_money_level', 'overdue_time', 'add_time'];
|
||||
$fields = ['uid', 'real_name', 'mark', 'nickname', 'avatar', 'phone', 'now_money', 'integral', 'exp', 'sign_num', 'user_type', 'status', 'level',
|
||||
'spread_uid', 'spread_time', 'user_type', 'pay_count', 'is_ever_level', 'is_money_level', 'overdue_time', 'add_time'];
|
||||
$data = app()->make(UserServices::class)->get($uid, $fields);
|
||||
$data['user_type'] = $userType[$data['user_type']];
|
||||
$data['status'] = $data['status'] ? '正常' : '禁用';
|
||||
$data['level'] = app()->make(SystemUserLevelServices::class)->value($data['level'], 'name') ?? '无';
|
||||
$data['agent_level'] = '无';
|
||||
$data['spread_open'] = $data['spread_open'] ? '分销开启' : '分销关闭';
|
||||
$data['spread_name'] = app()->make(UserServices::class)->value($data['spread_uid'], 'nickname') ?? '无';
|
||||
$data['spread_time'] = date('Y-m-d H:i:s', $data['spread_time']);
|
||||
$data['add_time'] = date('Y-m-d H:i:s', $data['add_time']);
|
||||
|
||||
@ -14,10 +14,8 @@ namespace app\services\user;
|
||||
|
||||
use crmeb\basic\BaseService;
|
||||
use app\dao\user\UserBillDao;
|
||||
use app\dao\order\OrderCenterDao;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use crmeb\exceptions\ApiException;
|
||||
use think\Exception;
|
||||
use crmeb\services\CacheService;
|
||||
use think\facade\Log;
|
||||
|
||||
@ -484,60 +482,6 @@ class UserBillServices extends BaseService
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户|所有佣金总数
|
||||
* @param array $where
|
||||
* @param int $time
|
||||
* @return float
|
||||
*/
|
||||
public function getUsersBokerageSum(array $where, $time = 0)
|
||||
{
|
||||
$where_data = [
|
||||
'type' => ['brokerage', 'brokerage_user'],
|
||||
'category' => 'now_money',
|
||||
'status' => 1,
|
||||
'pm' => $where['pm'] ?? '',
|
||||
'uid' => $where['uid'] ?? '',
|
||||
'time' => $where['time'] ?? 0
|
||||
];
|
||||
if ($time) $where_data['time'] = $time;
|
||||
return $this->dao->getBillSumColumn($where_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 某个用户佣金总和
|
||||
* @param int $uid
|
||||
* @return float
|
||||
*/
|
||||
public function getUserBillBrokerageSum(int $uid, array $type = ['brokerage', 'brokerage_user'], $time = '')
|
||||
{
|
||||
$where = ['uid' => $uid, 'category' => 'now_money'];
|
||||
if ($type) $where['type'] = $type;
|
||||
if ($time) $where['time'] = $time;
|
||||
return $this->dao->getBillSum($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户|所有佣金总数
|
||||
* @param int $uid
|
||||
* @param array $where_time
|
||||
* @return float
|
||||
*/
|
||||
public function getBrokerageSum(int $uid = 0, $where_time = [])
|
||||
{
|
||||
$where = ['category' => 'now_money', 'type' => ['system_add', 'pay_product', 'extract', 'pay_product_refund', 'system_sub'], 'pm' => 1, 'status' => 1];
|
||||
if ($uid) $where['uid'] = $uid;
|
||||
if ($where_time) $where['add_time'] = $where_time;
|
||||
return $this->dao->getBillSum($where);
|
||||
}
|
||||
|
||||
public function getBrokerageNumSum($link_ids = [])
|
||||
{
|
||||
$where = ['category' => 'now_money', 'type' => ['brokerage', 'brokerage_user']];
|
||||
if ($link_ids) $where['link_id'] = $link_ids;
|
||||
return $this->dao->getBillSum($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户|所有佣金总数
|
||||
* @param int $uid
|
||||
@ -830,76 +774,6 @@ class UserBillServices extends BaseService
|
||||
return compact('data', 'count');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取佣金列表
|
||||
* @param array $where
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function getCommissionList(array $where, int $limit = 0)
|
||||
{
|
||||
$where_data = [];
|
||||
$where_data['time'] = $where['time'];
|
||||
if (isset($where['nickname']) && $where['nickname']) {
|
||||
$where_data[] = ['u.nickname|u.uid', 'LIKE', "%$where[nickname]%"];
|
||||
}
|
||||
$brokerageModel = app()->make(\app\model\user\UserBrokerage::class);
|
||||
$balanceField = $brokerageModel->getName() === 'distribution_brokerage' ? 'au.brokerage_price' : 'u.brokerage_price';
|
||||
if (isset($where['price_max']) && isset($where['price_min'])) {
|
||||
if ($where['price_max'] !== '' && $where['price_min'] !== '') {
|
||||
$where_data[] = [$balanceField, 'between', [$where['price_min'], $where['price_max']]];
|
||||
} elseif ($where['price_min'] !== '' && $where['price_max'] === '') {
|
||||
$where_data[] = [$balanceField, '>=', $where['price_min']];
|
||||
} elseif ($where['price_min'] === '' && $where['price_max'] !== '') {
|
||||
$where_data[] = [$balanceField, '<=', $where['price_max']];
|
||||
}
|
||||
}
|
||||
$order_string = '';
|
||||
$order_arr = ['asc', 'desc'];
|
||||
if (isset($where['sum_number']) && in_array($where['sum_number'], $order_arr)) {
|
||||
$order_string .= ',income ' . $where['sum_number'];
|
||||
}
|
||||
if (isset($where['brokerage_price']) && in_array($where['brokerage_price'], $order_arr)) {
|
||||
$order_string .= ',' . $balanceField . ' ' . $where['brokerage_price'];
|
||||
}
|
||||
if ($order_string) {
|
||||
$order_string = trim($order_string, ',');
|
||||
}
|
||||
/** @var UserUserBrokerageServices $userUserBrokerage */
|
||||
$userUserBrokerage = app()->make(UserUserBrokerageServices::class);
|
||||
[$count, $list] = $userUserBrokerage->getBrokerageList($where_data, "b.type,b.pm,sum(IF(b.pm = 1 AND b.type <> 'extract_fail', b.number, 0)) as income,sum(IF(b.pm = 0, b.number, 0)) as pay,u.nickname,u.phone,u.uid,u.now_money,{$balanceField} as brokerage_price,b.add_time as time", $order_string, $limit);
|
||||
$uids = array_unique(array_column($list, 'uid'));
|
||||
/** @var UserExtractServices $userExtract */
|
||||
$userExtract = app()->make(UserExtractServices::class);
|
||||
$extractSumList = $userExtract->getUsersSumList($uids);
|
||||
foreach ($list as &$item) {
|
||||
$item['sum_number'] = $item['income'];
|
||||
$item['nickname'] = $item['nickname'] . " | " . ($item['phone'] ? $item['phone'] . " | " : '') . $item['uid'];
|
||||
$item['extract_price'] = $extractSumList[$item['uid']] ?? 0;
|
||||
$item['time'] = $item['time'] ? date('Y-m-d H:i:s', $item['time']) : '';
|
||||
}
|
||||
return compact('count', 'list');
|
||||
}
|
||||
|
||||
public function user_info(int $uid)
|
||||
{
|
||||
/** @var UserServices $user */
|
||||
$user = app()->make(UserServices::class);
|
||||
$user_info = $user->getUserInfo($uid, 'nickname,spread_uid,now_money,add_time,brokerage_price');
|
||||
if (!$user_info) {
|
||||
throw new AdminException('用户信息不存在');
|
||||
}
|
||||
$user_info = $user_info->toArray();
|
||||
$profile = event('DistributionServiceProxy', ['service' => 'DistributionUserServices', 'action' => 'profile', 'args' => [$uid, $user_info]]);
|
||||
$relation = event('DistributionServiceProxy', ['service' => 'DistributionRelationServices', 'action' => 'relation', 'args' => [$uid, $user_info]]);
|
||||
if (is_array($profile[0] ?? null)) $user_info = array_replace($user_info, $profile[0]);
|
||||
if (is_array($relation[0] ?? null)) $user_info = array_replace($user_info, $relation[0]);
|
||||
$user_info['number'] = $user_info['brokerage_price'];
|
||||
$user_info['add_time'] = date('Y-m-d H:i:s', $user_info['add_time']);
|
||||
$user_info['spread_name'] = $user_info['spread_uid'] ? $user->getUserInfo((int)$user_info['spread_uid'], 'nickname', true)['nickname'] ?? '' : '';
|
||||
return compact('user_info');
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录分享次数
|
||||
* @param int $uid 用户uid
|
||||
@ -926,25 +800,6 @@ class UserBillServices extends BaseService
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取佣金提现列表
|
||||
* @param int $uid
|
||||
* @param array $where
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getBillOneList(int $uid, array $where)
|
||||
{
|
||||
$where['uid'] = $uid;
|
||||
$data = $this->getBillList($where);
|
||||
foreach ($data['data'] as &$item) {
|
||||
$item['_add_time'] = $item['add_time'] ?? '';
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取积分列表
|
||||
* @param array $where
|
||||
@ -1030,205 +885,11 @@ class UserBillServices extends BaseService
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 佣金排行
|
||||
* @param string $time
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function brokerageRankList(string $time = 'week')
|
||||
{
|
||||
$where = [];
|
||||
$where['category'] = 'now_money';
|
||||
$where['type'] = ['brokerage', 'brokerage_user'];
|
||||
if ($time) {
|
||||
$where['time'] = $time;
|
||||
}
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$list = $this->dao->brokerageRankList($where, $page, $limit);
|
||||
foreach ($list as $key => &$item) {
|
||||
if (!isset($item['user']) || !$item['user']) {
|
||||
unset($list['$key']);
|
||||
continue;
|
||||
}
|
||||
$item['nickname'] = $item['user']['nickname'] ?? '';
|
||||
$item['avatar'] = $item['user']['avatar'] ?? '';
|
||||
if ($item['brokerage_price'] == '0.00' || $item['brokerage_price'] == 0 || !$item['brokerage_price']) {
|
||||
unset($list[$key]);
|
||||
}
|
||||
unset($item['user']);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户排名
|
||||
* @param int $uid
|
||||
* @param string $time
|
||||
*/
|
||||
public function getUserBrokerageRank(int $uid, string $time = 'week')
|
||||
{
|
||||
$where = [];
|
||||
$where['category'] = 'now_money';
|
||||
$where['type'] = ['brokerage', 'brokerage_user'];
|
||||
if ($time) {
|
||||
$where['time'] = $time;
|
||||
}
|
||||
$list = $this->dao->brokerageRankList($where, 0, 0);
|
||||
$position_tmp_one = array_column($list, 'uid');
|
||||
$position_tmp_two = array_column($list, 'brokerage_price', 'uid');
|
||||
if (!in_array($uid, $position_tmp_one)) {
|
||||
$position = 0;
|
||||
} else {
|
||||
if ($position_tmp_two[$uid] == 0.00) {
|
||||
$position = 0;
|
||||
} else {
|
||||
$position = array_search($uid, $position_tmp_one) + 1;
|
||||
}
|
||||
}
|
||||
return $position;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 推广数据 昨天的佣金 累计提现金额 当前佣金
|
||||
* @param int $uid
|
||||
* @return mixed
|
||||
*/
|
||||
public function commission(int $uid)
|
||||
{
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
if (!$userServices->getUserInfo($uid)) {
|
||||
throw new ApiException('数据不存在');
|
||||
}
|
||||
/** @var UserExtractServices $userExtract */
|
||||
$userExtract = app()->make(UserExtractServices::class);
|
||||
$data = [];
|
||||
$data['uid'] = $uid;
|
||||
$data['pm'] = 1;
|
||||
$data['commissionSum'] = $this->getUsersBokerageSum($data);
|
||||
$data['pm'] = 0;
|
||||
$data['commissionRefund'] = $this->getUsersBokerageSum($data);
|
||||
$data['commissionCount'] = $data['commissionSum'] > $data['commissionRefund'] ? bcsub((string)$data['commissionSum'], (string)$data['commissionRefund'], 2) : 0.00;
|
||||
$data['lastDayCount'] = $this->getUsersBokerageSum($data, 'yesterday');//昨天的佣金
|
||||
$data['extractCount'] = $userExtract->getUserExtract($uid);//累计提现金额
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 前端佣金排行页面数据
|
||||
* @param int $uid
|
||||
* @param $type
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function brokerage_rank(int $uid, $type)
|
||||
{
|
||||
/** @var UserServices $userService */
|
||||
$userService = app()->make(UserServices::class);
|
||||
if (!$userService->getUserInfo($uid)) {
|
||||
throw new ApiException('数据不存在');
|
||||
}
|
||||
return [
|
||||
'rank' => $this->brokerageRankList($type),
|
||||
'position' => $this->getUserBrokerageRank($uid, $type)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $uid
|
||||
* @param $type
|
||||
* @return array
|
||||
*/
|
||||
public function getUserBillList(int $uid, int $type)
|
||||
{
|
||||
$where = [];
|
||||
$where['uid'] = $uid;
|
||||
$where['category'] = 'now_money';
|
||||
switch ((int)$type) {
|
||||
case 0:
|
||||
$where['type'] = ['recharge', 'pay_money', 'system_add', 'pay_product_refund', 'system_sub', 'pay_member', 'offline_scan', 'lottery_add'];
|
||||
break;
|
||||
case 1:
|
||||
$where['type'] = ['pay_money', 'pay_member', 'offline_scan', 'user_recharge_refund'];
|
||||
break;
|
||||
case 2:
|
||||
$where['type'] = ['recharge', 'system_add', 'lottery_add'];
|
||||
break;
|
||||
case 3:
|
||||
$where['type'] = ['brokerage', 'brokerage_user'];
|
||||
break;
|
||||
case 4:
|
||||
$where['type'] = ['extract'];
|
||||
/** @var UserExtractServices $userExtractService */
|
||||
$userExtractService = app()->make(UserExtractServices::class);
|
||||
$userExtract = $userExtractService->getColumn(['uid' => $uid], 'fail_msg', 'id');
|
||||
break;
|
||||
}
|
||||
$field = 'FROM_UNIXTIME(add_time,"%Y-%m") as time,group_concat(id SEPARATOR ",") ids';
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$list = $this->dao->getUserBillListByGroup($where, $field, 'time', $page, $limit);
|
||||
$data = [];
|
||||
if ($list) {
|
||||
$listIds = array_column($list, 'ids');
|
||||
$ids = [];
|
||||
foreach ($listIds as $id) {
|
||||
$ids = array_merge($ids, explode(',', $id));
|
||||
}
|
||||
$info = $this->dao->getColumn([['id', 'in', $ids]], 'FROM_UNIXTIME(add_time,"%Y-%m-%d %H:%i") as add_time,title,number,pm,link_id', 'id');
|
||||
foreach ($list as $item) {
|
||||
$value['time'] = $item['time'];
|
||||
$id = explode(',', $item['ids']);
|
||||
array_multisort($id, SORT_DESC);
|
||||
$value['list'] = [];
|
||||
foreach ($id as $v) {
|
||||
if (isset($info[$v])) {
|
||||
if ($info[$v]['pm'] == 1 && $type == 4) $info[$v]['fail_msg'] = $userExtract[$info[$v]['link_id']];
|
||||
$value['list'][] = $info[$v];
|
||||
}
|
||||
}
|
||||
array_push($data, $value);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 推广 佣金/提现 总和
|
||||
* @param int $uid
|
||||
* @param $type 3 佣金 4 提现
|
||||
* @return mixed
|
||||
*/
|
||||
public function spread_count(int $uid, $type)
|
||||
{
|
||||
/** @var UserServices $userService */
|
||||
$userService = app()->make(UserServices::class);
|
||||
if (!$userService->getUserInfo($uid)) {
|
||||
throw new ApiException('数据不存在');
|
||||
}
|
||||
$count = 0;
|
||||
if ($type == 3) {
|
||||
$count1 = $this->getRecordCount($uid, 'now_money', ['brokerage', 'brokerage_user']);
|
||||
$count2 = $this->getRecordCount($uid, 'now_money', ['brokerage', 'brokerage_user'], '', true);
|
||||
$count = $count1 - $count2;
|
||||
} else if ($type == 4) {
|
||||
/** @var UserExtractServices $userExtract */
|
||||
$userExtract = app()->make(UserExtractServices::class);
|
||||
$count = $userExtract->getUserExtract($uid);//累计提现
|
||||
}
|
||||
return $count ?: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 推广订单
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
* @param int $uid
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
public function spread_order(int $uid, array $data)
|
||||
{
|
||||
@ -1292,55 +953,4 @@ class UserBillServices extends BaseService
|
||||
return $this->dao->getGroupField($where, $rechargeSumField, $group);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 事业部/代理商订单
|
||||
* @param $uid
|
||||
* @return array
|
||||
*/
|
||||
public function divisionOrder($uid)
|
||||
{
|
||||
/** @var UserServices $userService */
|
||||
$userService = app()->make(UserServices::class);
|
||||
$userInfo = $userService->getUserInfo($uid);
|
||||
if (!$userInfo) {
|
||||
throw new ApiException('数据不存在');
|
||||
}
|
||||
$profile = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices', 'action' => 'profile',
|
||||
'args' => [(int)$uid, is_object($userInfo) ? $userInfo->toArray() : (array)$userInfo],
|
||||
]);
|
||||
if (is_array($profile[0] ?? null)) {
|
||||
$userInfo = array_replace(is_object($userInfo) ? $userInfo->toArray() : (array)$userInfo, $profile[0]);
|
||||
}
|
||||
$division_type = (int)$userInfo['division_type'];
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$snapshot = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionOrderServices', 'action' => 'divisionOrderPage',
|
||||
'args' => [(int)$uid, $division_type, $page, $limit],
|
||||
]);
|
||||
$snapshot = is_array($snapshot[0] ?? null) ? $snapshot[0] : ['list' => [], 'count' => 0, 'months' => [], 'amountField' => 'division_brokerage'];
|
||||
$list = $snapshot['list'];
|
||||
$result['count'] = $snapshot['count'];
|
||||
$time_data = [];
|
||||
if ($list) {
|
||||
$uids = array_unique(array_column($list, 'uid'));
|
||||
$userInfos = $userService->getColumn([['uid', 'in', $uids]], 'uid,avatar,nickname', 'uid');
|
||||
foreach ($list as &$item) {
|
||||
$item['avatar'] = $userInfos[$item['uid']]['avatar'] ?? '';
|
||||
$item['nickname'] = $userInfos[$item['uid']]['nickname'] ?? '';
|
||||
$item['order_id'] = $item['order_no'] ?: $item['order_id'];
|
||||
$item['time'] = $item['add_time'] ? date('Y-m-d H:i', $item['add_time']) : '';
|
||||
$item['time_key'] = $item['add_time'] ? date('Y-m', $item['add_time']) : '';
|
||||
$item['type'] = ($item['status_code'] ?? '') === 'completed' ? 'brokerage' : 'number';
|
||||
$item['number'] = $item[$snapshot['amountField']] ?? 0;
|
||||
}
|
||||
foreach ($snapshot['months'] as $month) {
|
||||
$time_data[] = ['time' => $month['month'], 'count' => (int)$month['count']];
|
||||
}
|
||||
}
|
||||
$result['list'] = $list;
|
||||
$result['time'] = $time_data;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,541 +0,0 @@
|
||||
<?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\user;
|
||||
|
||||
use app\dao\user\UserBrokerageDao;
|
||||
use crmeb\basic\BaseService;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use crmeb\exceptions\ApiException;
|
||||
|
||||
/**
|
||||
* 用户佣金
|
||||
* Class UserBrokerageServices
|
||||
* @package app\services\user
|
||||
* @method getUserFrozenPrice(int $uid) 获取某个用户冻结的佣金
|
||||
*/
|
||||
class UserBrokerageServices extends BaseService
|
||||
{
|
||||
/**
|
||||
* @var UserBrokerageDao
|
||||
*/
|
||||
protected $dao;
|
||||
|
||||
/**
|
||||
* 用户记录模板
|
||||
* @var array[]
|
||||
*/
|
||||
protected $incomeData = [
|
||||
'get_self_member_brokerage' => [
|
||||
'title' => '获得自购付费会员佣金',
|
||||
'type' => 'self_member_brokerage',
|
||||
'mark' => '您成功消费{%pay_price%}元,奖励自购佣金{%number%}',
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
'get_member_brokerage' => [
|
||||
'title' => '获得下级购买付费会员佣金',
|
||||
'type' => 'one_member_brokerage',
|
||||
'mark' => '{%nickname%}成功消费{%pay_price%}元,奖励推广佣金{%number%}',
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
'get_two_member_brokerage' => [
|
||||
'title' => '获得二级购买付费会员佣金',
|
||||
'type' => 'two_member_brokerage',
|
||||
'mark' => '二级推广人{%nickname%}成功消费{%pay_price%}元,奖励推广佣金{%number%}',
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
'get_self_brokerage' => [
|
||||
'title' => '获得自购订单佣金',
|
||||
'type' => 'self_brokerage',
|
||||
'mark' => '您成功消费{%pay_price%}元,奖励自购佣金{%number%}',
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
'get_brokerage' => [
|
||||
'title' => '获得下级推广订单佣金',
|
||||
'type' => 'one_brokerage',
|
||||
'mark' => '{%nickname%}成功消费{%pay_price%}元,奖励推广佣金{%number%}',
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
'get_two_brokerage' => [
|
||||
'title' => '获得二级推广订单佣金',
|
||||
'type' => 'two_brokerage',
|
||||
'mark' => '二级推广人{%nickname%}成功消费{%pay_price%}元,奖励推广佣金{%number%}',
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
'get_user_brokerage' => [
|
||||
'title' => '获得推广用户佣金',
|
||||
'type' => 'brokerage_user',
|
||||
'mark' => '成功推广用户:{%nickname%},奖励推广佣金{%number%}',
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
'extract' => [
|
||||
'title' => '佣金提现',
|
||||
'type' => 'extract',
|
||||
'mark' => '{%mark%}',
|
||||
'status' => 1,
|
||||
'pm' => 0
|
||||
],
|
||||
'extract_fail' => [
|
||||
'title' => '提现失败',
|
||||
'type' => 'extract_fail',
|
||||
'mark' => '提现失败,退回佣金{%number%}元',
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
'brokerage_to_nowMoney' => [
|
||||
'title' => '佣金提现到余额',
|
||||
'type' => 'extract_money',
|
||||
'mark' => '佣金提现到余额{%number%}元',
|
||||
'status' => 1,
|
||||
'pm' => 0
|
||||
],
|
||||
'brokerage_refund' => [
|
||||
'title' => '退款退佣金',
|
||||
'type' => 'refund',
|
||||
'mark' => '订单退款扣除佣金{%number%}元',
|
||||
'status' => 1,
|
||||
'pm' => 0
|
||||
],
|
||||
'get_staff_brokerage' => [
|
||||
'title' => '获得员工推广订单佣金',
|
||||
'type' => 'staff_brokerage',
|
||||
'mark' => '{%nickname%}成功消费{%pay_price%}元,奖励推广佣金{%number%}',
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
'get_agent_brokerage' => [
|
||||
'title' => '获得代理推广订单佣金',
|
||||
'type' => 'agent_brokerage',
|
||||
'mark' => '{%nickname%}成功消费{%pay_price%}元,奖励推广佣金{%number%}',
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
'get_division_brokerage' => [
|
||||
'title' => '获得事业部推广订单佣金',
|
||||
'type' => 'division_brokerage',
|
||||
'mark' => '{%nickname%}成功消费{%pay_price%}元,奖励推广佣金{%number%}',
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
'get_pink_master_brokerage' => [
|
||||
'title' => '获得拼团团长佣金',
|
||||
'type' => 'pink_master_brokerage',
|
||||
'mark' => '开团成功,奖励团长佣金{%number%}',
|
||||
'status' => 1,
|
||||
'pm' => 1
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* UserBrokerageServices constructor.
|
||||
* @param UserBrokerageDao $dao
|
||||
*/
|
||||
public function __construct(UserBrokerageDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
/**
|
||||
* 佣金收入/支出(事务操作:记录流水 + 更新佣金余额)
|
||||
* @param string $type 佣金类型标识(对应 $incomeData 的 key)
|
||||
* @param int $uid 用户ID
|
||||
* @param int|string|array $number 佣金数量,数组时可携带 frozen_time、nickname 等额外参数
|
||||
* @param int|string $linkId 关联ID
|
||||
* @param string $mark 自定义备注,为空时使用配置模板
|
||||
* @return bool
|
||||
*/
|
||||
public function brokerageIncome(string $type, int $uid, $number, $linkId, string $mark = '')
|
||||
{
|
||||
$data = $this->incomeData[$type] ?? null;
|
||||
if (!$data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
|
||||
return $this->transaction(function () use (
|
||||
$uid, $type, $number, $linkId, $mark, $data, $userServices
|
||||
) {
|
||||
$userInfo = $userServices->get($uid, ['uid', 'brokerage_price']);
|
||||
if (!$userInfo) {
|
||||
throw new AdminException('用户不存在');
|
||||
}
|
||||
$profile = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices',
|
||||
'action' => 'profile',
|
||||
'args' => [$uid, $userInfo->toArray()],
|
||||
]);
|
||||
$pluginBalanceEnabled = is_array($profile) && is_array($profile[0] ?? null);
|
||||
|
||||
$data['uid'] = $uid;
|
||||
$data['link_id'] = $linkId;
|
||||
|
||||
if (is_array($number)) {
|
||||
$key = array_keys($number);
|
||||
$key = array_map(function ($item) {
|
||||
return '{%' . $item . '%}';
|
||||
}, $key);
|
||||
$value = array_values($number);
|
||||
$data['number'] = $number['number'] ?? 0;
|
||||
$data['frozen_time'] = $number['frozen_time'] ?? 0;
|
||||
$data['mark'] = $mark === '' ? str_replace($key, $value, $data['mark']) : $mark;
|
||||
} else {
|
||||
$data['number'] = $number;
|
||||
$data['mark'] = $mark === '' ? str_replace(['{%number%}'], $number, $data['mark']) : $mark;
|
||||
}
|
||||
|
||||
$data['add_time'] = time();
|
||||
|
||||
// 支出:扣减佣金余额(乐观锁防超扣)
|
||||
if ($data['pm'] == 0) {
|
||||
$changed = $pluginBalanceEnabled
|
||||
? event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices',
|
||||
'action' => 'changeBrokeragePrice',
|
||||
'args' => [$uid, '-' . (string)$data['number']],
|
||||
])
|
||||
: null;
|
||||
if ($pluginBalanceEnabled ? !($changed[0] ?? false) : !$userServices->bcDec($uid, 'brokerage_price', (string)$data['number'], 'uid')) {
|
||||
throw new AdminException('佣金不足');
|
||||
}
|
||||
} else {
|
||||
// 收入:增加佣金余额
|
||||
if ($pluginBalanceEnabled) {
|
||||
$changed = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices',
|
||||
'action' => 'changeBrokeragePrice',
|
||||
'args' => [$uid, (string)$data['number']],
|
||||
]);
|
||||
if (!($changed[0] ?? false)) {
|
||||
throw new AdminException('佣金余额更新失败');
|
||||
}
|
||||
} else {
|
||||
$userServices->bcInc($uid, 'brokerage_price', (string)$data['number'], 'uid');
|
||||
}
|
||||
}
|
||||
|
||||
// 重新查询余额,确保 balance 记录准确
|
||||
if ($pluginBalanceEnabled) {
|
||||
$profile = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices',
|
||||
'action' => 'profile',
|
||||
'args' => [$uid, []],
|
||||
]);
|
||||
$data['balance'] = $profile[0]['brokerage_price'] ?? 0;
|
||||
} else {
|
||||
$data['balance'] = $userServices->value(['uid' => $uid], 'brokerage_price');
|
||||
}
|
||||
|
||||
if (!$this->dao->save($data)) {
|
||||
throw new AdminException('佣金记录写入失败');
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 某个用户佣金总和
|
||||
* @param int $uid
|
||||
* @param array|string[] $type
|
||||
* @param string $time
|
||||
* @return float
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function getUserBrokerageSum(int $uid, array $type = ['one_brokerage', 'two_brokerage', 'brokerage_user'], $time = '')
|
||||
{
|
||||
$where = ['uid' => $uid];
|
||||
if ($type) $where['type'] = $type;
|
||||
if ($time) $where['time'] = $time;
|
||||
return $this->dao->sum($where, 'number', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退佣金
|
||||
* @param $order
|
||||
* @return bool
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function orderRefundBrokerageBack($order)
|
||||
{
|
||||
$snapshot = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionOrderServices',
|
||||
'action' => 'snapshot',
|
||||
'args' => [(int)($order['id'] ?? 0), (string)($order['order_no'] ?? $order['order_id'] ?? '')],
|
||||
]);
|
||||
if (is_array($snapshot[0] ?? null) && $snapshot[0]) {
|
||||
$order = array_replace((array)$order, $snapshot[0]);
|
||||
}
|
||||
$id = (int)$order['id'];
|
||||
$where = [
|
||||
'uid' => array_filter([$order['spread_uid'], $order['spread_two_uid'], $order['staff_id'], $order['agent_id'], $order['division_id']]),
|
||||
'type' => ['self_brokerage', 'one_brokerage', 'two_brokerage', 'staff_brokerage', 'agent_brokerage', 'division_brokerage', 'pink_master_brokerage'],
|
||||
'link_id' => $id,
|
||||
'pm' => 1
|
||||
];
|
||||
$brokerageList = $this->dao->getUserBrokerageList($where);
|
||||
//子订单
|
||||
if (!$brokerageList && $order['pid']) {
|
||||
$where['link_id'] = $order['pid'];
|
||||
$p_brokerageList = $this->dao->getUserBrokerageList($where);
|
||||
//主订单已分佣 子订单按订单拆分后计算结果回退
|
||||
if ($p_brokerageList) {
|
||||
$brokerageList = [
|
||||
['uid' => $order['spread_uid'], 'number' => $order['one_brokerage']],
|
||||
['uid' => $order['spread_two_uid'], 'number' => $order['two_brokerage']],
|
||||
['uid' => $order['staff_id'], 'number' => $order['staff_brokerage']],
|
||||
['uid' => $order['agent_id'], 'number' => $order['agent_brokerage']],
|
||||
['uid' => $order['division_id'], 'number' => $order['division_brokerage']],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$brokerageList) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
return $this->transaction(function () use ($brokerageList, $id, $where, $userServices) {
|
||||
foreach ($brokerageList as $item) {
|
||||
if (!$item['uid'] || $item['number'] <= 0) {
|
||||
continue;
|
||||
}
|
||||
// 退佣时余额不足则只扣到0,差额不追回
|
||||
$legacyBalance = $userServices->value(['uid' => $item['uid']], 'brokerage_price');
|
||||
$profile = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices',
|
||||
'action' => 'profile',
|
||||
'args' => [(int)$item['uid'], ['brokerage_price' => $legacyBalance]],
|
||||
]);
|
||||
$usermoney = $profile[0]['brokerage_price'] ?? $legacyBalance;
|
||||
if ($item['number'] > $usermoney) {
|
||||
$item['number'] = $usermoney;
|
||||
}
|
||||
if ($item['number'] <= 0) {
|
||||
continue;
|
||||
}
|
||||
$this->brokerageIncome('brokerage_refund', $item['uid'], $item['number'], $id);
|
||||
}
|
||||
//修改佣金冻结时间
|
||||
$this->dao->update($where, ['frozen_time' => 0]);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 佣金排行
|
||||
* @param string $time
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function brokerageRankList(string $time = 'week')
|
||||
{
|
||||
$where = ['pm' => 1];
|
||||
if ($time) {
|
||||
$where['time'] = $time;
|
||||
}
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$list = $this->dao->brokerageRankList($where, $page, $limit);
|
||||
foreach ($list as $key => &$item) {
|
||||
if (!isset($item['user']) || !$item['user'] || $item['brokerage_price'] <= 0) {
|
||||
unset($list[$key]);
|
||||
continue;
|
||||
}
|
||||
$item['nickname'] = $item['user']['nickname'] ?? '';
|
||||
$item['avatar'] = $item['user']['avatar'] ?? '';
|
||||
if ($item['brokerage_price'] == '0.00' || $item['brokerage_price'] == 0 || !$item['brokerage_price']) {
|
||||
unset($list[$key]);
|
||||
}
|
||||
unset($item['user']);
|
||||
}
|
||||
return array_merge($list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户排名
|
||||
* @param int $uid
|
||||
* @param string $time
|
||||
* @return false|int|string
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getUserBrokerageRank(int $uid, string $time = 'week')
|
||||
{
|
||||
$where = ['pm' => 1];
|
||||
if ($time) {
|
||||
$where['time'] = $time;
|
||||
}
|
||||
$list = $this->dao->brokerageRankList($where);
|
||||
foreach ($list as $key => &$item) {
|
||||
if (!isset($item['user']) || !$item['user'] || $item['brokerage_price'] <= 0) {
|
||||
unset($list[$key]);
|
||||
}
|
||||
}
|
||||
$position_tmp_one = array_column($list, 'uid');
|
||||
$position_tmp_two = array_column($list, 'brokerage_price', 'uid');
|
||||
if (!in_array($uid, $position_tmp_one)) {
|
||||
$position = 0;
|
||||
} else {
|
||||
if ($position_tmp_two[$uid] == 0.00) {
|
||||
$position = 0;
|
||||
} else {
|
||||
$position = array_search($uid, $position_tmp_one) + 1;
|
||||
}
|
||||
}
|
||||
return $position;
|
||||
}
|
||||
|
||||
/**
|
||||
* 推广数据 昨天的佣金 累计提现金额 当前佣金
|
||||
* @param int $uid
|
||||
* @return mixed
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function commission(int $uid)
|
||||
{
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
if (!$userServices->getUserInfo($uid)) {
|
||||
throw new ApiException('数据不存在');
|
||||
}
|
||||
/** @var UserExtractServices $userExtract */
|
||||
$userExtract = app()->make(UserExtractServices::class);
|
||||
$data = [];
|
||||
$data['uid'] = $uid;
|
||||
$data['pm'] = 1;
|
||||
$data['commissionSum'] = $this->getUsersBokerageSum($data);
|
||||
$extract_fail = $this->dao->sum(['uid' => $uid, 'pm' => 1, 'type' => 'extract_fail'], 'number');
|
||||
$data['commissionSum'] = bcadd($data['commissionSum'], $extract_fail, 2);
|
||||
$data['pm'] = 0;
|
||||
$data['commissionRefund'] = $this->getUsersBokerageSum($data);
|
||||
$data['commissionCount'] = $data['commissionSum'] > $data['commissionRefund'] ? bcsub((string)$data['commissionSum'], (string)$data['commissionRefund'], 2) : 0.00;
|
||||
$data['lastDayCount'] = $this->getUsersBokerageSum($data, 'yesterday');//昨天的佣金
|
||||
$data['extractCount'] = $userExtract->getUserExtract($uid);//累计提现金额
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算佣金
|
||||
* @param array $where
|
||||
* @param int $time
|
||||
* @return mixed
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function getUsersBokerageSum(array $where, $time = 0)
|
||||
{
|
||||
$where_data = [
|
||||
'status' => 1,
|
||||
'pm' => $where['pm'] ?? '',
|
||||
'uid' => $where['uid'] ?? '',
|
||||
'time' => $where['time'] ?? 0
|
||||
];
|
||||
if ($time) $where_data['time'] = $time;
|
||||
return $this->dao->getBrokerageSumColumn($where_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 佣金明细
|
||||
* @param $uid
|
||||
* @param $type
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getBrokerageList($uid, $type)
|
||||
{
|
||||
$where = [];
|
||||
$where['uid'] = $uid;
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
if ($type == 4) {
|
||||
$where['type'] = ['extract', 'extract_money'];
|
||||
}
|
||||
if ($type == 3) {
|
||||
$where['not_type'] = ['extract_fail'];
|
||||
}
|
||||
/** @var UserExtractServices $userExtractService */
|
||||
$userExtractService = app()->make(UserExtractServices::class);
|
||||
$userExtract = $userExtractService->getColumn(['uid' => $uid], 'fail_msg,extract_type,state,wechat_order_id,status', 'id');
|
||||
$list = $this->dao->getList($where, '*', $page, $limit);
|
||||
$count = $this->dao->count($where);
|
||||
$times = [];
|
||||
if ($list) {
|
||||
foreach ($list as &$item) {
|
||||
$item['time'] = $item['time_key'] = $item['add_time'] ? date('Y-m', (int)$item['add_time']) : '';
|
||||
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', (int)$item['add_time']) : '';
|
||||
$item['fail_msg'] = $item['type'] == 'extract_fail' ? $userExtract[$item['link_id']]['fail_msg'] : '';
|
||||
if ($type == 4) {
|
||||
$extract_type = $userExtract[$item['link_id']]['extract_type'] ?? '';
|
||||
if ($extract_type == 'alipay') {
|
||||
$item['extract_type'] = '支付宝';
|
||||
} elseif ($extract_type == 'weixin') {
|
||||
$item['extract_type'] = '微信';
|
||||
} elseif ($extract_type == 'bank') {
|
||||
$item['extract_type'] = '银行卡';
|
||||
} else {
|
||||
$item['extract_type'] = '余额';
|
||||
}
|
||||
$item['state'] = $userExtract[$item['link_id']]['state'] ?? '';
|
||||
$item['fail_msg'] = $userExtract[$item['link_id']]['fail_msg'] ?? '';
|
||||
$item['extract_status'] = $userExtract[$item['link_id']]['status'] ?? '';
|
||||
$item['wechat_order_id'] = $userExtract[$item['link_id']]['wechat_order_id'] ?? '';
|
||||
} else {
|
||||
$item['fail_msg'] = $userExtract[$item['link_id']]['fail_msg'] ?? '';
|
||||
$item['extract_status'] = $userExtract[$item['link_id']]['status'] ?? '';
|
||||
$item['extract_type'] = '';
|
||||
}
|
||||
$item['is_frozen'] = $item['frozen_time'] > time() ? 1 : 0;
|
||||
$item['frozen_time'] = $item['frozen_time'] ? date('Y-m-d H:i:s', (int)$item['frozen_time']) : '';
|
||||
}
|
||||
$times = array_merge(array_unique(array_column($list, 'time_key')));
|
||||
}
|
||||
return ['list' => $list, 'time' => $times, 'count' => $count];
|
||||
}
|
||||
|
||||
/**
|
||||
* 前端佣金排行页面数据
|
||||
* @param int $uid
|
||||
* @param $type
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function brokerageRank(int $uid, $type)
|
||||
{
|
||||
/** @var UserServices $userService */
|
||||
$userService = app()->make(UserServices::class);
|
||||
if (!$userService->getUserInfo($uid)) {
|
||||
throw new ApiException('数据不存在');
|
||||
}
|
||||
return [
|
||||
'rank' => $this->brokerageRankList($type),
|
||||
'position' => $this->getUserBrokerageRank($uid, $type)
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -24,7 +24,7 @@ class UserCancelServices extends BaseService
|
||||
protected $status = ['待审核', '已通过', '已拒绝'];
|
||||
|
||||
/**
|
||||
* UserExtractServices constructor.
|
||||
* UserCancelServices constructor.
|
||||
* @param UserCancelDao $dao
|
||||
*/
|
||||
public function __construct(UserCancelDao $dao)
|
||||
|
||||
@ -1,762 +0,0 @@
|
||||
<?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\user;
|
||||
|
||||
use app\dao\user\UserExtractDao;
|
||||
use crmeb\basic\BaseService;
|
||||
use app\services\order\OrderCenterServices;
|
||||
use app\services\pay\PayRecordServices;
|
||||
use app\services\pay\PayServices;
|
||||
use app\services\pay\PayTransferNotifyServices;
|
||||
use app\services\statistic\CapitalFlowServices;
|
||||
use app\services\system\admin\SystemAdminServices;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use crmeb\exceptions\ApiException;
|
||||
use crmeb\exceptions\PayException;
|
||||
use crmeb\services\AliPayService;
|
||||
use crmeb\utils\FormBuilder as Form;
|
||||
use crmeb\services\pay\Pay;
|
||||
use crmeb\services\pay\PayChannelConfig;
|
||||
use crmeb\services\workerman\ChannelService;
|
||||
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Route as Url;
|
||||
|
||||
/**
|
||||
* 用户提现服务
|
||||
* Class UserExtractServices
|
||||
* @package app\services\user
|
||||
*/
|
||||
class UserExtractServices extends BaseService
|
||||
{
|
||||
/**
|
||||
* @var UserExtractDao
|
||||
*/
|
||||
protected $dao;
|
||||
|
||||
/**
|
||||
* UserExtractServices constructor.
|
||||
* @param UserExtractDao $dao
|
||||
*/
|
||||
public function __construct(UserExtractDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过事件代理查询用户 openid,避免 app 层直接引用 modules
|
||||
* @param int $uid 用户ID
|
||||
* @param string $type 用户类型(wechat/routine/app)
|
||||
* @return string openid,未取到返回空字符串
|
||||
*/
|
||||
private function proxyUidToOpenid(int $uid, string $type): string
|
||||
{
|
||||
$results = event('ChannelServiceProxy', ['endpoint' => 'wechat_mp', 'service' => 'WechatUserServices', 'action' => 'uidToOpenid', 'args' => [$uid, $type]]);
|
||||
return proxy_event_result($results, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一条提现记录
|
||||
* @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 int $uid
|
||||
* @return float
|
||||
*/
|
||||
public function getUserExtract(int $uid)
|
||||
{
|
||||
return $this->dao->getWhereSum(['uid' => $uid, 'status' => 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某些用户的提现总数列表
|
||||
* @param array $uids
|
||||
*/
|
||||
public function getUsersSumList(array $uids)
|
||||
{
|
||||
return $this->dao->getWhereSumList(['uid' => $uids, 'status' => 1]);
|
||||
}
|
||||
|
||||
public function getCount(array $where = [])
|
||||
{
|
||||
return $this->dao->getCount($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提现列表
|
||||
* @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 getUserExtractList(array $where, string $field = '*')
|
||||
{
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$list = $this->dao->getExtractList($where, $field, $page, $limit);
|
||||
foreach ($list as &$item) {
|
||||
$item['nickname'] = $item['user']['nickname'] ?? '';
|
||||
$item['receive_price'] = bcsub((string)$item['extract_price'], (string)$item['extract_fee'], 2);
|
||||
}
|
||||
$count = $this->dao->count($where);
|
||||
return compact('list', 'count');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提现总数
|
||||
* @param array $where
|
||||
*/
|
||||
public function getExtractSum(array $where)
|
||||
{
|
||||
return $this->dao->getExtractMoneyByWhere($where, 'extract_price');
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝提现申请
|
||||
* @param $id
|
||||
* @param $fail_msg
|
||||
* @return bool
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function changeFail(int $id, $userExtract, $message)
|
||||
{
|
||||
$fail_time = time();
|
||||
$extract_number = $userExtract['extract_price'];
|
||||
$mark = '提现失败,退回佣金' . $extract_number . '元';
|
||||
$uid = $userExtract['uid'];
|
||||
$status = -1;
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$user = $userServices->getUserInfo($uid);
|
||||
$changed = $this->transaction(function () use ($user, $uid, $id, $extract_number, $message, $userServices, $status, $fail_time) {
|
||||
$locked = $this->dao->getDb()->where('id', $id)->lock(true)->find();
|
||||
if (!$locked) throw new AdminException('提现记录不存在');
|
||||
if ((int)$locked['status'] === -1) return false;
|
||||
// 原转账合同先于远端调用落库,响应未知也不能按未转账返佣。
|
||||
$transfer = ($locked['extract_type'] ?? '') === 'weixin' && !empty($locked['wechat_order_id'])
|
||||
? app()->make(PayRecordServices::class)->find($locked['wechat_order_id'], true) : [];
|
||||
$confirmedFailure = in_array($locked['state'] ?? '', ['FAIL', 'CANCELLED'], true);
|
||||
if ((int)$locked['status'] === 1 || (!$confirmedFailure && (!empty($locked['state']) || $transfer))) {
|
||||
throw new AdminException('转账已受理或到账,请先在微信确认撤销/失败结果');
|
||||
}
|
||||
//增加佣金记录(事务内自动增加余额并记录流水)
|
||||
/** @var UserBrokerageServices $userBrokerageServices */
|
||||
$userBrokerageServices = app()->make(UserBrokerageServices::class);
|
||||
$userBrokerageServices->brokerageIncome('extract_fail', (int)$locked['uid'], $locked['extract_price'], $id);
|
||||
if (!$this->dao->update($id, ['fail_time' => $fail_time, 'fail_msg' => $message, 'status' => $status])) {
|
||||
throw new AdminException('修改失败');
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!$changed) return true;
|
||||
|
||||
//触发余额变动事件(消息发送已移至 UserBalanceChangeListener)
|
||||
event('UserBalanceChange', [
|
||||
$uid,
|
||||
'sub',
|
||||
$extract_number,
|
||||
$user['now_money'] ?? 0,
|
||||
'提现失败,余额已返还'
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过提现申请
|
||||
* @param int $id
|
||||
* @param $userExtract
|
||||
* @return bool
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function changeSuccess(int $id, $userExtract)
|
||||
{
|
||||
$userExtract = $this->dao->get($id);
|
||||
if (!$userExtract || (int)$userExtract['status'] !== 0) throw new AdminException('提现状态已变化,不能重复审核');
|
||||
if ($userExtract['extract_type'] === 'weixin' && !empty($userExtract['wechat_order_id'])
|
||||
&& app()->make(PayRecordServices::class)->find($userExtract['wechat_order_id'])) {
|
||||
if (!app()->make(PayTransferNotifyServices::class)->sync($userExtract['wechat_order_id'])) {
|
||||
throw new PayException('转账结果尚未确认,请稍后重试');
|
||||
}
|
||||
return 'v3_extract';
|
||||
}
|
||||
if (!empty($userExtract['state'])) return 'v3_extract';
|
||||
$extractNumber = bcsub($userExtract['extract_price'], $userExtract['extract_fee'], 2);
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$userType = $userServices->value(['uid' => $userExtract['uid']], 'user_type');
|
||||
$nickname = $userServices->value(['uid' => $userExtract['uid']], 'nickname');
|
||||
$phone = $userServices->value(['uid' => $userExtract['uid']], 'phone');
|
||||
$order_id = $userExtract['wechat_order_id'] !== '' ? $userExtract['wechat_order_id'] : app()->make(OrderCenterServices::class)->getNewOrderId('tx');
|
||||
$insertData = ['wechat_order_id' => $order_id, 'nickname' => $nickname, 'phone' => $phone];
|
||||
|
||||
//微信自动提现到零钱
|
||||
if (sys_config('weixin_extract_type', 0) && $userExtract['extract_type'] == 'weixin') {
|
||||
$type = '';
|
||||
$openid = $this->proxyUidToOpenid((int)$userExtract['uid'], (string)$userExtract['channel_type']);
|
||||
if ($userExtract['channel_type'] == 'wechat') {
|
||||
$type = 'JSAPI';
|
||||
} elseif ($userExtract['channel_type'] == 'routine') {
|
||||
$type = 'mini';
|
||||
} elseif ($userExtract['channel_type'] == 'app') {
|
||||
$type = 'APP';
|
||||
}
|
||||
if (!$openid) {
|
||||
$openid = $this->proxyUidToOpenid((int)$userExtract['uid'], 'wechat');
|
||||
$type = 'JSAPI';
|
||||
}
|
||||
if (!$openid) {
|
||||
$openid = $this->proxyUidToOpenid((int)$userExtract['uid'], 'routine');
|
||||
$type = 'mini';
|
||||
}
|
||||
if (!$openid) {
|
||||
$openid = $this->proxyUidToOpenid((int)$userExtract['uid'], 'app');
|
||||
$type = 'APP';
|
||||
}
|
||||
if (!$openid) {
|
||||
throw new ValidateException('该用户暂不支持自动转账到零钱,请手动转账');
|
||||
}
|
||||
//v3商家转账
|
||||
$payServices = app()->make(PayServices::class);
|
||||
if ($payServices->hasCapability('wechat', 'merchant_pay_new')) {
|
||||
$pay = app()->make(Pay::class, ['v3_wechat_pay']);
|
||||
// 支付配置统一存储于 eb_pay_channel.config_fields,不再读取 system_config
|
||||
$v3PayConfig = PayChannelConfig::get('weixin_v3');
|
||||
// 确认收款使用单笔商家转账,与平台公钥或证书验签模式无关。
|
||||
$context = ['channel_key' => 'weixin_v3', 'platform' => $type === 'mini' ? 'wechat_mini' : ($type === 'APP' ? 'app' : 'wechat_mp'),
|
||||
'mode' => 'transfer', 'mch_id' => trim((string)($v3PayConfig['pay_weixin_mchid'] ?? '')),
|
||||
'sub_mch_id' => '', 'app_id' => trim((string)($v3PayConfig[$type === 'mini' ? 'routine_appId' : ($type === 'APP' ? 'wechat_app_appid' : 'wechat_appid')] ?? '')),
|
||||
'openid' => $openid, 'payer_uid' => (int)$userExtract['uid']];
|
||||
app()->make(PayRecordServices::class)->prepareTransfer($id, $order_id, $extractNumber, $context);
|
||||
$res = $pay->merchantPayNew(
|
||||
$type,
|
||||
$order_id,
|
||||
$v3PayConfig['v3_transfer_scene_id'] ?? '1000',
|
||||
$openid,
|
||||
$userExtract['real_name'],
|
||||
bcmul($extractNumber, '100', 0),
|
||||
'佣金提现到零钱',
|
||||
sys_config('site_url') . '/api/transfer/notify/' . $type,
|
||||
'劳务报酬',
|
||||
[
|
||||
[
|
||||
'info_type' => '岗位类型',
|
||||
'info_content' => '推广员奖励'
|
||||
],
|
||||
[
|
||||
'info_type' => '报酬说明',
|
||||
'info_content' => '推广订单奖励提现'
|
||||
],
|
||||
]
|
||||
);
|
||||
$this->saveTransferAcceptance($id, $order_id, $res);
|
||||
// 只由已验签查单/通知推进终态并记到账流水,受理和待确认均不算到账。
|
||||
if (in_array($res['state'] ?? '', ['SUCCESS', 'FAIL', 'CANCELLED'], true)) {
|
||||
app()->make(PayTransferNotifyServices::class)->sync($order_id);
|
||||
}
|
||||
return 'v3_extract';
|
||||
} else {
|
||||
$context = (new Pay('wechat_pay'))->getTransferContext([
|
||||
'platform' => $type === 'mini' ? 'wechat_mini' : ($type === 'APP' ? 'app' : 'wechat_mp'),
|
||||
'openid' => $openid, 'payer_uid' => (int)$userExtract['uid'],
|
||||
]);
|
||||
app()->make(PayRecordServices::class)->prepareTransfer($id, $order_id, $extractNumber, $context);
|
||||
// 微信提现(v2企业付款到零钱):经事件代理执行,避免 app 层直调 driver 层
|
||||
$res = proxy_event_result(event('WechatMerchantPay', [
|
||||
'openid' => $openid,
|
||||
'order_id' => $order_id,
|
||||
'amount' => (string)$extractNumber,
|
||||
'desc' => '提现佣金到零钱',
|
||||
'type' => $type,
|
||||
]));
|
||||
if (!$res) throw new ApiException('企业付款尚未确认,请查询原单号后重试');
|
||||
$this->saveTransferAcceptance($id, $order_id, ['state' => 'PROCESSING']);
|
||||
app()->make(PayTransferNotifyServices::class)->sync($order_id);
|
||||
return 'v3_extract';
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
throw new ApiException('企业付款到零钱失败,请稍后再试');
|
||||
}
|
||||
}
|
||||
if (sys_config('alipay_extract_type', 0) && $userExtract['extract_type'] == 'alipay') {
|
||||
// 构造支付宝提现参数
|
||||
// 支付配置统一存储于 eb_pay_channel.config_fields,不再读取 system_config
|
||||
$alipaySignType = (int)(PayChannelConfig::get('alipay')['alipay_sign_type'] ?? 0);
|
||||
if ($alipaySignType == 0) {
|
||||
$bizParams = [
|
||||
'payee_type' => 'ALIPAY_LOGONID', // 收款方账户类型,ALIPAY_LOGONID-支付宝登录账号
|
||||
'payee_account' => $userExtract['real_name'], // 收款方账户,实名认证的支付宝账号
|
||||
'amount' => $extractNumber, // 提现金额
|
||||
'payer_show_name' => sys_config('site_name'), // 付款方姓名/个人名称
|
||||
'payee_real_name' => $userExtract['user_name'], // 收款方真实姓名/个人名称
|
||||
'remark' => '提现 ¥' . $extractNumber . ' 到支付宝', // 业务备注
|
||||
];
|
||||
} else {
|
||||
$bizParams = [
|
||||
'out_biz_no' => $order_id, // 商户订单号
|
||||
'trans_amount' => $extractNumber,
|
||||
'biz_scene' => 'DIRECT_TRANSFER',
|
||||
'product_code' => 'TRANS_ACCOUNT_NO_PWD',
|
||||
'order_title' => sys_config('site_name') . '提现',
|
||||
'payee_info' => [
|
||||
'identity' => $userExtract['alipay_code'],
|
||||
'identity_type' => 'ALIPAY_LOGON_ID',
|
||||
'name' => $userExtract['user_name'],
|
||||
],
|
||||
'remark' => '提现 ¥' . $extractNumber . ' 到支付宝', // 业务备注
|
||||
];
|
||||
}
|
||||
// 调用支付宝服务发起支付宝提现请求
|
||||
$res = AliPayService::instance()->merchantPay($bizParams, $alipaySignType);
|
||||
// 如果支付宝提现请求失败,则抛出异常
|
||||
if (!$res) {
|
||||
throw new ApiException('提现失败,请稍查看日志!');
|
||||
}
|
||||
}
|
||||
|
||||
/** @var UserServices $userService */
|
||||
$userService = app()->make(UserServices::class);
|
||||
$user = $userService->getUserInfo($userExtract['uid']);
|
||||
$insertData['nickname'] = $user['nickname'];
|
||||
$insertData['phone'] = $user['phone'];
|
||||
|
||||
/** @var CapitalFlowServices $capitalFlowServices */
|
||||
$capitalFlowServices = app()->make(CapitalFlowServices::class);
|
||||
$capitalFlowServices->setFlow([
|
||||
'order_id' => $order_id,
|
||||
'uid' => $userExtract['uid'],
|
||||
'price' => bcmul('-1', $extractNumber, 2),
|
||||
'pay_type' => $userExtract['extract_type'],
|
||||
'nickname' => $insertData['nickname'],
|
||||
'phone' => $insertData['phone']
|
||||
], 'extract');
|
||||
|
||||
if (!$this->dao->update($id, ['status' => 1])) {
|
||||
throw new AdminException('修改失败');
|
||||
}
|
||||
//触发提现成功事件(消息发送已移至 UserExtractListener)
|
||||
event('UserExtract', [
|
||||
$userExtract['uid'],
|
||||
$userExtract,
|
||||
$extractNumber,
|
||||
0,
|
||||
strtolower($userType),
|
||||
$nickname,
|
||||
$user['phone'] ?? ''
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅保存转账受理信息,不能提前写终态或覆盖先到的通知。
|
||||
* @param int $id 提现ID
|
||||
* @param string $orderNo 固定商户单号
|
||||
* @param array $response 已验签的受理响应
|
||||
* @return void
|
||||
*/
|
||||
public function saveTransferAcceptance(int $id, string $orderNo, array $response): void
|
||||
{
|
||||
if (($response['out_bill_no'] ?? $orderNo) !== $orderNo) throw new AdminException('转账受理单号不一致');
|
||||
$this->transaction(function () use ($id, $orderNo, $response) {
|
||||
$source = $this->dao->getDb()->where('id', $id)->lock(true)->find();
|
||||
if (!$source || $source['wechat_order_id'] !== $orderNo) throw new AdminException('转账原单号已变化');
|
||||
if ((int)$source['status'] !== 0 || in_array($source['state'] ?? '', ['SUCCESS', 'FAIL', 'CANCELLED'], true)) return;
|
||||
$state = (string)($response['state'] ?? 'PROCESSING');
|
||||
$data = ['out_bill_no' => $orderNo, 'package_info' => $response['package_info'] ?? '',
|
||||
'state' => in_array($state, ['SUCCESS', 'FAIL', 'CANCELLED'], true) ? 'PROCESSING' : $state];
|
||||
if (!empty($response['transfer_bill_no'])) $data['transfer_bill_no'] = $response['transfer_bill_no'];
|
||||
$source->save($data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的商家转账详情,查单时由可信终态统一完成到账或返佣。
|
||||
* @param int $uid 用户ID
|
||||
* @param string $orderNo 原商户转账单号
|
||||
* @param bool $sync 是否向微信核验并同步状态
|
||||
* @return array 前端收款所需字段
|
||||
*/
|
||||
public function transferInfo(int $uid, string $orderNo, bool $sync = false): array
|
||||
{
|
||||
$info = $this->dao->getExtractByOrderId($uid, $orderNo);
|
||||
if (!$info || $info['extract_type'] !== 'weixin') throw new ApiException('提现记录不存在');
|
||||
$record = app()->make(PayRecordServices::class)->find($orderNo);
|
||||
if ($record && (($record['channel_key'] ?? '') !== 'weixin_v3' || ($record['context']['mode'] ?? '') !== 'transfer')) {
|
||||
throw new ApiException('该提现不支持确认收款');
|
||||
}
|
||||
if ($sync && (int)$info['status'] === 0 && $record) {
|
||||
if (!app()->make(PayTransferNotifyServices::class)->sync($orderNo)) {
|
||||
throw new PayException('转账状态核验失败,请稍后重试');
|
||||
}
|
||||
$info = $this->dao->getExtractByOrderId($uid, $orderNo);
|
||||
}
|
||||
$context = $record['context'] ?? [];
|
||||
$canReceive = (int)$info['status'] === 0 && ($info['state'] ?? '') === 'WAIT_USER_CONFIRM'
|
||||
&& !empty($info['package_info']) && !empty($context['mch_id']) && !empty($context['app_id']);
|
||||
return [
|
||||
'id' => (int)$info['id'],
|
||||
'wechat_order_id' => $orderNo,
|
||||
'out_bill_no' => $info['out_bill_no'] ?? '',
|
||||
'transfer_bill_no' => $info['transfer_bill_no'] ?? '',
|
||||
'status' => (int)$info['status'],
|
||||
'state' => $info['state'] ?? '',
|
||||
'fail_reason' => $info['fail_reason'] ?? '',
|
||||
'fail_msg' => $info['fail_msg'] ?? '',
|
||||
'true_extract_price' => bcsub((string)$info['extract_price'], (string)$info['extract_fee'], 2),
|
||||
'package_info' => $canReceive ? $info['package_info'] : '',
|
||||
'mchid' => $context['mch_id'] ?? '',
|
||||
'wechat_appid' => $context['app_id'] ?? '',
|
||||
'can_receive' => $canReceive,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示资源列表
|
||||
* @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)
|
||||
{
|
||||
$list = $this->getUserExtractList($where);
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
//待提现金额
|
||||
$where['status'] = 0;
|
||||
$extract_statistics['price'] = $this->getExtractSum($where);
|
||||
//已提现金额
|
||||
$where['status'] = 1;
|
||||
$extract_statistics['priced'] = $this->getExtractSum($where);
|
||||
/** @var UserBrokerageServices $userBrokerageServices */
|
||||
$userBrokerageServices = app()->make(UserBrokerageServices::class);
|
||||
$where['pm'] = 1;
|
||||
$brokerage_count = $userBrokerageServices->getUsersBokerageSum($where);
|
||||
$refund_brokerage = $userBrokerageServices->sum(['type' => 'refund'], 'number');
|
||||
$extract_statistics['brokerage_count'] = bcsub((string)$brokerage_count, (string)$refund_brokerage, 2);
|
||||
//未提现金额
|
||||
$extract_statistics['brokerage_not'] = $extract_statistics['brokerage_count'] > $extract_statistics['priced'] ? bcsub((string)$extract_statistics['brokerage_count'], (string)$extract_statistics['priced'], 2) : 0.00;
|
||||
return compact('extract_statistics', 'list');
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示编辑资源表单页.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function edit(int $id)
|
||||
{
|
||||
$UserExtract = $this->getExtract($id);
|
||||
if (!$UserExtract) {
|
||||
throw new AdminException('数据不存在');
|
||||
}
|
||||
$f = array();
|
||||
$f[] = Form::input('real_name', '姓名', $UserExtract['real_name']);
|
||||
$f[] = Form::number('extract_price', '提现金额', (float)$UserExtract['extract_price'])->precision(2)->disabled(true);
|
||||
if ($UserExtract['extract_type'] == 'alipay') {
|
||||
$f[] = Form::input('alipay_code', '支付宝账号', $UserExtract['alipay_code']);
|
||||
} else if ($UserExtract['extract_type'] == 'weixin') {
|
||||
$f[] = Form::input('wechat', '微信号', $UserExtract['wechat']);
|
||||
} else if ($UserExtract['extract_type'] == 'balance') {
|
||||
} else {
|
||||
$f[] = Form::input('bank_code', '银行卡号', $UserExtract['bank_code']);
|
||||
$f[] = Form::input('bank_address', '开户行', $UserExtract['bank_address']);
|
||||
}
|
||||
$f[] = Form::input('mark', '备注', $UserExtract['mark'])->type('textarea');
|
||||
return create_form('编辑', $f, Url::buildUrl('/finance/extract/' . $id), 'PUT');
|
||||
}
|
||||
|
||||
public function update(int $id, array $data)
|
||||
{
|
||||
if (array_key_exists('extract_price', $data)) {
|
||||
$source = $this->dao->get($id);
|
||||
if ($source && $source['extract_type'] === 'weixin'
|
||||
&& bccomp((string)$source['extract_price'], (string)$data['extract_price'], 2) !== 0) {
|
||||
throw new AdminException('已扣除的提现佣金金额不能修改');
|
||||
}
|
||||
}
|
||||
if (!$this->dao->update($id, $data))
|
||||
throw new AdminException('修改失败');
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝
|
||||
* @param $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function refuse(int $id, string $message)
|
||||
{
|
||||
$extract = $this->getExtract($id);
|
||||
if (!$extract) {
|
||||
throw new AdminException('数据不存在');
|
||||
}
|
||||
if ($extract->status == 1) {
|
||||
throw new AdminException('已经提现');
|
||||
}
|
||||
if ($extract->status == -1) {
|
||||
throw new AdminException('您的提现申请已被拒绝');
|
||||
}
|
||||
$res = $this->changeFail($id, $extract, $message);
|
||||
if ($res) {
|
||||
return true;
|
||||
} else {
|
||||
throw new AdminException('操作失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过
|
||||
* @param int $id
|
||||
* @return mixed
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function adopt(int $id)
|
||||
{
|
||||
$extract = $this->getExtract($id);
|
||||
if (!$extract) {
|
||||
throw new AdminException('数据不存在');
|
||||
}
|
||||
if ($extract->status == 1) {
|
||||
throw new AdminException('已经提现');
|
||||
}
|
||||
if ($extract->status == -1) {
|
||||
throw new AdminException('您的提现申请已被拒绝');
|
||||
}
|
||||
$res = $this->changeSuccess($id, $extract);
|
||||
if ($res) {
|
||||
return $res;
|
||||
} else {
|
||||
throw new AdminException('操作失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**待提现的数量
|
||||
* @return int
|
||||
*/
|
||||
public function userExtractCount()
|
||||
{
|
||||
return $this->dao->count(['status' => 0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 银行卡提现
|
||||
* @param int $uid
|
||||
* @return mixed
|
||||
*/
|
||||
public function bank(int $uid)
|
||||
{
|
||||
/** @var UserServices $userService */
|
||||
$userService = app()->make(UserServices::class);
|
||||
$user = $userService->getUserInfo($uid, 'brokerage_price,uid');
|
||||
if (!$user) {
|
||||
throw new ApiException('数据不存在');
|
||||
}
|
||||
$profile = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices',
|
||||
'action' => 'profile',
|
||||
'args' => [$uid, $user->toArray()],
|
||||
]);
|
||||
if (is_array($profile) && is_array($profile[0] ?? null)) {
|
||||
$user['brokerage_price'] = $profile[0]['brokerage_price'];
|
||||
}
|
||||
/** @var UserBrokerageServices $services */
|
||||
$services = app()->make(UserBrokerageServices::class);
|
||||
$data['broken_commission'] = $services->getUserFrozenPrice($uid);
|
||||
if ($data['broken_commission'] < 0)
|
||||
$data['broken_commission'] = '0';
|
||||
$data['brokerage_price'] = $user['brokerage_price'];
|
||||
//可提现佣金
|
||||
$data['commissionCount'] = bcsub((string)$data['brokerage_price'], (string)$data['broken_commission'], 2);
|
||||
$extractBank = sys_config('user_extract_bank') ?? []; //提现银行
|
||||
$extractBank = str_replace("\r\n", "\n", $extractBank);//防止不兼容
|
||||
$data['extractBank'] = explode("\n", is_array($extractBank) ? ($extractBank[0] ?? $extractBank) : $extractBank);
|
||||
$data['minPrice'] = sys_config('user_extract_min_price');//提现最低金额
|
||||
$data['weixinExtractType'] = (int)sys_config('weixin_extract_type', 0);//微信到账方式
|
||||
$data['alipayExtractType'] = (int)sys_config('alipay_extract_type', 0);//支付宝到账方式
|
||||
$data['withdrawal_fee'] = sys_config('withdrawal_fee', 0);//提现手续费
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提现申请
|
||||
* @param int $uid
|
||||
* @param array $data
|
||||
*/
|
||||
public function cash(int $uid, array $data)
|
||||
{
|
||||
/** @var UserServices $userService */
|
||||
$userService = app()->make(UserServices::class);
|
||||
$user = $userService->getUserInfo($uid);
|
||||
if (!$user) {
|
||||
throw new ApiException('数据不存在');
|
||||
}
|
||||
$profile = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices',
|
||||
'action' => 'profile',
|
||||
'args' => [$uid, $user->toArray()],
|
||||
]);
|
||||
if (is_array($profile) && is_array($profile[0] ?? null)) {
|
||||
$user['brokerage_price'] = $profile[0]['brokerage_price'];
|
||||
}
|
||||
|
||||
if ($data['extract_type'] == 'weixin' && !sys_config('weixin_extract_type', 0) && !$data['weixin']) {
|
||||
throw new ApiException('请输入微信账号');
|
||||
}
|
||||
|
||||
if ($data['extract_type'] == 'weixin' && bccomp($data['money'], '0.1', 2) < 0) {
|
||||
throw new ApiException('微信提现最低不能小于0.1元');
|
||||
}
|
||||
|
||||
//通过事件代理查询用户 openid,避免 app 层直接引用 modules
|
||||
$openid = $this->proxyUidToOpenid($uid, 'wechat');
|
||||
if (!$openid) {
|
||||
$openid = $this->proxyUidToOpenid($uid, 'routine');
|
||||
}
|
||||
|
||||
if ($data['extract_type'] == 'weixin' && sys_config('weixin_extract_type', 0) && !$openid) {
|
||||
throw new ApiException('请先关注公众号');
|
||||
}
|
||||
|
||||
/** @var UserBrokerageServices $services */
|
||||
$services = app()->make(UserBrokerageServices::class);
|
||||
$data['broken_commission'] = $services->getUserFrozenPrice($uid);
|
||||
if ($data['broken_commission'] < 0)
|
||||
$data['broken_commission'] = 0;
|
||||
$data['brokerage_price'] = $user['brokerage_price'];
|
||||
//可提现佣金
|
||||
$commissionCount = bcsub((string)$data['brokerage_price'], (string)$data['broken_commission'], 2);
|
||||
if ($data['money'] > $commissionCount) {
|
||||
throw new ApiException('可提现佣金不足');
|
||||
}
|
||||
|
||||
$extractPrice = $user['brokerage_price'];
|
||||
$userExtractMinPrice = sys_config('user_extract_min_price');
|
||||
if ($data['money'] < $userExtractMinPrice) {
|
||||
throw new ApiException('提现金额不能小于{:money}元', ['money' => $userExtractMinPrice]);
|
||||
}
|
||||
if ($extractPrice < 0) {
|
||||
throw new ApiException('提现佣金不足{:money}元', ['money' => $data['money']]);
|
||||
}
|
||||
if ($data['money'] > $extractPrice) {
|
||||
throw new ApiException('提现佣金不足{:money}元', ['money' => $data['money']]);
|
||||
}
|
||||
if ($data['money'] <= 0) {
|
||||
throw new ApiException('提现佣金大于0');
|
||||
}
|
||||
$data['extract_price'] = bcmul($data['money'], '1', 2);
|
||||
$insertData = [
|
||||
'wechat_order_id' => app()->make(OrderCenterServices::class)->getNewOrderId('tx'),
|
||||
'uid' => $user['uid'],
|
||||
'extract_type' => $data['extract_type'],
|
||||
'extract_price' => $data['extract_price'],
|
||||
'extract_fee' => bcmul((string)$data['extract_price'], bcdiv((string)sys_config('withdrawal_fee', '0'), '100', 4), 2),
|
||||
'add_time' => time(),
|
||||
'balance' => $user['brokerage_price'],
|
||||
'status' => 0,
|
||||
'channel_type' => $data['channel_type']
|
||||
];
|
||||
if (isset($data['name']) && strlen(trim($data['name']))) $insertData['real_name'] = $data['name'];
|
||||
else $insertData['real_name'] = $user['nickname'];
|
||||
if (isset($data['cardnum'])) $insertData['bank_code'] = $data['cardnum'];
|
||||
else $insertData['bank_code'] = '';
|
||||
if (isset($data['bankname'])) $insertData['bank_address'] = $data['bankname'];
|
||||
else $insertData['bank_address'] = '';
|
||||
if (isset($data['weixin'])) $insertData['wechat'] = $data['weixin'];
|
||||
else $insertData['wechat'] = $user['nickname'];
|
||||
$mark = '';
|
||||
$feeMark = sys_config('withdrawal_fee', 0) == 0 ? '' : ',手续费' . $insertData['extract_fee'] . '元';
|
||||
if ($data['extract_type'] == 'alipay') {
|
||||
$insertData['alipay_code'] = $data['alipay_code'];
|
||||
$insertData['qrcode_url'] = $data['qrcode_url'];
|
||||
$insertData['user_name'] = $data['user_name'];
|
||||
$insertData['real_name'] = $data['user_name'];
|
||||
$mark = '使用支付宝提现' . $insertData['extract_price'] . '元' . $feeMark;
|
||||
} else if ($data['extract_type'] == 'bank') {
|
||||
$mark = '使用银联卡' . $insertData['bank_code'] . '提现' . $insertData['extract_price'] . '元' . $feeMark;
|
||||
} else if ($data['extract_type'] == 'weixin') {
|
||||
$insertData['user_name'] = $data['user_name'];
|
||||
$insertData['real_name'] = $data['user_name'];
|
||||
$insertData['qrcode_url'] = $data['qrcode_url'];
|
||||
$mark = '使用微信提现' . $insertData['extract_price'] . '元' . $feeMark;
|
||||
if (sys_config('weixin_extract_type', 0) && $openid) {
|
||||
if ($data['extract_price'] < 0.1) {
|
||||
throw new ApiException('企业微信付款到零钱最低金额为1元');
|
||||
}
|
||||
}
|
||||
}
|
||||
$res1 = $this->transaction(function () use ($insertData, $data, $uid, $userService, $user, $mark) {
|
||||
if (!$res1 = $this->dao->save($insertData)) {
|
||||
throw new ApiException('申请提现失败');
|
||||
}
|
||||
|
||||
//保存佣金记录(事务内自动扣减余额并记录流水)
|
||||
/** @var UserBrokerageServices $userBrokerageServices */
|
||||
$userBrokerageServices = app()->make(UserBrokerageServices::class);
|
||||
$userBrokerageServices->brokerageIncome('extract', $uid, ['mark' => $mark, 'number' => $data['extract_price']], $res1['id']);
|
||||
return $res1;
|
||||
});
|
||||
|
||||
try {
|
||||
ChannelService::instance()->send('WITHDRAW', ['id' => $res1->id]);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
/** @var SystemAdminServices $systemAdmin */
|
||||
$systemAdmin = app()->make(SystemAdminServices::class);
|
||||
$systemAdmin->adminNewPush();
|
||||
//触发提现申请事件(消息发送已移至 UserExtractApplyListener)
|
||||
event('UserExtractApply', [
|
||||
$user['nickname'],
|
||||
$data['extract_price']
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $where
|
||||
* @param string $SumField
|
||||
* @param string $selectType
|
||||
* @param string $group
|
||||
* @return float|mixed
|
||||
*/
|
||||
public function getOutMoneyByWhere(array $where, string $SumField, string $selectType, string $group = "")
|
||||
{
|
||||
switch ($selectType) {
|
||||
case "sum" :
|
||||
return $this->dao->getWhereSumField($where, $SumField);
|
||||
case "group" :
|
||||
return $this->dao->getGroupField($where, $SumField, $group);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -309,10 +309,21 @@ class UserMoneyServices extends BaseService
|
||||
*/
|
||||
public function balanceList($where)
|
||||
{
|
||||
$status = [];
|
||||
$typeTitleMap = [];
|
||||
foreach ($this->incomeData as $value) {
|
||||
$status[$value['type']] = $value['title'];
|
||||
$typeTitleMap[$value['type']] = $value['title'];
|
||||
}
|
||||
// 交易类型筛选项不展示:充值及退款、佣金转余额、抽奖、注册赠送
|
||||
$filterExcludeTypes = [
|
||||
'recharge',
|
||||
'recharge_refund',
|
||||
'recharge_refund_release',
|
||||
'extract',
|
||||
'lottery_use',
|
||||
'lottery_add',
|
||||
'register_system_add',
|
||||
];
|
||||
$status = array_diff_key($typeTitleMap, array_flip($filterExcludeTypes));
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$list = $this->dao->getList($where, $page, $limit);
|
||||
//关联用户
|
||||
@ -336,10 +347,10 @@ class UserMoneyServices extends BaseService
|
||||
} elseif ($item['type'] == 'pay_member') {
|
||||
$item['relation'] = $memberOrderServices->value(['id' => $item['link_id']], 'order_id');
|
||||
} else {
|
||||
$item['relation'] = $status[$item['type']];
|
||||
$item['relation'] = $typeTitleMap[$item['type']] ?? $item['type'];
|
||||
}
|
||||
$item['add_time'] = date('Y-m-d H:i:s', $item['add_time']);
|
||||
$item['type_name'] = $status[$item['type']];
|
||||
$item['type_name'] = $typeTitleMap[$item['type']] ?? $item['type'];
|
||||
}
|
||||
$count = $this->dao->count($where);
|
||||
return compact('list', 'count', 'status');
|
||||
|
||||
@ -185,69 +185,6 @@ class UserRechargeServices extends BaseService
|
||||
return 'wx' . date('YmdHis', time()) . substr(implode(NULL, array_map('ord', str_split(substr(uniqid('', true), 7, 13), 1))), 0, 8);
|
||||
}
|
||||
|
||||
public function importNowMoney(int $uid, $price)
|
||||
{
|
||||
/** @var \app\services\user\UserServices $userServices */
|
||||
$userServices = app()->make(\app\services\user\UserServices::class);
|
||||
$user = $userServices->getUserInfo($uid);
|
||||
if (!$user) {
|
||||
throw new ApiException('参数错误');
|
||||
}
|
||||
$profile = event('DistributionServiceProxy', [
|
||||
'service' => 'DistributionUserServices', 'action' => 'profile',
|
||||
'args' => [$uid, is_object($user) ? $user->toArray() : (array)$user],
|
||||
]);
|
||||
if (is_array($profile[0] ?? null)) {
|
||||
$user = array_replace(is_object($user) ? $user->toArray() : (array)$user, $profile[0]);
|
||||
}
|
||||
/** @var \app\services\user\UserBrokerageServices $frozenPrices */
|
||||
$frozenPrices = app()->make(\app\services\user\UserBrokerageServices::class);
|
||||
$broken_commission = $frozenPrices->getUserFrozenPrice($uid);
|
||||
$commissionCount = bcsub((string)$user['brokerage_price'], (string)$broken_commission, 2);
|
||||
if ($price > $commissionCount) {
|
||||
throw new ApiException('转入金额不能大于可提现佣金');
|
||||
}
|
||||
|
||||
return $this->transaction(function () use ($uid, $price, $user) {
|
||||
$rechargeInfo = [
|
||||
'uid' => $uid,
|
||||
'order_id' => SnowflakeIdGenerator::generate('cz'),
|
||||
'recharge_type' => 'balance',
|
||||
'price' => $price,
|
||||
'give_price' => 0,
|
||||
'paid' => 1,
|
||||
'pay_time' => time(),
|
||||
'add_time' => time()
|
||||
];
|
||||
if (!$re = $this->dao->save($rechargeInfo)) {
|
||||
throw new ApiException('写入余额充值失败');
|
||||
}
|
||||
|
||||
/** @var \app\services\user\UserBrokerageServices $userBrokerageServices */
|
||||
$userBrokerageServices = app()->make(\app\services\user\UserBrokerageServices::class);
|
||||
$userBrokerageServices->brokerageIncome('brokerage_to_nowMoney', $uid, $price, $re['id']);
|
||||
|
||||
/** @var \app\services\user\UserMoneyServices $userMoneyServices */
|
||||
$userMoneyServices = app()->make(\app\services\user\UserMoneyServices::class);
|
||||
$userMoneyServices->moneyIncome('brokerage_to_nowMoney', $uid, $price, $re['id']);
|
||||
|
||||
$extractInfo = [
|
||||
'uid' => $uid,
|
||||
'real_name' => $user['nickname'],
|
||||
'extract_type' => 'balance',
|
||||
'extract_price' => $price,
|
||||
'balance' => $user['brokerage_price'],
|
||||
'add_time' => time(),
|
||||
'status' => 1
|
||||
];
|
||||
/** @var \app\services\user\UserExtractServices $userExtract */
|
||||
$userExtract = app()->make(\app\services\user\UserExtractServices::class);
|
||||
$userExtract->save($extractInfo);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public function recharge(int $uid, $price, $recharId, $type, $from, bool $renten = false)
|
||||
{
|
||||
/** @var \app\services\user\UserServices $userServices */
|
||||
@ -301,9 +238,6 @@ class UserRechargeServices extends BaseService
|
||||
return $order_info;
|
||||
}
|
||||
return ['msg' => '', 'type' => $from, 'data' => $order_info];
|
||||
case 1:
|
||||
$this->importNowMoney($uid, $price);
|
||||
return ['msg' => '转入余额成功', 'type' => $from, 'data' => []];
|
||||
default:
|
||||
throw new ApiException('参数错误');
|
||||
}
|
||||
|
||||
@ -63,7 +63,6 @@ class UserSpreadServices extends BaseService
|
||||
return false;
|
||||
}
|
||||
if ($this->dao->save(['uid' => $uid, 'spread_uid' => $spread_uid, 'spread_time' => $spread_time ?: time()])) {
|
||||
$userServices->incField($spread_uid, 'spread_count', 1);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
||||
@ -49,7 +49,7 @@ class UserStoreOrderServices extends BaseService
|
||||
$where[] = ['u.nickname|u.phone', 'LIKE', "%$keyword%"];
|
||||
}
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$field = "u.uid,u.nickname,u.phone,u.avatar,from_unixtime(u.add_time,'%Y/%m/%d') as time,u.spread_time,u.spread_count as childCount,p.orderCount,p.numberCount";
|
||||
$field = "u.uid,u.nickname,u.phone,u.avatar,from_unixtime(u.add_time,'%Y/%m/%d') as time,u.spread_time,(SELECT COUNT(*) FROM eb_user su WHERE su.spread_uid = u.uid) as childCount,p.orderCount,p.numberCount";
|
||||
$list = $this->dao->getUserSpreadCountList($where, $field, $orderBy, $page, $limit);
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
<?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\user;
|
||||
|
||||
use crmeb\basic\BaseService;
|
||||
use app\dao\user\UserUserBrokerageDao;
|
||||
|
||||
/**
|
||||
* 用户关联佣金
|
||||
* Class UserUserBrokerageServices
|
||||
* @package app\services\user
|
||||
*/
|
||||
class UserUserBrokerageServices extends BaseService
|
||||
{
|
||||
/**
|
||||
* @var UserUserBrokerageDao
|
||||
*/
|
||||
protected $dao;
|
||||
|
||||
/**
|
||||
* UserUserBrokerageServices constructor.
|
||||
* @param UserUserBrokerageDao $dao
|
||||
*/
|
||||
public function __construct(UserUserBrokerageDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取佣金列表
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @param string $order
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function getBrokerageList(array $where, string $field = '*', string $order = '', int $limit = 0)
|
||||
{
|
||||
if ($limit) {
|
||||
[$page] = $this->getPageValue();
|
||||
} else {
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
}
|
||||
$list = $this->dao->getList($where, $field, $order, $page, $limit);
|
||||
$count = $this->dao->getCount($where);
|
||||
return [$count, $list];
|
||||
}
|
||||
}
|
||||
@ -104,16 +104,8 @@ class UserWechatAuthServices extends BaseService
|
||||
$wechatInfo = [];
|
||||
}
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$spreadInfo = $userServices->getUserInfo((int)$spreadId);
|
||||
if (!$spreadInfo) {
|
||||
if (!$userServices->getUserInfo((int)$spreadId)) {
|
||||
$spreadId = 0;
|
||||
$wechatInfo['staff_id'] = 0;
|
||||
$wechatInfo['agent_id'] = 0;
|
||||
$wechatInfo['division_id'] = 0;
|
||||
} else {
|
||||
$wechatInfo['staff_id'] = $spreadInfo['staff_id'];
|
||||
$wechatInfo['agent_id'] = $spreadInfo['agent_id'];
|
||||
$wechatInfo['division_id'] = $spreadInfo['division_id'];
|
||||
}
|
||||
|
||||
unset($wechatInfo['subscribe_scene'], $wechatInfo['qr_scene'], $wechatInfo['qr_scene_str']);
|
||||
|
||||
@ -57,7 +57,6 @@ class SafeEvent extends Event
|
||||
'OutPush',
|
||||
'UserRecharge',
|
||||
'UserRechargeRefund',
|
||||
'UserExtract',
|
||||
'UserRevenue',
|
||||
'UserBalanceChange',
|
||||
// 营销旁路类:失败影响活动参与但不应影响交易主流程
|
||||
|
||||
@ -290,10 +290,7 @@ class UserSignServices extends BaseService
|
||||
$user['frozen_integral'] = $userBill->getIntegralSumByPm($user['uid'], 1, '', true);
|
||||
}
|
||||
unset($user['pwd']);
|
||||
if (!$user['is_promoter']) {
|
||||
$user['is_promoter'] = (int)sys_config('store_brokerage_statu') == 2;
|
||||
}
|
||||
return $user->hidden(['account', 'real_name', 'birthday', 'card_id', 'mark', 'partner_id', 'group_id', 'add_time', 'add_ip', 'phone', 'last_time', 'last_ip', 'spread_uid', 'spread_time', 'user_type', 'status', 'level', 'clean_time', 'addres'])->toArray();
|
||||
return $user->hidden(['account', 'real_name', 'birthday', 'card_id', 'mark', 'group_id', 'add_time', 'add_ip', 'phone', 'last_time', 'last_ip', 'spread_uid', 'spread_time', 'user_type', 'status', 'level', 'clean_time', 'addres'])->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -80,8 +80,6 @@ class Installer extends BaseModuleInstaller
|
||||
`unit_name` varchar(32) NOT NULL DEFAULT '' COMMENT 'SKU单位(SKU级实际单位,如瓶/箱/盒)',
|
||||
`image` varchar(128) NOT NULL DEFAULT '' COMMENT 'SKU图片',
|
||||
`unique_code` char(8) NOT NULL DEFAULT '' COMMENT '唯一码',
|
||||
`brokerage` decimal(12,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '一级返佣',
|
||||
`brokerage_two` decimal(12,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '二级返佣',
|
||||
`is_default_select` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否默认选中',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
|
||||
@ -252,9 +252,6 @@ class ProductSaveServices extends BaseService
|
||||
if (isset($attr['is_vip_price']) && !isset($attr['vip_price'])) {
|
||||
$attr['vip_price'] = $attr['is_vip_price'];
|
||||
}
|
||||
if (isset($attr['is_sub_price']) && !isset($attr['brokerage'])) {
|
||||
$attr['brokerage'] = $attr['is_sub_price'];
|
||||
}
|
||||
// suk(逗号分隔字符串,如"红色,M")→ detail(KV对象,如{"颜色":"红色","尺寸":"M"})
|
||||
if (!isset($attr['detail']) && !empty($attr['suk']) && !empty($data['items'])) {
|
||||
$sukValues = explode(',', $attr['suk']);
|
||||
@ -291,8 +288,6 @@ class ProductSaveServices extends BaseService
|
||||
'stock' => $data['stock'] ?? 0,
|
||||
'cost' => $data['cost'] ?? 0,
|
||||
'pic' => $data['image'] ?: '',
|
||||
'brokerage' => 0,
|
||||
'brokerage_two' => 0,
|
||||
'vip_price' => $data['vip_price'] ?? 0,
|
||||
'detail' => ['规格' => '默认'],
|
||||
]];
|
||||
@ -339,23 +334,6 @@ class ProductSaveServices extends BaseService
|
||||
}
|
||||
$item['cost'] = $item['cost'] ?? 0;
|
||||
$item['vip_price'] = $item['vip_price'] ?? $item['price'];
|
||||
$item['brokerage'] = $item['brokerage'] ?? 0;
|
||||
$item['brokerage_two'] = $item['brokerage_two'] ?? 0;
|
||||
}
|
||||
unset($item);
|
||||
|
||||
// 佣金校验(is_sub 可能为数组或标量,统一判断分销开关)
|
||||
$isSub = is_array($data['is_sub'] ?? [])
|
||||
? (in_array(1, $data['is_sub']) ? 1 : 0)
|
||||
: intval($data['is_sub'] ?? 0);
|
||||
foreach ($detail as &$item) {
|
||||
if ($isSub == 0) {
|
||||
$item['brokerage'] = 0;
|
||||
$item['brokerage_two'] = 0;
|
||||
}
|
||||
if (($item['brokerage'] + $item['brokerage_two']) > $item['price']) {
|
||||
throw new \RuntimeException('一二级返佣相加不能大于商品售价');
|
||||
}
|
||||
}
|
||||
unset($item);
|
||||
|
||||
@ -559,8 +537,6 @@ class ProductSaveServices extends BaseService
|
||||
'stock' => $item['stock'] ?? 0,
|
||||
'image' => $image,
|
||||
'unique_code' => $uniqueCode,
|
||||
'brokerage' => $item['brokerage'] ?? 0,
|
||||
'brokerage_two' => $item['brokerage_two'] ?? 0,
|
||||
'is_default_select' => (int)($item['is_default_select'] ?? ($index === 0 ? 1 : 0)),
|
||||
'is_show' => $item['is_show'] ?? 1,
|
||||
'bar_code' => $item['bar_code'] ?? '',
|
||||
|
||||
@ -200,10 +200,8 @@ class ProductSaveServices extends BaseService
|
||||
// 首图:从轮播图第一张取
|
||||
$data['image'] = $data['slider_image'][0] ?? '';
|
||||
|
||||
// 从 oneFormValidate 取 vip_price、brokerage 等值
|
||||
// 从 oneFormValidate 取 vip_price 等值
|
||||
$oneForm = $data['oneFormValidate'][0] ?? [];
|
||||
$brokerage = (float)($oneForm['brokerage'] ?? 0);
|
||||
$brokerageTwo = (float)($oneForm['brokerage_two'] ?? 0);
|
||||
$vipPrice = (float)($oneForm['vip_price'] ?? 0);
|
||||
|
||||
// 未携带 attrs(异常兜底)时,自动生成单规格默认 SKU
|
||||
@ -214,8 +212,6 @@ class ProductSaveServices extends BaseService
|
||||
'stock' => $data['stock'] ?? 0,
|
||||
'cost' => $data['cost'] ?? 0,
|
||||
'pic' => $data['image'] ?: '',
|
||||
'brokerage' => $brokerage,
|
||||
'brokerage_two' => $brokerageTwo,
|
||||
'vip_price' => $vipPrice,
|
||||
'detail' => ['规格' => '默认'],
|
||||
'download_url' => '',
|
||||
|
||||
@ -1,167 +0,0 @@
|
||||
<?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 modules\service\mcp\tools;
|
||||
|
||||
use app\dao\user\UserExtractDao;
|
||||
use crmeb\interfaces\McpToolInterface;
|
||||
use modules\service\mcp\SensitiveData;
|
||||
|
||||
/**
|
||||
* MCP 工具:提现申请列表
|
||||
*
|
||||
* 银行账号、支付宝账号、姓名、微信号等敏感信息脱敏后返回。
|
||||
*/
|
||||
class ExtractListTool implements McpToolInterface
|
||||
{
|
||||
/**
|
||||
* 提现 DAO
|
||||
* @var UserExtractDao
|
||||
*/
|
||||
private UserExtractDao $dao;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param UserExtractDao $dao 提现 DAO
|
||||
*/
|
||||
public function __construct(UserExtractDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具名称
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'crmeb_extract_list';
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具描述
|
||||
* @return string
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return '查询用户佣金提现申请列表,支持按状态、提现方式、用户 UID 筛选;'
|
||||
. 'status:0 审核中、1 已提现、-1 未通过(未通过原因见 fail_msg);'
|
||||
. 'extract_type:bank 银行卡、alipay 支付宝、weixin 微信、balance 转余额;'
|
||||
. 'balance 字段为提现前佣金余额。银行账号、支付宝账号、姓名、微信号已脱敏。';
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具中文名(管理后台展示)
|
||||
* @return string
|
||||
*/
|
||||
public function label(): string
|
||||
{
|
||||
return '提现申请列表';
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具分类(管理后台分组展示)
|
||||
* @return string
|
||||
*/
|
||||
public function category(): string
|
||||
{
|
||||
return '财务';
|
||||
}
|
||||
|
||||
/**
|
||||
* 入参 Schema
|
||||
* @return array
|
||||
*/
|
||||
public function inputSchema(): array
|
||||
{
|
||||
return [
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'page' => ['type' => 'number', 'description' => '页码'],
|
||||
'limit' => ['type' => 'number', 'description' => '每页数量'],
|
||||
'uid' => ['type' => 'number', 'description' => '用户 UID'],
|
||||
'status' => ['type' => 'number', 'description' => '状态:0 审核中、1 已提现、-1 未通过'],
|
||||
'extract_type' => ['type' => 'string', 'description' => '提现方式:bank/alipay/weixin/balance'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行工具
|
||||
*
|
||||
* @param array $args 参数(page/limit/uid/status/extract_type)
|
||||
* @return array 提现申请列表和总数
|
||||
*/
|
||||
public function execute(array $args): array
|
||||
{
|
||||
$page = max(1, (int)($args['page'] ?? 1));
|
||||
$limit = min(100, max(1, (int)($args['limit'] ?? 10)));
|
||||
|
||||
$where = [];
|
||||
if (!empty($args['uid'])) {
|
||||
$where[] = ['uid', '=', (int)$args['uid']];
|
||||
}
|
||||
if (isset($args['status']) && $args['status'] !== '') {
|
||||
$where[] = ['status', '=', (int)$args['status']];
|
||||
}
|
||||
if (isset($args['extract_type']) && $args['extract_type'] !== '') {
|
||||
$where[] = ['extract_type', '=', (string)$args['extract_type']];
|
||||
}
|
||||
|
||||
$list = $this->dao->search($where)
|
||||
->field('id,uid,real_name,extract_type,bank_code,alipay_code,wechat,extract_price,extract_fee,balance,mark,status,fail_msg,add_time')
|
||||
->order('id desc')
|
||||
->page($page, $limit)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
foreach ($list as &$item) {
|
||||
$item['real_name'] = self::maskName((string)($item['real_name'] ?? ''));
|
||||
$item['bank_code'] = self::maskAccount((string)($item['bank_code'] ?? ''));
|
||||
$item['alipay_code'] = self::maskAccount((string)($item['alipay_code'] ?? ''));
|
||||
$item['wechat'] = self::maskAccount((string)($item['wechat'] ?? ''));
|
||||
}
|
||||
unset($item);
|
||||
|
||||
$count = $this->dao->search($where)->count();
|
||||
return ['list' => $list, 'count' => $count];
|
||||
}
|
||||
|
||||
/**
|
||||
* 姓名脱敏(仅保留首字符)
|
||||
* @param string $value 姓名
|
||||
* @return string
|
||||
*/
|
||||
private static function maskName(string $value): string
|
||||
{
|
||||
if ($value === '') {
|
||||
return '';
|
||||
}
|
||||
return mb_substr($value, 0, 1) . str_repeat('*', max(0, mb_strlen($value) - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 账号类信息脱敏(保留后 4 位)
|
||||
* @param string $value 账号
|
||||
* @return string
|
||||
*/
|
||||
private static function maskAccount(string $value): string
|
||||
{
|
||||
if ($value === '') {
|
||||
return '';
|
||||
}
|
||||
if (mb_strlen($value) <= 4) {
|
||||
return '******';
|
||||
}
|
||||
return str_repeat('*', mb_strlen($value) - 4) . mb_substr($value, -4);
|
||||
}
|
||||
}
|
||||
@ -1,129 +0,0 @@
|
||||
<?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 modules\service\mcp\tools;
|
||||
|
||||
use app\dao\user\UserExtractDao;
|
||||
use crmeb\interfaces\McpToolInterface;
|
||||
use modules\service\mcp\ReportPeriod;
|
||||
|
||||
/**
|
||||
* MCP 工具:提现统计
|
||||
*
|
||||
* 按提现申请时间统计各状态笔数与金额。
|
||||
*/
|
||||
class ExtractStatisticsTool implements McpToolInterface
|
||||
{
|
||||
/**
|
||||
* 提现 DAO
|
||||
* @var UserExtractDao
|
||||
*/
|
||||
private UserExtractDao $dao;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param UserExtractDao $dao 提现 DAO
|
||||
*/
|
||||
public function __construct(UserExtractDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具名称
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'crmeb_extract_statistics';
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具描述
|
||||
* @return string
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return '按提现申请时间(add_time)统计指定时间段内的提现情况:'
|
||||
. '已提现(成功)笔数、金额与手续费合计,审核中笔数与金额,未通过笔数。'
|
||||
. '不按审核/打款时间统计,金额为申请金额。默认近7天,最多31天。';
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具中文名(管理后台展示)
|
||||
* @return string
|
||||
*/
|
||||
public function label(): string
|
||||
{
|
||||
return '提现统计';
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具分类(管理后台分组展示)
|
||||
* @return string
|
||||
*/
|
||||
public function category(): string
|
||||
{
|
||||
return '财务';
|
||||
}
|
||||
|
||||
/**
|
||||
* 入参 Schema
|
||||
* @return array
|
||||
*/
|
||||
public function inputSchema(): array
|
||||
{
|
||||
return [
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'start_date' => ['type' => 'string', 'format' => 'date', 'description' => '开始日期 YYYY-MM-DD'],
|
||||
'end_date' => ['type' => 'string', 'format' => 'date', 'description' => '结束日期 YYYY-MM-DD,包含结束日'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行工具
|
||||
*
|
||||
* @param array $args 参数(start_date/end_date)
|
||||
* @return array 提现统计结果
|
||||
*/
|
||||
public function execute(array $args): array
|
||||
{
|
||||
$period = ReportPeriod::fromArgs($args, '提现统计');
|
||||
$range = [$period['start_time'], $period['end_time']];
|
||||
|
||||
$sumFields = ['COUNT(id) AS cnt', 'COALESCE(SUM(extract_price),0) AS amount', 'COALESCE(SUM(extract_fee),0) AS fee'];
|
||||
$success = $this->dao->search([['status', '=', 1]])
|
||||
->whereBetween('add_time', $range)->field($sumFields)->find();
|
||||
$success = $success ? $success->toArray() : [];
|
||||
$auditing = $this->dao->search([['status', '=', 0]])
|
||||
->whereBetween('add_time', $range)->field($sumFields)->find();
|
||||
$auditing = $auditing ? $auditing->toArray() : [];
|
||||
$fail = $this->dao->search([['status', '=', -1]])
|
||||
->whereBetween('add_time', $range)->field(['COUNT(id) AS cnt'])->find();
|
||||
$fail = $fail ? $fail->toArray() : [];
|
||||
|
||||
return [
|
||||
'start_date' => $period['start_date'],
|
||||
'end_date' => $period['end_date'],
|
||||
'metrics' => [
|
||||
'success_count' => (int)($success['cnt'] ?? 0),
|
||||
'success_amount' => (string)($success['amount'] ?? '0.00'),
|
||||
'success_fee' => (string)($success['fee'] ?? '0.00'),
|
||||
'auditing_count' => (int)($auditing['cnt'] ?? 0),
|
||||
'auditing_amount' => (string)($auditing['amount'] ?? '0.00'),
|
||||
'fail_count' => (int)($fail['cnt'] ?? 0),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -12,8 +12,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace modules\service\mcp\tools;
|
||||
|
||||
use app\dao\user\UserBrokerageDao;
|
||||
use app\dao\user\UserExtractDao;
|
||||
use app\dao\user\UserMoneyDao;
|
||||
use app\dao\user\UserRechargeDao;
|
||||
use crmeb\interfaces\McpToolInterface;
|
||||
@ -22,7 +20,7 @@ use modules\service\mcp\ReportPeriod;
|
||||
/**
|
||||
* MCP 工具:财务总览统计
|
||||
*
|
||||
* 汇总指定时间段内平台核心资金数据:充值、提现、余额流水、佣金流水。
|
||||
* 汇总指定时间段内平台核心资金数据:充值、余额流水(提现/佣金统计已随分销佣金功能下线移除)。
|
||||
*/
|
||||
class FinanceOverviewTool implements McpToolInterface
|
||||
{
|
||||
@ -32,41 +30,23 @@ class FinanceOverviewTool implements McpToolInterface
|
||||
*/
|
||||
private UserRechargeDao $rechargeDao;
|
||||
|
||||
/**
|
||||
* 提现 DAO
|
||||
* @var UserExtractDao
|
||||
*/
|
||||
private UserExtractDao $extractDao;
|
||||
|
||||
/**
|
||||
* 余额流水 DAO
|
||||
* @var UserMoneyDao
|
||||
*/
|
||||
private UserMoneyDao $moneyDao;
|
||||
|
||||
/**
|
||||
* 佣金流水 DAO
|
||||
* @var UserBrokerageDao
|
||||
*/
|
||||
private UserBrokerageDao $brokerageDao;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param UserRechargeDao $rechargeDao 充值 DAO
|
||||
* @param UserExtractDao $extractDao 提现 DAO
|
||||
* @param UserMoneyDao $moneyDao 余额流水 DAO
|
||||
* @param UserBrokerageDao $brokerageDao 佣金流水 DAO
|
||||
*/
|
||||
public function __construct(
|
||||
UserRechargeDao $rechargeDao,
|
||||
UserExtractDao $extractDao,
|
||||
UserMoneyDao $moneyDao,
|
||||
UserBrokerageDao $brokerageDao
|
||||
UserMoneyDao $moneyDao
|
||||
) {
|
||||
$this->rechargeDao = $rechargeDao;
|
||||
$this->extractDao = $extractDao;
|
||||
$this->moneyDao = $moneyDao;
|
||||
$this->brokerageDao = $brokerageDao;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,9 +66,7 @@ class FinanceOverviewTool implements McpToolInterface
|
||||
{
|
||||
return '汇总统计指定时间段内平台核心资金数据:'
|
||||
. '充值(仅已支付,按支付时间统计笔数、金额、赠送金额);'
|
||||
. '提现(按申请时间统计已提现/审核中/未通过的笔数与金额及成功手续费);'
|
||||
. '余额流水(按记账时间统计有效记录的收入与支出笔数、金额);'
|
||||
. '佣金流水(按记账时间统计有效记录的收入与支出笔数、金额,收入含冻结中佣金)。'
|
||||
. '余额流水(按记账时间统计有效记录的收入与支出笔数、金额)。'
|
||||
. '不包含积分/经验变动,金额单位为元。默认近7天,最多31天。';
|
||||
}
|
||||
|
||||
@ -143,18 +121,6 @@ class FinanceOverviewTool implements McpToolInterface
|
||||
->find();
|
||||
$recharge = $recharge ? $recharge->toArray() : [];
|
||||
|
||||
// 提现:按申请时间,分状态统计
|
||||
$extractFields = ['COUNT(id) AS cnt', 'COALESCE(SUM(extract_price),0) AS amount', 'COALESCE(SUM(extract_fee),0) AS fee'];
|
||||
$extractSuccess = $this->extractDao->search([['status', '=', 1]])
|
||||
->whereBetween('add_time', $range)->field($extractFields)->find();
|
||||
$extractSuccess = $extractSuccess ? $extractSuccess->toArray() : [];
|
||||
$extractAudit = $this->extractDao->search([['status', '=', 0]])
|
||||
->whereBetween('add_time', $range)->field($extractFields)->find();
|
||||
$extractAudit = $extractAudit ? $extractAudit->toArray() : [];
|
||||
$extractFail = $this->extractDao->search([['status', '=', -1]])
|
||||
->whereBetween('add_time', $range)->field(['COUNT(id) AS cnt'])->find();
|
||||
$extractFail = $extractFail ? $extractFail->toArray() : [];
|
||||
|
||||
// 余额流水:仅有效记录,按记账时间统计收/支
|
||||
$flowFields = ['COUNT(id) AS cnt', 'COALESCE(SUM(number),0) AS amount'];
|
||||
$moneyIn = $this->moneyDao->search([['pm', '=', 1], ['status', '=', 1]])
|
||||
@ -164,14 +130,6 @@ class FinanceOverviewTool implements McpToolInterface
|
||||
->whereBetween('add_time', $range)->field($flowFields)->find();
|
||||
$moneyOut = $moneyOut ? $moneyOut->toArray() : [];
|
||||
|
||||
// 佣金流水:仅有效记录,按记账时间统计收/支(收入含冻结中佣金)
|
||||
$brokerageIn = $this->brokerageDao->search([['pm', '=', 1], ['status', '=', 1]])
|
||||
->whereBetween('add_time', $range)->field($flowFields)->find();
|
||||
$brokerageIn = $brokerageIn ? $brokerageIn->toArray() : [];
|
||||
$brokerageOut = $this->brokerageDao->search([['pm', '=', 0], ['status', '=', 1]])
|
||||
->whereBetween('add_time', $range)->field($flowFields)->find();
|
||||
$brokerageOut = $brokerageOut ? $brokerageOut->toArray() : [];
|
||||
|
||||
return [
|
||||
'start_date' => $period['start_date'],
|
||||
'end_date' => $period['end_date'],
|
||||
@ -181,26 +139,12 @@ class FinanceOverviewTool implements McpToolInterface
|
||||
'amount' => (string)($recharge['amount'] ?? '0.00'),
|
||||
'give_amount' => (string)($recharge['give_amount'] ?? '0.00'),
|
||||
],
|
||||
'extract' => [
|
||||
'success_count' => (int)($extractSuccess['cnt'] ?? 0),
|
||||
'success_amount' => (string)($extractSuccess['amount'] ?? '0.00'),
|
||||
'success_fee' => (string)($extractSuccess['fee'] ?? '0.00'),
|
||||
'auditing_count' => (int)($extractAudit['cnt'] ?? 0),
|
||||
'auditing_amount' => (string)($extractAudit['amount'] ?? '0.00'),
|
||||
'fail_count' => (int)($extractFail['cnt'] ?? 0),
|
||||
],
|
||||
'user_money' => [
|
||||
'in_count' => (int)($moneyIn['cnt'] ?? 0),
|
||||
'in_amount' => (string)($moneyIn['amount'] ?? '0.00'),
|
||||
'out_count' => (int)($moneyOut['cnt'] ?? 0),
|
||||
'out_amount' => (string)($moneyOut['amount'] ?? '0.00'),
|
||||
],
|
||||
'user_brokerage' => [
|
||||
'in_count' => (int)($brokerageIn['cnt'] ?? 0),
|
||||
'in_amount' => (string)($brokerageIn['amount'] ?? '0.00'),
|
||||
'out_count' => (int)($brokerageOut['cnt'] ?? 0),
|
||||
'out_amount' => (string)($brokerageOut['amount'] ?? '0.00'),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -1,135 +0,0 @@
|
||||
<?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 modules\service\mcp\tools;
|
||||
|
||||
use app\dao\user\UserBrokerageDao;
|
||||
use crmeb\interfaces\McpToolInterface;
|
||||
|
||||
/**
|
||||
* MCP 工具:佣金流水列表
|
||||
*
|
||||
* v8 起分销佣金变动记于独立账单表 user_brokerage。
|
||||
*/
|
||||
class UserBrokerageListTool implements McpToolInterface
|
||||
{
|
||||
/**
|
||||
* 佣金流水 DAO
|
||||
* @var UserBrokerageDao
|
||||
*/
|
||||
private UserBrokerageDao $dao;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param UserBrokerageDao $dao 佣金流水 DAO
|
||||
*/
|
||||
public function __construct(UserBrokerageDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具名称
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'crmeb_user_brokerage_list';
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具描述
|
||||
* @return string
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return '查询用户分销佣金流水列表(v8 独立佣金账单表),支持按用户 UID、类型、收/支方向、状态筛选,'
|
||||
. '返回变动金额、变动后佣金、冻结到期时间与备注;'
|
||||
. 'type 常见值:self_brokerage 自购订单佣金、one_brokerage/two_brokerage 一/二级推广订单佣金、'
|
||||
. 'brokerage_user 推广用户佣金、extract 佣金提现、extract_fail 提现失败退回、extract_money 提现到余额、'
|
||||
. 'refund 退款退回佣金、pink_master_brokerage 拼团团长佣金;'
|
||||
. 'pm:1 收入、0 支出;status:1 有效、0 待确定、-1 无效,冻结中的佣金以 frozen_time 大于当前时间判定。';
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具中文名(管理后台展示)
|
||||
* @return string
|
||||
*/
|
||||
public function label(): string
|
||||
{
|
||||
return '佣金流水列表';
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具分类(管理后台分组展示)
|
||||
* @return string
|
||||
*/
|
||||
public function category(): string
|
||||
{
|
||||
return '财务';
|
||||
}
|
||||
|
||||
/**
|
||||
* 入参 Schema
|
||||
* @return array
|
||||
*/
|
||||
public function inputSchema(): array
|
||||
{
|
||||
return [
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'page' => ['type' => 'number', 'description' => '页码'],
|
||||
'limit' => ['type' => 'number', 'description' => '每页数量'],
|
||||
'uid' => ['type' => 'number', 'description' => '用户 UID'],
|
||||
'type' => ['type' => 'string', 'description' => '变动类型,如 one_brokerage/extract/refund'],
|
||||
'pm' => ['type' => 'number', 'description' => '收/支方向:1 收入、0 支出'],
|
||||
'status' => ['type' => 'number', 'description' => '状态:1 有效、0 待确定、-1 无效'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行工具
|
||||
*
|
||||
* @param array $args 参数(page/limit/uid/type/pm/status)
|
||||
* @return array 佣金流水列表和总数
|
||||
*/
|
||||
public function execute(array $args): array
|
||||
{
|
||||
$page = max(1, (int)($args['page'] ?? 1));
|
||||
$limit = min(100, max(1, (int)($args['limit'] ?? 10)));
|
||||
|
||||
$where = [];
|
||||
if (!empty($args['uid'])) {
|
||||
$where[] = ['uid', '=', (int)$args['uid']];
|
||||
}
|
||||
if (isset($args['type']) && $args['type'] !== '') {
|
||||
$where[] = ['type', '=', (string)$args['type']];
|
||||
}
|
||||
if (isset($args['pm']) && $args['pm'] !== '') {
|
||||
$where[] = ['pm', '=', (int)$args['pm'] == 1 ? 1 : 0];
|
||||
}
|
||||
if (isset($args['status']) && $args['status'] !== '') {
|
||||
$where[] = ['status', '=', (int)$args['status']];
|
||||
}
|
||||
|
||||
$list = $this->dao->search($where)
|
||||
->field('id,uid,link_id,type,title,number,balance,pm,mark,status,frozen_time,add_time')
|
||||
->order('id desc')
|
||||
->page($page, $limit)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
$count = $this->dao->search($where)->count();
|
||||
return ['list' => $list, 'count' => $count];
|
||||
}
|
||||
}
|
||||
@ -961,7 +961,7 @@ CREATE TABLE IF NOT EXISTS `eb_pay_channel_platform` (
|
||||
CREATE TABLE IF NOT EXISTS `eb_pay_record` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`order_no` varchar(32) NOT NULL DEFAULT '' COMMENT '原商户订单号',
|
||||
`business_type` varchar(32) NOT NULL DEFAULT '' COMMENT '支付业务: product/user_recharge/member/offline_scan/user_extract',
|
||||
`business_type` varchar(32) NOT NULL DEFAULT '' COMMENT '支付业务: product/user_recharge/member/offline_scan',
|
||||
`channel_key` varchar(32) NOT NULL DEFAULT '' COMMENT '原支付渠道',
|
||||
`amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '服务端支付金额',
|
||||
`context` json DEFAULT NULL COMMENT '原支付身份快照,不含密钥',
|
||||
@ -1367,8 +1367,6 @@ CREATE TABLE IF NOT EXISTS `eb_store_product_attr_value` (
|
||||
`vip_price` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '会员专享价',
|
||||
`weight` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '重量',
|
||||
`volume` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '体积',
|
||||
`brokerage` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '一级返佣',
|
||||
`brokerage_two` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '二级返佣',
|
||||
`type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '活动类型 0=商品,1=秒杀,2=砍价,3=拼团',
|
||||
`quota` int NOT NULL DEFAULT '0' COMMENT '活动限购数量',
|
||||
`quota_show` int NOT NULL DEFAULT '0' COMMENT '活动限购数量显示',
|
||||
@ -2060,7 +2058,6 @@ CREATE TABLE IF NOT EXISTS `eb_user` (
|
||||
`birthday` int NOT NULL DEFAULT '0' COMMENT '生日',
|
||||
`card_id` varchar(20) NOT NULL DEFAULT '' COMMENT '身份证号码',
|
||||
`mark` varchar(255) NOT NULL DEFAULT '' COMMENT '用户备注',
|
||||
`partner_id` int NOT NULL DEFAULT '0' COMMENT '合伙人id',
|
||||
`group_id` int NOT NULL DEFAULT '0' COMMENT '用户分组id',
|
||||
`nickname` varchar(60) NOT NULL DEFAULT '' COMMENT '用户昵称',
|
||||
`avatar` varchar(256) NOT NULL DEFAULT '' COMMENT '用户头像',
|
||||
@ -2071,21 +2068,16 @@ CREATE TABLE IF NOT EXISTS `eb_user` (
|
||||
`last_time` int unsigned NOT NULL DEFAULT '0' COMMENT '最后一次登录时间',
|
||||
`last_ip` varchar(16) NOT NULL DEFAULT '' COMMENT '最后一次登录ip',
|
||||
`now_money` decimal(12,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '用户余额',
|
||||
`brokerage_price` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '佣金金额',
|
||||
`integral` int unsigned NOT NULL DEFAULT '0' COMMENT '用户剩余积分',
|
||||
`exp` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '会员经验',
|
||||
`sign_num` int NOT NULL DEFAULT '0' COMMENT '连续签到天数',
|
||||
`sign_remind` tinyint(1) NOT NULL DEFAULT '0' COMMENT '签到提醒状态',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1为正常,0为禁止',
|
||||
`level` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '等级',
|
||||
`agent_level` int NOT NULL DEFAULT '0' COMMENT '分销等级',
|
||||
`spread_open` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有推广资格',
|
||||
`spread_uid` int unsigned NOT NULL DEFAULT '0' COMMENT '推广元id',
|
||||
`spread_time` int unsigned NOT NULL DEFAULT '0' COMMENT '推广员关联时间',
|
||||
`spread_uid` int unsigned NOT NULL DEFAULT '0' COMMENT '上级用户',
|
||||
`spread_time` int unsigned NOT NULL DEFAULT '0' COMMENT '上级关联时间',
|
||||
`user_type` varchar(32) NOT NULL DEFAULT '' COMMENT '用户类型',
|
||||
`is_promoter` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否为推广员',
|
||||
`pay_count` int unsigned NOT NULL DEFAULT '0' COMMENT '用户购买次数',
|
||||
`spread_count` int NOT NULL DEFAULT '0' COMMENT '下级人数',
|
||||
`clean_time` int NOT NULL DEFAULT '0' COMMENT '清理会员时间',
|
||||
`addres` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址',
|
||||
`adminid` int unsigned NOT NULL DEFAULT '0' COMMENT '管理员编号 ',
|
||||
@ -2095,26 +2087,12 @@ CREATE TABLE IF NOT EXISTS `eb_user` (
|
||||
`is_ever_level` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否永久性会员 0: 非永久会员 1:永久会员',
|
||||
`overdue_time` bigint NOT NULL DEFAULT '0' COMMENT '会员到期时间',
|
||||
`uniqid` varchar(32) NOT NULL DEFAULT '' COMMENT '用户唯一值',
|
||||
`division_name` varchar(255) NOT NULL DEFAULT '' COMMENT '事业部/代理商名称',
|
||||
`division_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '代理类型:0普通,1事业部,2代理,3员工',
|
||||
`division_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '代理状态',
|
||||
`is_division` tinyint(1) NOT NULL DEFAULT '0' COMMENT '事业部状态',
|
||||
`is_agent` tinyint(1) NOT NULL DEFAULT '0' COMMENT '代理状态',
|
||||
`is_staff` tinyint(1) NOT NULL DEFAULT '0' COMMENT '员工状态',
|
||||
`division_id` int NOT NULL DEFAULT '0' COMMENT '事业部id',
|
||||
`agent_id` int NOT NULL DEFAULT '0' COMMENT '代理商id',
|
||||
`staff_id` int NOT NULL DEFAULT '0' COMMENT '员工id',
|
||||
`division_percent` int NOT NULL DEFAULT '0' COMMENT '分佣比例',
|
||||
`division_change_time` int NOT NULL DEFAULT '0' COMMENT '事业部/代理/员工修改时间',
|
||||
`division_end_time` int NOT NULL DEFAULT '0' COMMENT '事业部/代理/员工结束时间',
|
||||
`division_invite` int NOT NULL DEFAULT '0' COMMENT '代理商邀请码',
|
||||
`is_del` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否注销',
|
||||
PRIMARY KEY (`uid`) USING BTREE,
|
||||
KEY `account` (`account`) USING BTREE,
|
||||
KEY `spreaduid` (`spread_uid`) USING BTREE,
|
||||
KEY `level` (`level`) USING BTREE,
|
||||
KEY `status` (`status`) USING BTREE,
|
||||
KEY `is_promoter` (`is_promoter`) USING BTREE
|
||||
KEY `status` (`status`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='用户表';
|
||||
|
||||
-- eb_user_address:用户地址表
|
||||
@ -2164,42 +2142,7 @@ CREATE TABLE IF NOT EXISTS `eb_user_bill` (
|
||||
KEY `type` (`category`,`type`,`link_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='用户账单表';
|
||||
|
||||
-- eb_user_brokerage:用户分佣账单表
|
||||
CREATE TABLE IF NOT EXISTS `eb_user_brokerage` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '用户佣金id',
|
||||
`uid` int unsigned NOT NULL DEFAULT '0' COMMENT '用户uid',
|
||||
`link_id` varchar(32) NOT NULL DEFAULT '0' COMMENT '关联id',
|
||||
`type` varchar(64) NOT NULL DEFAULT '' COMMENT '明细类型',
|
||||
`title` varchar(64) NOT NULL DEFAULT '' COMMENT '账单标题',
|
||||
`number` decimal(12,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '明细数字',
|
||||
`balance` decimal(12,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '剩余',
|
||||
`pm` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '0 = 支出 1 = 获得',
|
||||
`mark` varchar(512) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '0 = 带确定 1 = 有效 -1 = 无效',
|
||||
`take` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0 = 未收货 1 = 已收货',
|
||||
`frozen_time` int unsigned NOT NULL DEFAULT '0' COMMENT '冻结到期时间',
|
||||
`add_time` int unsigned NOT NULL DEFAULT '0' COMMENT '添加时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `uid` (`uid`) USING BTREE,
|
||||
KEY `status` (`status`) USING BTREE,
|
||||
KEY `add_time` (`add_time`) USING BTREE,
|
||||
KEY `pm` (`pm`) USING BTREE,
|
||||
KEY `type` (`type`,`link_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='用户分佣账单表';
|
||||
|
||||
-- eb_user_brokerage_frozen:用户佣金冻结记录表
|
||||
CREATE TABLE IF NOT EXISTS `eb_user_brokerage_frozen` (
|
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增ID',
|
||||
`uid` int NOT NULL DEFAULT '0' COMMENT '用户uid',
|
||||
`price` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '金额',
|
||||
`uill_id` int NOT NULL DEFAULT '0' COMMENT '关联id',
|
||||
`frozen_time` int NOT NULL DEFAULT '0' COMMENT '冻结到期时间',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否有效',
|
||||
`add_time` int NOT NULL DEFAULT '0' COMMENT '添加时间',
|
||||
`order_id` varchar(50) NOT NULL DEFAULT '' COMMENT '订单id',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `uid` (`uid`,`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='用户佣金冻结记录表';
|
||||
|
||||
-- eb_user_cancel:用户注销表
|
||||
CREATE TABLE IF NOT EXISTS `eb_user_cancel` (
|
||||
@ -2242,40 +2185,6 @@ CREATE TABLE IF NOT EXISTS `eb_user_enter` (
|
||||
KEY `status` (`status`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='商户申请表';
|
||||
|
||||
-- eb_user_extract:用户提现表
|
||||
CREATE TABLE IF NOT EXISTS `eb_user_extract` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
|
||||
`wechat_order_id` varchar(32) NOT NULL DEFAULT '' COMMENT '微信订单ID',
|
||||
`uid` int unsigned NOT NULL DEFAULT '0' COMMENT '用户id',
|
||||
`real_name` varchar(64) NOT NULL DEFAULT '' COMMENT '名称',
|
||||
`user_name` varchar(64) NOT NULL DEFAULT '' COMMENT '用户姓名',
|
||||
`extract_type` varchar(32) NOT NULL DEFAULT 'bank' COMMENT 'bank = 银行卡 alipay = 支付宝wx=微信',
|
||||
`bank_code` varchar(32) NOT NULL DEFAULT '0' COMMENT '银行卡',
|
||||
`bank_address` varchar(256) NOT NULL DEFAULT '' COMMENT '开户地址',
|
||||
`alipay_code` varchar(64) NOT NULL DEFAULT '' COMMENT '支付宝账号',
|
||||
`extract_price` decimal(12,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '提现金额',
|
||||
`extract_fee` decimal(12,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '提现手续费',
|
||||
`mark` varchar(512) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`balance` decimal(12,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '提现前佣金',
|
||||
`fail_msg` varchar(128) NOT NULL DEFAULT '' COMMENT '无效原因',
|
||||
`fail_time` int unsigned NOT NULL DEFAULT '0' COMMENT '无效时间',
|
||||
`add_time` int unsigned NOT NULL DEFAULT '0' COMMENT '添加时间',
|
||||
`status` tinyint NOT NULL DEFAULT '0' COMMENT '-1 未通过 0 审核中 1 已提现',
|
||||
`wechat` varchar(50) NOT NULL DEFAULT '' COMMENT '微信号',
|
||||
`qrcode_url` varchar(255) NOT NULL DEFAULT '' COMMENT '二维码地址',
|
||||
`channel_type` varchar(32) NOT NULL DEFAULT '' COMMENT '提现来源',
|
||||
`out_bill_no` varchar(255) NOT NULL DEFAULT '' COMMENT '商户单号',
|
||||
`transfer_bill_no` varchar(255) NOT NULL DEFAULT '' COMMENT '微信转账单号',
|
||||
`state` varchar(32) NOT NULL DEFAULT '' COMMENT '单据状态',
|
||||
`package_info` varchar(2000) NOT NULL DEFAULT '' COMMENT '跳转领取页面的package信息',
|
||||
`fail_reason` varchar(255) NOT NULL DEFAULT '' COMMENT '失败原因',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `extract_type` (`extract_type`) USING BTREE,
|
||||
KEY `status` (`status`) USING BTREE,
|
||||
KEY `add_time` (`add_time`) USING BTREE,
|
||||
KEY `openid` (`uid`) USING BTREE,
|
||||
KEY `fail_time` (`fail_time`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='用户提现表';
|
||||
|
||||
-- eb_user_friends:用户好友关系
|
||||
CREATE TABLE IF NOT EXISTS `eb_user_friends` (
|
||||
@ -2421,8 +2330,8 @@ CREATE TABLE IF NOT EXISTS `eb_user_search` (
|
||||
CREATE TABLE IF NOT EXISTS `eb_user_spread` (
|
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增ID',
|
||||
`uid` int NOT NULL DEFAULT '0' COMMENT '用户uid',
|
||||
`spread_uid` int NOT NULL DEFAULT '0' COMMENT '推广人uid',
|
||||
`spread_time` int NOT NULL DEFAULT '0' COMMENT '推广时间',
|
||||
`spread_uid` int NOT NULL DEFAULT '0' COMMENT '上级用户',
|
||||
`spread_time` int NOT NULL DEFAULT '0' COMMENT '上级关联时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `uid` (`uid`) USING BTREE,
|
||||
KEY `spread_uid` (`spread_uid`) USING BTREE
|
||||
|
||||
@ -1,57 +1,57 @@
|
||||
-- eb_store_product_attr_value default data
|
||||
|
||||
INSERT INTO `eb_store_product_attr_value` (`id`, `product_id`, `suk`, `stock`, `sales`, `price`, `image`, `unique`, `cost`, `bar_code`, `ot_price`, `vip_price`, `weight`, `volume`, `brokerage`, `brokerage_two`, `type`, `quota`, `quota_show`, `is_virtual`, `coupon_id`, `disk_info`) VALUES
|
||||
(12, 1, '冰冰蓝,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '2035bf28', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(13, 1, '冰冰蓝,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '139ae798', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(14, 1, '小白裙,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '99a2a4c0', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(15, 1, '小白裙,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'c220a445', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(16, 2, '冰冰蓝,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '56844b8a', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(17, 2, '冰冰蓝,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '9f05bd22', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(18, 2, '小白裙,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'd098ed4e', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(19, 2, '小白裙,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'e3f7714b', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(20, 3, '冰冰蓝,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'f090d58a', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(21, 3, '冰冰蓝,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '1b4b869b', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(22, 3, '小白裙,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'cfbb45d3', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(23, 3, '小白裙,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'f975554e', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(24, 4, '冰冰蓝,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '06543925', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(25, 4, '冰冰蓝,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'f58604f6', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(26, 4, '小白裙,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '8d85e6cb', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(27, 4, '小白裙,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'c7068db5', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 20, 20, 0, 0, ''),
|
||||
(29, 5, '黑色,46mm', 100, 0, '269.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5ae4f6a3f8bf153beb849e02e14b5c40.png', '100fac2c', '219.00', '', '359.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 50, 50, 0, 0, ''),
|
||||
(30, 6, '黑色,46mm', 100, 0, '269.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5ae4f6a3f8bf153beb849e02e14b5c40.png', '99594ea5', '219.00', '', '359.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 50, 50, 0, 0, ''),
|
||||
(31, 7, '黑色,46mm', 100, 0, '269.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5ae4f6a3f8bf153beb849e02e14b5c40.png', 'ea37aabb', '219.00', '', '359.00', '0.00', '1.00', '1.00', '0.00', '0.00', 1, 50, 50, 0, 0, ''),
|
||||
(51, 9, '深空灰色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'c8123941', '3299.00', '', '3599.00', '0.00', '1.00', '1.00', '10.00', '5.00', 1, 10, 10, 0, 0, ''),
|
||||
(52, 9, '星光色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '3f796cea', '3299.00', '', '3599.00', '0.00', '1.00', '1.00', '10.00', '5.00', 1, 10, 10, 0, 0, ''),
|
||||
(53, 9, '粉色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '838385df', '3299.00', '', '3599.00', '0.00', '1.00', '1.00', '10.00', '5.00', 1, 10, 10, 0, 0, ''),
|
||||
(54, 10, '深空灰色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '6576b8c7', '3299.00', '', '3599.00', '0.00', '1.00', '1.00', '10.00', '5.00', 1, 10, 10, 0, 0, ''),
|
||||
(55, 10, '星光色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '5f4755fc', '3299.00', '', '3599.00', '0.00', '1.00', '1.00', '10.00', '5.00', 1, 10, 10, 0, 0, ''),
|
||||
(56, 10, '粉色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '67a6e796', '3299.00', '', '3599.00', '0.00', '1.00', '1.00', '10.00', '5.00', 1, 10, 10, 0, 0, ''),
|
||||
(73, 8, '深空灰色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'f94124e5', '3299.00', '', '4799.00', '0.00', '1.00', '1.00', '10.00', '5.00', 1, 10, 10, 0, 0, ''),
|
||||
(74, 8, '星光色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '8353e407', '3299.00', '', '4799.00', '0.00', '1.00', '1.00', '10.00', '5.00', 1, 10, 10, 0, 0, ''),
|
||||
(75, 8, '粉色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'eee2d733', '3299.00', '', '4799.00', '0.00', '1.00', '1.00', '10.00', '5.00', 1, 10, 10, 0, 0, ''),
|
||||
(76, 1, '冰冰蓝,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'a1a5e606', '2099.00', '123123', '2899.00', '2288.00', '1.00', '1.00', '0.00', '0.00', 0, 0, 0, 0, 0, ''),
|
||||
(77, 1, '冰冰蓝,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '3023a2cc', '2099.00', '123123', '2899.00', '2288.00', '1.00', '1.00', '0.00', '0.00', 0, 0, 0, 0, 0, ''),
|
||||
(78, 1, '小白裙,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'a918ea81', '2099.00', '123123', '2899.00', '2288.00', '1.00', '1.00', '0.00', '0.00', 0, 0, 0, 0, 0, ''),
|
||||
(79, 1, '小白裙,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'fdc24d0f', '2099.00', '123123', '2899.00', '2288.00', '1.00', '1.00', '0.00', '0.00', 0, 0, 0, 0, 0, ''),
|
||||
(81, 3, '黑色,46mm', 100, 0, '269.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5ae4f6a3f8bf153beb849e02e14b5c40.png', '11c60075', '219.00', '', '359.00', '0.00', '1.00', '1.00', '0.00', '0.00', 0, 0, 0, 0, 0, ''),
|
||||
(82, 4, '深空灰色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'fcd734ba', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(83, 4, '深空灰色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '1aead9ce', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(84, 4, '深空灰色,256G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '0861b6c5', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(85, 4, '深空灰色,256G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '2ce93ba6', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(86, 4, '紫色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'c38188d3', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(87, 4, '紫色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '00271626', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(88, 4, '紫色,256G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'e610eef6', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(89, 4, '紫色,256G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '8a2bf54d', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(90, 4, '星光色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '36810e54', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(91, 4, '星光色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '1ec62878', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(92, 4, '星光色,256G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '045c7587', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(93, 4, '星光色,256G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '18e3bd8b', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(94, 4, '粉色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'dd59f872', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(95, 4, '粉色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'f707b61e', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(96, 4, '粉色,256G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'd92cc632', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(97, 4, '粉色,256G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '8fe3d9d7', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, ''),
|
||||
(99, 1, '深空灰色,64G,WLAN版', 100, 0, '39990.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'daa6fde0', '3299.00', '', '4799.00', '0.00', '1.00', '1.00', '10.00', '5.00', 4, 10, 10, 0, 0, ''),
|
||||
(100, 2, '冰冰蓝,8+128', 100, 0, '22990.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '6b0d29da', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', '0.00', '0.00', 4, 100, 100, 0, 0, ''),
|
||||
(101, 1, '深空灰色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '26ad2f25', '3299.00', '', '4799.00', '0.00', '1.00', '1.00', '10.00', '5.00', 2, 100, 100, 0, 0, ''),
|
||||
(102, 1, '深空灰色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '5943864e', '3299.00', '', '4799.00', '0.00', '1.00', '1.00', '10.00', '5.00', 3, 100, 100, 0, 0, ''),
|
||||
(103, 2, '银色', 100, 0, '1999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/cf5414089d598d064b0a4d7154a4f491.png', '318d6620', '1699.00', '', '2199.00', '1988.00', '1.00', '1.00', '10.00', '5.00', 0, 0, 0, 0, 0, '');
|
||||
INSERT INTO `eb_store_product_attr_value` (`id`, `product_id`, `suk`, `stock`, `sales`, `price`, `image`, `unique`, `cost`, `bar_code`, `ot_price`, `vip_price`, `weight`, `volume`, `type`, `quota`, `quota_show`, `is_virtual`, `coupon_id`, `disk_info`) VALUES
|
||||
(12, 1, '冰冰蓝,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '2035bf28', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(13, 1, '冰冰蓝,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '139ae798', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(14, 1, '小白裙,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '99a2a4c0', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(15, 1, '小白裙,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'c220a445', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(16, 2, '冰冰蓝,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '56844b8a', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(17, 2, '冰冰蓝,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '9f05bd22', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(18, 2, '小白裙,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'd098ed4e', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(19, 2, '小白裙,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'e3f7714b', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(20, 3, '冰冰蓝,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'f090d58a', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(21, 3, '冰冰蓝,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '1b4b869b', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(22, 3, '小白裙,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'cfbb45d3', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(23, 3, '小白裙,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'f975554e', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(24, 4, '冰冰蓝,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '06543925', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(25, 4, '冰冰蓝,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'f58604f6', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(26, 4, '小白裙,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '8d85e6cb', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(27, 4, '小白裙,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'c7068db5', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 1, 20, 20, 0, 0, ''),
|
||||
(29, 5, '黑色,46mm', 100, 0, '269.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5ae4f6a3f8bf153beb849e02e14b5c40.png', '100fac2c', '219.00', '', '359.00', '0.00', '1.00', '1.00', 1, 50, 50, 0, 0, ''),
|
||||
(30, 6, '黑色,46mm', 100, 0, '269.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5ae4f6a3f8bf153beb849e02e14b5c40.png', '99594ea5', '219.00', '', '359.00', '0.00', '1.00', '1.00', 1, 50, 50, 0, 0, ''),
|
||||
(31, 7, '黑色,46mm', 100, 0, '269.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5ae4f6a3f8bf153beb849e02e14b5c40.png', 'ea37aabb', '219.00', '', '359.00', '0.00', '1.00', '1.00', 1, 50, 50, 0, 0, ''),
|
||||
(51, 9, '深空灰色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'c8123941', '3299.00', '', '3599.00', '0.00', '1.00', '1.00', 1, 10, 10, 0, 0, ''),
|
||||
(52, 9, '星光色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '3f796cea', '3299.00', '', '3599.00', '0.00', '1.00', '1.00', 1, 10, 10, 0, 0, ''),
|
||||
(53, 9, '粉色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '838385df', '3299.00', '', '3599.00', '0.00', '1.00', '1.00', 1, 10, 10, 0, 0, ''),
|
||||
(54, 10, '深空灰色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '6576b8c7', '3299.00', '', '3599.00', '0.00', '1.00', '1.00', 1, 10, 10, 0, 0, ''),
|
||||
(55, 10, '星光色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '5f4755fc', '3299.00', '', '3599.00', '0.00', '1.00', '1.00', 1, 10, 10, 0, 0, ''),
|
||||
(56, 10, '粉色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '67a6e796', '3299.00', '', '3599.00', '0.00', '1.00', '1.00', 1, 10, 10, 0, 0, ''),
|
||||
(73, 8, '深空灰色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'f94124e5', '3299.00', '', '4799.00', '0.00', '1.00', '1.00', 1, 10, 10, 0, 0, ''),
|
||||
(74, 8, '星光色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '8353e407', '3299.00', '', '4799.00', '0.00', '1.00', '1.00', 1, 10, 10, 0, 0, ''),
|
||||
(75, 8, '粉色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'eee2d733', '3299.00', '', '4799.00', '0.00', '1.00', '1.00', 1, 10, 10, 0, 0, ''),
|
||||
(76, 1, '冰冰蓝,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'a1a5e606', '2099.00', '123123', '2899.00', '2288.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(77, 1, '冰冰蓝,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '3023a2cc', '2099.00', '123123', '2899.00', '2288.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(78, 1, '小白裙,8+128', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'a918ea81', '2099.00', '123123', '2899.00', '2288.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(79, 1, '小白裙,8+256', 100, 0, '2299.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', 'fdc24d0f', '2099.00', '123123', '2899.00', '2288.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(81, 3, '黑色,46mm', 100, 0, '269.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5ae4f6a3f8bf153beb849e02e14b5c40.png', '11c60075', '219.00', '', '359.00', '0.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(82, 4, '深空灰色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'fcd734ba', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(83, 4, '深空灰色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '1aead9ce', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(84, 4, '深空灰色,256G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '0861b6c5', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(85, 4, '深空灰色,256G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '2ce93ba6', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(86, 4, '紫色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'c38188d3', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(87, 4, '紫色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '00271626', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(88, 4, '紫色,256G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'e610eef6', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(89, 4, '紫色,256G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '8a2bf54d', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(90, 4, '星光色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '36810e54', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(91, 4, '星光色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '1ec62878', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(92, 4, '星光色,256G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '045c7587', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(93, 4, '星光色,256G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '18e3bd8b', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(94, 4, '粉色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'dd59f872', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(95, 4, '粉色,64G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'f707b61e', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(96, 4, '粉色,256G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'd92cc632', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(97, 4, '粉色,256G,蜂窝版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '8fe3d9d7', '3299.00', '', '4799.00', '3599.00', '1.00', '1.00', 0, 0, 0, 0, 0, ''),
|
||||
(99, 1, '深空灰色,64G,WLAN版', 100, 0, '39990.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', 'daa6fde0', '3299.00', '', '4799.00', '0.00', '1.00', '1.00', 4, 10, 10, 0, 0, ''),
|
||||
(100, 2, '冰冰蓝,8+128', 100, 0, '22990.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/3b570808fc3593aa51b2a94ceaba3e6f.png', '6b0d29da', '2099.00', '123123', '2899.00', '0.00', '1.00', '1.00', 4, 100, 100, 0, 0, ''),
|
||||
(101, 1, '深空灰色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '26ad2f25', '3299.00', '', '4799.00', '0.00', '1.00', '1.00', 2, 100, 100, 0, 0, ''),
|
||||
(102, 1, '深空灰色,64G,WLAN版', 100, 0, '3999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/5b64e6584c06020347a5fa58af79d42a.png', '5943864e', '3299.00', '', '4799.00', '0.00', '1.00', '1.00', 3, 100, 100, 0, 0, ''),
|
||||
(103, 2, '银色', 100, 0, '1999.00', 'http://demo.crmeb.com/uploads/attach/2023/02/20230213/cf5414089d598d064b0a4d7154a4f491.png', '318d6620', '1699.00', '', '2199.00', '1988.00', '1.00', '1.00', 0, 0, 0, 0, 0, '');
|
||||
@ -21,9 +21,7 @@ INSERT INTO `eb_system_menus` (`id`, `pid`, `icon`, `menu_name`, `module`, `cont
|
||||
(30, 27, 'CRMEB-174-youhuiquan-xianxing', '优惠券', 'admin', 'marketing.store_coupon', '', '', '', '[]', 99, 1, 1, 1, '/marketing/store_coupon_issue/index', '27', 1, 'marketing', 1, 'marketing-store_coupon-index', 'core:marketing-store_coupon-index', 0, '优惠券'),
|
||||
(34, 27, 'CRMEB-276-jifen-xianxing', '积分管理', 'admin', 'marketing.user_point', '', '', '', '[]', 95, 1, 1, 1, '/marketing/integral/record', '27', 1, 'marketing', 1, 'marketing-user_point-index', 'core:marketing-user_point-index', 0, '积分管理'),
|
||||
(35, 0, 'CRMEB-115-caiwu-mianxing', '财务', 'admin', 'finance', '', '', '', '[]', 90, 1, 1, 1, '/finance', '', 1, 'home', 1, 'admin-finance', 'core:admin-finance', 0, '财务'),
|
||||
(36, 35, 'CRMEB-302-caiwucaozuo-xianxing', '财务操作', 'admin', 'finance', '', '', '', '[]', 1, 1, 1, 1, '/finance/user_extract/index', '35', 1, 'finance', 1, 'finance-user_extract-index', 'core:finance-user_extract-index', 0, '财务操作'),
|
||||
(37, 35, 'CRMEB-300-caiwujilu-xianxing', '财务记录', 'admin', 'finance', '', '', '', '[]', 1, 1, 1, 1, '/finance/billing_records/index', '35', 1, 'finance', 1, 'finance-user-recharge-index', 'core:finance-user-recharge-index', 0, '财务记录'),
|
||||
(39, 36, '', '提现申请', 'admin', 'finance.user_extract', '', '', '', '[]', 1, 1, 1, 1, '/finance/user_extract/index', '', 1, 'finance', 0, 'finance-user_extract', 'core:finance-user_extract', 0, '提现申请'),
|
||||
(40, 37, '', '充值记录', 'admin', 'finance.user_recharge', '', '', '', '[]', 1, 0, 0, 1, '/finance/user_recharge/index', '', 1, 'finance', 0, 'finance-user-recharge', 'core:finance-user-recharge', 0, '充值记录'),
|
||||
(43, 0, 'CRMEB-121-neirong-mianxing', '内容', 'admin', 'cms', '', '', '', '[]', 85, 1, 1, 1, '/cms', '', 1, 'home', 1, 'admin-cms', 'core:admin-cms', 0, '内容'),
|
||||
(44, 43, 'CRMEB-208-wenzhangguanli-xianxing', '文章管理', 'admin', 'cms.article', 'index', '', '', '[]', 1, 1, 1, 1, '/cms/article/index', '', 1, 'cms', 0, 'cms-article-index', 'core:cms-article-index', 0, '文章管理'),
|
||||
@ -679,7 +677,6 @@ INSERT INTO `eb_system_menus` (`id`, `pid`, `icon`, `menu_name`, `module`, `cont
|
||||
(4905, 898, '', '发送记录', '', '', '', '', '', '[]', 4, 1, 1, 1, '/setting/notification/log', '25/898', 1, '', 0, 'notification-log', 'core:notification-log', 0, ''),
|
||||
(4960, 4, 'CRMEB-420-tuikuandingdanshu-xianxing', '售后订单', 'adminapi', 'order.after_sale', 'index', '', '', '[]', 60, 1, 1, 1, '/order/afterSale', '4', 1, 'order', 1, 'admin-order-afterSale-index', 'core:admin-order-afterSale-index', 0, '售后订单'),
|
||||
(2610, 760, '', '收款二维码', 'admin', '', '', '', '', '[]', 0, 1, 1, 1, '/', '4/760', 3, '', 0, 'order-offline-qrcode', '', 0, '收款二维码'),
|
||||
(5223, 37, '', '佣金记录', 'admin', 'finance.finance', '', '', '', '[]', 1, 0, 0, 1, '/finance/finance/commission', '35/37', 1, 'finance', 1, 'finance-finance-commission', 'core:finance-finance-commission', 0, '佣金记录'),
|
||||
(5225, 3477, '', '打印管理', 'admin', '', '', '', '', '[]', 0, 1, 1, 1, '/driver/printerDriver/index', '25/3477', 1, '', 0, 'driver-printer', 'core:driver-printer', 0, '查看已安装的打印机驱动'),
|
||||
(5226, 12, 'CRMEB-286-peisong-xianxing', '面单配置', 'admin', '', '', '', '', '[]', 115, 1, 1, 1, '/setting/express', '12', 1, 'setting', 1, 'driver:express:setting', 'core:driver:express:setting', 0, '物流公司、打印机编号、寄件信息与打印记录'),
|
||||
(6001, 1, 'CRMEB-196-shangpinguanli-xianxing', '商品中台', 'admin', 'product.center_product', 'list', '', '', '[]', 101, 1, 1, 1, '/product/center/list', '', 1, 'product', 1, 'admin-center-product-index', 'core:admin-center-product-index', 0, '商品中台统一管理'),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
-- eb_user default data
|
||||
|
||||
INSERT INTO `eb_user` (`uid`, `account`, `pwd`, `real_name`, `birthday`, `card_id`, `mark`, `partner_id`, `group_id`, `nickname`, `avatar`, `phone`, `add_time`, `add_ip`, `last_time`, `last_ip`, `now_money`, `brokerage_price`, `integral`, `exp`, `sign_num`, `status`, `level`, `agent_level`, `spread_open`, `spread_uid`, `spread_time`, `user_type`, `is_promoter`, `pay_count`, `spread_count`, `clean_time`, `addres`, `adminid`, `login_type`, `record_phone`, `is_money_level`, `is_ever_level`, `overdue_time`, `uniqid`, `division_type`, `division_status`, `is_division`, `is_agent`, `is_staff`, `division_id`, `agent_id`, `staff_id`, `division_percent`, `division_change_time`, `division_end_time`, `division_invite`, `is_del`) VALUES
|
||||
(1, '', '96e79218965eb72c92a549dd5a330112', 'CRMEB', 0, '', '', 0, 1, 'CRMEB', '/statics/system_images/default_avatar.jpeg', '13000000000', 1642573867, '', 0, '', '0.00', '0.00', 0, '3000.00', 0, 1, 3, 0, 1, 0, 0, 'h5', 1, 0, 0, 0, '', 0, '', '0', 0, 0, 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
(2, '', '96e79218965eb72c92a549dd5a330112', '客服', 1664208000, '', '', 0, 0, '客服', '/statics/system_images/default_avatar.jpeg', '15088888888', 1664273171, '', 0, '', '0.00', '0.00', 0, '0.00', 0, 1, 0, 0, 1, 0, 0, 'h5', 1, 0, 0, 0, '', 0, '', '0', 0, 0, 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
INSERT INTO `eb_user` (`uid`, `account`, `pwd`, `real_name`, `birthday`, `card_id`, `mark`, `group_id`, `nickname`, `avatar`, `phone`, `add_time`, `add_ip`, `last_time`, `last_ip`, `now_money`, `integral`, `exp`, `sign_num`, `status`, `level`, `spread_uid`, `spread_time`, `user_type`, `pay_count`, `clean_time`, `addres`, `adminid`, `login_type`, `record_phone`, `is_money_level`, `is_ever_level`, `overdue_time`, `uniqid`, `is_del`) VALUES
|
||||
(1, '', '96e79218965eb72c92a549dd5a330112', 'CRMEB', 0, '', '', 1, 'CRMEB', '/statics/system_images/default_avatar.jpeg', '13000000000', 1642573867, '', 0, '', '0.00', 0, '3000.00', 0, 1, 3, 0, 0, 'h5', 0, 0, '', 0, '', '0', 0, 0, 0, '', 0),
|
||||
(2, '', '96e79218965eb72c92a549dd5a330112', '客服', 1664208000, '', '', 0, '客服', '/statics/system_images/default_avatar.jpeg', '15088888888', 1664273171, '', 0, '', '0.00', 0, '0.00', 0, 1, 0, 0, 0, 'h5', 0, 0, '', 0, '', '0', 0, 0, 0, '', 0);
|
||||
@ -32,29 +32,6 @@ export function billListApi(data) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 佣金记录 -- 列表
|
||||
* @param {Object} param data {Object} 传值
|
||||
*/
|
||||
export function commissionListApi(data) {
|
||||
return request({
|
||||
url: 'finance/finance/commission_list',
|
||||
method: 'get',
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 佣金记录 -- 详情
|
||||
* @param {Number} param id {Number} 佣金记录ID
|
||||
*/
|
||||
export function commissionDetailApi(id) {
|
||||
return request({
|
||||
url: `finance/finance/user_info/${id}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 佣金记录 -- 个人提现列表
|
||||
* @param {Number} param id {Number} 佣金记录 用户ID
|
||||
@ -161,18 +138,6 @@ export function userFinanceApi(data) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 佣金记录 -- 用户佣金导出
|
||||
* @param {Number} param data {Number} 请求参数data
|
||||
*/
|
||||
export function userCommissionApi(data) {
|
||||
return request({
|
||||
url: `export/userCommission`,
|
||||
method: 'get',
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 用户充值记录 -- 用户充值记录导出
|
||||
* @param {Number} param data {Number} 请求参数data
|
||||
|
||||
@ -16,9 +16,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="item acea-row row-middle">
|
||||
<div class="name commission ivu-form-item-content">
|
||||
佣金:<span>{{ spread.brokerage_price }}</span>
|
||||
</div>
|
||||
<div class="name ivu-form-item-content">
|
||||
真实姓名:<span>{{ spread.real_name }}</span>
|
||||
</div>
|
||||
|
||||
@ -16,9 +16,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="item acea-row row-middle">
|
||||
<div class="name commission ivu-form-item-content">
|
||||
佣金:<span>{{ spread.brokerage_pric }}</span>
|
||||
</div>
|
||||
<div class="name ivu-form-item-content">
|
||||
真实姓名:<span>{{ spread.real_name }}</span>
|
||||
</div>
|
||||
|
||||
@ -545,7 +545,6 @@ export const getDefaultConfig = (num) => ({
|
||||
{ id: 2, name: '积分' },
|
||||
{ id: 5, name: '收藏商品' },
|
||||
{ id: 6, name: '浏览记录' },
|
||||
{ id: 8, name: '推广佣金' },
|
||||
{ id: 9, name: '推广人' },
|
||||
{ id: 10, name: '推广订单' },
|
||||
],
|
||||
@ -734,7 +733,6 @@ const dataList = computed(() => {
|
||||
{ id: 2, name: '积分', val: '3000' },
|
||||
{ id: 5, name: '收藏商品', val: '1660' },
|
||||
{ id: 6, name: '浏览记录', val: '1660' },
|
||||
{ id: 8, name: '推广佣金', val: '666' },
|
||||
{ id: 9, name: '推广人', val: '1660' },
|
||||
{ id: 10, name: '推广订单', val: '1660' },
|
||||
];
|
||||
|
||||
@ -1,231 +0,0 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<el-dialog v-model="modals" title="用户详情" :close-on-click-modal="false" width="720px">
|
||||
<div class="" v-loading="spinShow">
|
||||
<div class="dashboard-workplace-header-tip">
|
||||
<div class="dashboard-workplace-header-tip-desc">
|
||||
<span class="dashboard-workplace-header-tip-desc-sp">姓名:{{ detailsData.nickname }}</span>
|
||||
<span class="dashboard-workplace-header-tip-desc-sp"
|
||||
>上级推广人:{{ detailsData.spread_name ? detailsData.spread_name : '无' }}</span
|
||||
>
|
||||
<span class="dashboard-workplace-header-tip-desc-sp">佣金总收入:{{ detailsData.number }}</span>
|
||||
<span class="dashboard-workplace-header-tip-desc-sp">用户余额:{{ detailsData.now_money }}</span>
|
||||
<span class="dashboard-workplace-header-tip-desc-sp">创建时间:{{ detailsData.add_time }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider direction="vertical" dashed />
|
||||
<el-form ref="formRef" label-width="75px" :label-position="labelPosition" class="tabform" @submit.prevent>
|
||||
<el-row :gutter="24">
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-form-item label="订单号/昵称:">-->
|
||||
<!-- <el-input enter-button placeholder="请输入" element-id="name" v-model="formValidate.nickname"-->
|
||||
<!-- clearable/>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="时间范围:" class="tab_data">
|
||||
<date-range-picker
|
||||
width="250px"
|
||||
clearable
|
||||
:editable="false"
|
||||
@change="onchangeTime"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
></date-range-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button type="primary" v-db-click @click="userSearchs">搜索</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<el-table
|
||||
:data="tabList"
|
||||
ref="table"
|
||||
v-loading="loading"
|
||||
no-userFrom-text="暂无数据"
|
||||
no-filtered-userFrom-text="暂无筛选结果"
|
||||
class="table"
|
||||
>
|
||||
<el-table-column label="佣金金额" min-width="130">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.number }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="获得时间" min-width="130">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row._add_time }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="130">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.mark }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="acea-row row-right page">
|
||||
<pagination
|
||||
v-if="total"
|
||||
:total="total"
|
||||
v-model:page="formValidate.page"
|
||||
v-model:limit="formValidate.limit"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useMediaStore } from '@/store/module/media';
|
||||
import { commissionDetailApi, extractlistApi } from '@/api/finance';
|
||||
|
||||
defineOptions({ name: 'commissionDetails' });
|
||||
|
||||
const { isMobile } = storeToRefs(useMediaStore());
|
||||
|
||||
const modals = ref(false);
|
||||
const spinShow = ref(false);
|
||||
const detailsData = ref({});
|
||||
const Ids = ref(0);
|
||||
const loading = ref(false);
|
||||
const formValidate = ref({
|
||||
nickname: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
page: 1, // 当前页
|
||||
limit: 15, // 每页显示条数
|
||||
});
|
||||
const total = ref(0);
|
||||
const tabList = ref([]);
|
||||
|
||||
const labelWidth = computed(() => {
|
||||
return isMobile.value ? undefined : '80px';
|
||||
});
|
||||
const labelPosition = computed(() => {
|
||||
return isMobile.value ? 'top' : 'right';
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (Ids.value) {
|
||||
getList();
|
||||
}
|
||||
});
|
||||
|
||||
// 时间
|
||||
function onchangeTime(e) {
|
||||
formValidate.value.start_time = e[0];
|
||||
formValidate.value.end_time = e[1];
|
||||
}
|
||||
// 详情
|
||||
function getDetails(id) {
|
||||
Ids.value = id;
|
||||
spinShow.value = true;
|
||||
commissionDetailApi(id)
|
||||
.then(async (res) => {
|
||||
if (res.status === 200) {
|
||||
let data = res.data;
|
||||
detailsData.value = data.user_info;
|
||||
spinShow.value = false;
|
||||
} else {
|
||||
spinShow.value = false;
|
||||
ElMessage.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
spinShow.value = false;
|
||||
ElMessage.error(res.msg);
|
||||
});
|
||||
}
|
||||
// 列表
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
extractlistApi(Ids.value, formValidate.value)
|
||||
.then(async (res) => {
|
||||
let data = res.data;
|
||||
tabList.value = data.data;
|
||||
total.value = data.count;
|
||||
loading.value = false;
|
||||
})
|
||||
.catch((res) => {
|
||||
loading.value = false;
|
||||
ElMessage.error(res.msg);
|
||||
});
|
||||
}
|
||||
// 搜索
|
||||
function userSearchs() {
|
||||
formValidate.value.page = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
// 供父组件通过 $refs 调用(原 Options API 方法默认公开,script setup 需显式暴露)
|
||||
defineExpose({ modals, getDetails, getList });
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.table {
|
||||
.ivu-table-default {
|
||||
overflow-y: auto;
|
||||
max-height: 350px;
|
||||
}
|
||||
}
|
||||
.dashboard-workplace {
|
||||
&-header {
|
||||
&-avatar {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
margin-right: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&-tip {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
&-title {
|
||||
font-size: 13px;
|
||||
color: #000000;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
&-desc {
|
||||
&-sp {
|
||||
width: 33.33%;
|
||||
color: var(--prev-color-text-regular);
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-extra {
|
||||
.ivu-col {
|
||||
p {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
p:first-child {
|
||||
span:first-child {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
span:last-child {
|
||||
color: #808695;
|
||||
}
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,168 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card :bordered="false" shadow="never" :body-style="{ padding: 0 }">
|
||||
<div class="padding-add">
|
||||
<el-form ref="formRef" :label-width="labelWidth" label-position="right" inline @submit.prevent>
|
||||
<el-form-item label="昵称/ID:">
|
||||
<el-input placeholder="请输入" v-model="formValidate.nickname" clearable class="form_content_width" />
|
||||
</el-form-item>
|
||||
<el-form-item label="佣金范围:" class="tab_data">
|
||||
<el-input-number :controls="false" :min="0" class="mr10" v-model="formValidate.price_min" />
|
||||
<span class="mr10">一</span>
|
||||
<el-input-number :controls="false" :min="0" v-model="formValidate.price_max" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" v-db-click @click="userSearchs">查询</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card :bordered="false" shadow="never" class="mt16">
|
||||
<el-button v-auth="['export-userCommission']" class="export" v-db-click @click="exports">导出</el-button>
|
||||
<el-table
|
||||
ref="table"
|
||||
:data="tabList"
|
||||
v-loading="loading"
|
||||
empty-text="暂无数据"
|
||||
@on-sort-change="sortChanged"
|
||||
class="mt14"
|
||||
>
|
||||
<el-table-column label="用户信息" min-width="100">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.nickname }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总佣金金额" min-width="100">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.sum_number }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="账户余额" min-width="100">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.now_money }}</span>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="账户佣金" min-width="100">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.brokerage_price }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提现佣金" min-width="100">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.extract_price }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="acea-row row-right page">
|
||||
<pagination
|
||||
v-if="total"
|
||||
:total="total"
|
||||
v-model:page="formValidate.page"
|
||||
v-model:limit="formValidate.limit"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
<commission-details ref="commission"></commission-details>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useMediaStore } from '@/store/module/media';
|
||||
import { commissionListApi, userCommissionApi } from '@/api/finance';
|
||||
import commissionDetails from './handle/commissionDetails';
|
||||
|
||||
defineOptions({ name: 'commissionRecord' });
|
||||
|
||||
const { isMobile } = storeToRefs(useMediaStore());
|
||||
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const tabList = ref([]);
|
||||
const formValidate = ref({
|
||||
nickname: '',
|
||||
price_max: undefined,
|
||||
price_min: undefined,
|
||||
excel: 0,
|
||||
page: 1, // 当前页
|
||||
limit: 15, // 每页显示条数
|
||||
});
|
||||
const commission = ref(null);
|
||||
|
||||
const labelWidth = computed(() => {
|
||||
return isMobile.value ? undefined : '80px';
|
||||
});
|
||||
const labelPosition = computed(() => {
|
||||
return isMobile.value ? 'top' : 'right';
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
// 列表
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
commissionListApi(formValidate.value)
|
||||
.then(async (res) => {
|
||||
let data = res.data;
|
||||
tabList.value = data.list;
|
||||
total.value = data.count;
|
||||
loading.value = false;
|
||||
})
|
||||
.catch((res) => {
|
||||
loading.value = false;
|
||||
ElMessage.error(res.msg);
|
||||
});
|
||||
}
|
||||
// 搜索
|
||||
function userSearchs() {
|
||||
formValidate.value.page = 1;
|
||||
getList();
|
||||
}
|
||||
// 导出
|
||||
function exports() {
|
||||
let data = {
|
||||
price_max: formValidate.value.price_max,
|
||||
price_min: formValidate.value.price_min,
|
||||
nickname: formValidate.value.nickname,
|
||||
};
|
||||
userCommissionApi(data)
|
||||
.then((res) => {
|
||||
location.href = res.data[0];
|
||||
})
|
||||
.catch((res) => {
|
||||
ElMessage.error(res.msg);
|
||||
});
|
||||
}
|
||||
// 详情
|
||||
function Info(row) {
|
||||
commission.value.modals = true;
|
||||
commission.value.getDetails(row.uid);
|
||||
commission.value.getList(row.uid);
|
||||
}
|
||||
// 排序
|
||||
function sortChanged(e) {
|
||||
if (e.key == 'sum_number') {
|
||||
delete formValidate.value.brokerage_price;
|
||||
} else {
|
||||
delete formValidate.value.sum_number;
|
||||
}
|
||||
formValidate.value[e.key] = e.order;
|
||||
getList();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.lines {
|
||||
padding-top: 6px !important;
|
||||
}
|
||||
.tabform .export {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.tab_data :deep(.ivu-form-item-content) {
|
||||
display: flex !important;
|
||||
}
|
||||
</style>
|
||||
@ -1,453 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card :bordered="false" shadow="never" class="ivu-mb-16" :body-style="{ padding: 0 }">
|
||||
<div class="padding-add">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formValidate"
|
||||
:label-width="labelWidth"
|
||||
label-position="right"
|
||||
inline
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item label="时间选择:">
|
||||
<date-range-picker
|
||||
width="250px"
|
||||
clearable
|
||||
v-model="timeVal"
|
||||
:editable="false"
|
||||
@change="onchangeTime"
|
||||
format="YYYY/MM/DD"
|
||||
value-format="YYYY/MM/DD"
|
||||
class="mr20"
|
||||
></date-range-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="提现状态:">
|
||||
<el-select
|
||||
clearable
|
||||
v-model="formValidate.status"
|
||||
placeholder="请选择状态"
|
||||
@change="selChange"
|
||||
class="form_content_width"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, index) in treeData.withdrawal"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
:label="item.title"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="提现方式:">
|
||||
<el-select
|
||||
clearable
|
||||
v-model="formValidate.extract_type"
|
||||
placeholder="请选择状态"
|
||||
@change="selChange"
|
||||
class="form_content_width"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, index) in treeData.payment"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
:label="item.title"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="搜索:">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="微信昵称/姓名/支付宝账号/银行卡号"
|
||||
v-model="formValidate.nireid"
|
||||
class="form_content_width"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" v-db-click @click="selChange">查询</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-card>
|
||||
<cards-data :cardLists="cardLists" v-if="extractStatistics"></cards-data>
|
||||
<el-card :bordered="false" shadow="never">
|
||||
<el-table ref="table" :data="tabList" v-loading="loading" empty-text="暂无数据" class="mt14">
|
||||
<el-table-column label="ID" width="80">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.id }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="用户信息" min-width="130">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
用户昵称: {{ scope.row.nickname }} <br />
|
||||
用户id:{{ scope.row.uid }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提现金额" min-width="100">
|
||||
<template #default="scope">
|
||||
<div>{{ scope.row.extract_price }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提现手续费" min-width="100">
|
||||
<template #default="scope">
|
||||
<div>{{ scope.row.extract_fee }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="到账金额" min-width="100">
|
||||
<template #default="scope">
|
||||
<div class="f-price">{{ scope.row.receive_price }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提现方式" min-width="130">
|
||||
<template #default="scope">
|
||||
<div class="type" v-if="scope.row.extract_type === 'bank'">
|
||||
<div class="item">姓名:{{ scope.row.real_name }}</div>
|
||||
<div class="item">银行卡号:{{ scope.row.bank_code }}</div>
|
||||
<div class="item">银行开户地址:{{ scope.row.bank_address }}</div>
|
||||
</div>
|
||||
<div class="type" v-if="scope.row.extract_type === 'weixin'">
|
||||
<div class="item">昵称:{{ scope.row.nickname }}</div>
|
||||
<div class="item">微信号:{{ scope.row.wechat }}</div>
|
||||
</div>
|
||||
<div class="type" v-if="scope.row.extract_type === 'alipay'">
|
||||
<div class="item">姓名:{{ scope.row.real_name }}</div>
|
||||
<div class="item">支付宝号:{{ scope.row.alipay_code }}</div>
|
||||
</div>
|
||||
<div class="type" v-if="scope.row.extract_type === 'balance'">
|
||||
<div class="item">姓名:{{ scope.row.real_name }}</div>
|
||||
<div class="item">提现方式:佣金转入余额</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收款码" min-width="90">
|
||||
<template #default="scope">
|
||||
<div
|
||||
class="tabBox_img"
|
||||
v-viewer
|
||||
v-if="scope.row.extract_type === 'weixin' || scope.row.extract_type === 'alipay'"
|
||||
>
|
||||
<img v-lazy="scope.row.qrcode_url" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="申请时间" min-width="130">
|
||||
<template #default="scope">
|
||||
<span>{{ $filters.formatDate(scope.row.add_time) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="130">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.mark }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核状态" min-width="130">
|
||||
<template #default="scope">
|
||||
<div class="status" v-if="scope.row.status === 0">
|
||||
<div class="statusVal">申请中</div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="statusVal" v-if="scope.row.status === 1">提现通过</div>
|
||||
<div class="statusVal" v-if="scope.row.status === -1">
|
||||
提现未通过<br />未通过原因:{{ scope.row.fail_msg }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="170">
|
||||
<!-- v-if 须在插槽作用域内判断:scope 是插槽 prop,写在 template 外层时
|
||||
不存在(Vue2 slot-scope 可行,Vue3 #default 编译后 v-if 落在作用域外) -->
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.status == 0">
|
||||
<a href="javascript:void(0);" v-db-click @click="edit(scope.row)">编辑</a>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<a class="item" v-db-click @click="adopt(scope.row, '审核通过', scope.$index)">通过</a>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<a class="item" v-db-click @click="invalid(scope.row)">驳回</a>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="acea-row row-right page">
|
||||
<pagination
|
||||
v-if="total"
|
||||
:total="total"
|
||||
v-model:page="formValidate.page"
|
||||
v-model:limit="formValidate.limit"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 编辑表单-->
|
||||
<edit-from ref="edits" :FromData="FromData" @submitFail="submitFail"></edit-from>
|
||||
<!-- 拒绝通过-->
|
||||
<el-dialog v-model="modals" title="未通过原因" :close-on-click-modal="false" width="540px">
|
||||
<el-input v-model="fail_msg.message" type="textarea" :rows="4" placeholder="请输入未通过原因" />
|
||||
<template #footer
|
||||
><div>
|
||||
<el-button type="primary" v-db-click @click="oks">确定</el-button>
|
||||
</div></template
|
||||
>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useMediaStore } from '@/store/module/media';
|
||||
import cardsData from '@/components/cards/cards';
|
||||
import editFrom from '@/components/from/from';
|
||||
import { cashListApi, cashEditApi, refuseApi } from '@/api/finance';
|
||||
import { modalSure } from '@/utils/public';
|
||||
import { authLapse } from '@/utils/authLapse';
|
||||
import Setting from '@/setting';
|
||||
|
||||
defineOptions({ name: 'cashApply' });
|
||||
|
||||
const route = useRoute();
|
||||
const { isMobile } = storeToRefs(useMediaStore());
|
||||
|
||||
const edits = ref(null);
|
||||
const images = ref(['1.jpg', '2.jpg']);
|
||||
const modal_loading = ref(false);
|
||||
const fail_msg = ref({
|
||||
message: '输入信息不完整或有误!',
|
||||
});
|
||||
const modals = ref(false);
|
||||
const total = ref(0);
|
||||
const cardLists = ref([]);
|
||||
const loading = ref(false);
|
||||
const tabList = ref([]);
|
||||
const treeData = ref({
|
||||
withdrawal: [
|
||||
{
|
||||
title: '全部',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
title: '未通过',
|
||||
value: -1,
|
||||
},
|
||||
{
|
||||
title: '申请中',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
title: '已通过',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
payment: [
|
||||
{
|
||||
title: '全部',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
title: '微信',
|
||||
value: 'wx',
|
||||
},
|
||||
{
|
||||
title: '支付宝',
|
||||
value: 'alipay',
|
||||
},
|
||||
{
|
||||
title: '银行卡',
|
||||
value: 'bank',
|
||||
},
|
||||
],
|
||||
});
|
||||
const formValidate = ref({
|
||||
status: '',
|
||||
extract_type: '',
|
||||
nireid: '',
|
||||
data: '',
|
||||
page: 1,
|
||||
limit: 15,
|
||||
});
|
||||
const extractStatistics = ref({});
|
||||
const timeVal = ref([]);
|
||||
const FromData = ref(null);
|
||||
const extractId = ref(0);
|
||||
|
||||
watch(
|
||||
() => route.fullPath,
|
||||
() => {
|
||||
if (route.fullPath === Setting.routePre + '/finance/user_extract/index?status=0') {
|
||||
getPath();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const labelWidth = computed(() => {
|
||||
return isMobile.value ? undefined : '80px';
|
||||
});
|
||||
const labelPosition = computed(() => {
|
||||
return isMobile.value ? 'top' : 'right';
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (route.fullPath === Setting.routePre + '/finance/user_extract/index?status=0') {
|
||||
getPath();
|
||||
} else {
|
||||
getList();
|
||||
}
|
||||
});
|
||||
|
||||
function getPath() {
|
||||
formValidate.value.page = 1;
|
||||
formValidate.value.status = parseInt(route.query.status);
|
||||
getList();
|
||||
}
|
||||
// 无效
|
||||
function invalid(row) {
|
||||
extractId.value = row.id;
|
||||
modals.value = true;
|
||||
}
|
||||
// 确定
|
||||
function oks() {
|
||||
modal_loading.value = true;
|
||||
refuseApi(extractId.value, fail_msg.value)
|
||||
.then(async (res) => {
|
||||
ElMessage.success(res.msg);
|
||||
modal_loading.value = false;
|
||||
modals.value = false;
|
||||
getList();
|
||||
})
|
||||
.catch((res) => {
|
||||
ElMessage.error(res.msg);
|
||||
});
|
||||
}
|
||||
// 通过
|
||||
function adopt(row, tit, num) {
|
||||
let delfromData = {
|
||||
title: tit,
|
||||
num: num,
|
||||
url: `finance/extract/adopt/${row.id}`,
|
||||
method: 'put',
|
||||
ids: '',
|
||||
};
|
||||
modalSure(delfromData)
|
||||
.then((res) => {
|
||||
ElMessage.success(res.msg);
|
||||
getList();
|
||||
})
|
||||
.catch((res) => {
|
||||
ElMessage.error(res.msg);
|
||||
});
|
||||
}
|
||||
// 具体日期
|
||||
function onchangeTime(e) {
|
||||
timeVal.value = e;
|
||||
formValidate.value.data = timeVal.value ? timeVal.value.join('-') : '';
|
||||
formValidate.value.page = 1;
|
||||
getList();
|
||||
}
|
||||
// 选择时间
|
||||
function selectChange(tab) {
|
||||
formValidate.value.page = 1;
|
||||
formValidate.value.data = tab;
|
||||
timeVal.value = [];
|
||||
getList();
|
||||
}
|
||||
// 选择
|
||||
function selChange() {
|
||||
formValidate.value.page = 1;
|
||||
getList();
|
||||
}
|
||||
// 列表
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
cashListApi(formValidate.value)
|
||||
.then(async (res) => {
|
||||
let data = res.data;
|
||||
tabList.value = data.list.list;
|
||||
total.value = data.list.count;
|
||||
extractStatistics.value = data.extract_statistics;
|
||||
cardLists.value = [
|
||||
{
|
||||
col: 6,
|
||||
count: extractStatistics.value.brokerage_count,
|
||||
name: '佣金总金额',
|
||||
className: 'iconyuezhifujine',
|
||||
},
|
||||
{ col: 6, count: extractStatistics.value.price, name: '待提现金额', className: 'iconfufeihuiyuanjine' },
|
||||
{ col: 6, count: extractStatistics.value.priced, name: '已提现金额', className: 'iconzhifuyongjinjine' },
|
||||
{
|
||||
col: 6,
|
||||
count: extractStatistics.value.brokerage_not,
|
||||
name: '未提现金额',
|
||||
className: 'iconshangpintuikuanjine',
|
||||
},
|
||||
];
|
||||
loading.value = false;
|
||||
})
|
||||
.catch((res) => {
|
||||
loading.value = false;
|
||||
ElMessage.error(res.msg);
|
||||
});
|
||||
}
|
||||
// 编辑
|
||||
function edit(row) {
|
||||
cashEditApi(row.id)
|
||||
.then(async (res) => {
|
||||
if (res.data.status === false) {
|
||||
return authLapse(res.data);
|
||||
}
|
||||
FromData.value = res.data;
|
||||
edits.value.modals = true;
|
||||
})
|
||||
.catch((res) => {
|
||||
ElMessage.error(res.msg);
|
||||
});
|
||||
}
|
||||
// 编辑提交成功
|
||||
function submitFail() {
|
||||
// getList();
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ivu-mt .type .item {
|
||||
margin: 3px 0;
|
||||
}
|
||||
.tabform {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.Refresh {
|
||||
font-size: 12px;
|
||||
color: var(--prev-color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
.ivu-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.status :deep(.item ~ .item) {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.status :deep(.statusVal) {
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
/*.ivu-mt :deep(.ivu-table-header*/
|
||||
/* border-top:1px dashed #ddd!important*/
|
||||
:deep(.type) {
|
||||
padding: 3px 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.tabBox_img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.z-price {
|
||||
color: red;
|
||||
}
|
||||
.f-price {
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
@ -25,14 +25,11 @@
|
||||
<div>选定条件下,用户在线下扫码支付的金额</div>
|
||||
<br />
|
||||
<div>支出金额</div>
|
||||
<div>余额支付金额、支付佣金金额、商品退款金额</div>
|
||||
<div>余额支付金额、商品退款金额</div>
|
||||
<br />
|
||||
<div>余额支付金额</div>
|
||||
<div>用户下单时使用余额实际支付的金额</div>
|
||||
<br />
|
||||
<div>佣金支付金额</div>
|
||||
<div>后台给推广员支付的推广佣金,以实际支付为准</div>
|
||||
<br />
|
||||
<div>商品退款金额</div>
|
||||
<div>用户成功退款的商品金额</div>
|
||||
</div></template
|
||||
|
||||
@ -50,10 +50,6 @@
|
||||
<div class="section">
|
||||
<div class="section-hd">用户概况</div>
|
||||
<div class="section-bd">
|
||||
<div class="item">
|
||||
<div>推广资格:</div>
|
||||
<div class="value">{{ psInfo.spread_open ? '开启' : '关闭' }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div>用户状态:</div>
|
||||
<div class="value">{{ psInfo.status ? '开启' : '锁定' }}</div>
|
||||
|
||||
@ -37,17 +37,6 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<template v-if="collapse">
|
||||
<el-form-item v-if="distributionEnabled" label="分销等级:">
|
||||
<el-select v-model="agent_level" placeholder="请选择分销等级" clearable class="form_content_width">
|
||||
<el-option value="all" label="全部"></el-option>
|
||||
<el-option
|
||||
:value="item.grade"
|
||||
v-for="(item, index) in membershipList"
|
||||
:key="index"
|
||||
:label="item.name"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户标签:" label-for="label_id">
|
||||
<div class="labelInput acea-row row-between-wrapper" v-db-click @click="openSelectLabel">
|
||||
<div style="width: 222px">
|
||||
@ -481,14 +470,12 @@ const userFrom = ref({
|
||||
limit: 15,
|
||||
level: '',
|
||||
group_id: '',
|
||||
agent_level: '',
|
||||
field_key: '',
|
||||
});
|
||||
const before_pay_time = ref([]);
|
||||
const field_key = ref('');
|
||||
const level = ref('');
|
||||
const group_id = ref('');
|
||||
const agent_level = ref('');
|
||||
const label_id = ref('');
|
||||
const user_time_type = ref('');
|
||||
const pay_count = ref('');
|
||||
@ -891,7 +878,6 @@ function getList() {
|
||||
userFrom.value.field_key = field_key.value === 'all' ? '' : field_key.value;
|
||||
userFrom.value.level = level.value === 'all' ? '' : level.value;
|
||||
userFrom.value.group_id = group_id.value === 'all' ? '' : group_id.value;
|
||||
userFrom.value.agent_level = agent_level.value === 'all' ? '' : agent_level.value;
|
||||
loading.value = true;
|
||||
userList(userFrom.value)
|
||||
.then(async (res) => {
|
||||
@ -925,7 +911,6 @@ async function exportList() {
|
||||
userFrom.value.field_key = field_key.value === 'all' ? '' : field_key.value;
|
||||
userFrom.value.level = level.value === 'all' ? '' : level.value;
|
||||
userFrom.value.group_id = group_id.value === 'all' ? '' : group_id.value;
|
||||
userFrom.value.agent_level = agent_level.value === 'all' ? '' : agent_level.value;
|
||||
let [th, filekey, data, fileName] = [[], [], [], ''];
|
||||
// let fileName = "";
|
||||
let excelData = JSON.parse(JSON.stringify(userFrom.value));
|
||||
@ -992,13 +977,11 @@ function reset(name) {
|
||||
limit: 15,
|
||||
level: '',
|
||||
group_id: '',
|
||||
agent_level: '',
|
||||
field_key: '',
|
||||
};
|
||||
field_key.value = '';
|
||||
level.value = '';
|
||||
group_id.value = '';
|
||||
agent_level.value = '';
|
||||
dataLabel.value = [];
|
||||
selectDataLabel.value = [];
|
||||
user_time_type.value = '';
|
||||
|
||||
@ -44,15 +44,6 @@ export default {
|
||||
},
|
||||
component: () => import('@/pages/finance/capitalFlow/index'),
|
||||
},
|
||||
{
|
||||
path: 'user_extract/index',
|
||||
name: `${pre}cashApply`,
|
||||
meta: {
|
||||
auth: ['finance-user_extract'],
|
||||
title: '提现申请',
|
||||
},
|
||||
component: () => import('@/pages/finance/userExtract/index'),
|
||||
},
|
||||
{
|
||||
path: 'user_recharge/index',
|
||||
name: `${pre}recharge`,
|
||||
@ -71,15 +62,6 @@ export default {
|
||||
},
|
||||
component: () => import('@/pages/finance/financialRecords/bill'),
|
||||
},
|
||||
{
|
||||
path: 'finance/commission',
|
||||
name: `${pre}commissionRecord`,
|
||||
meta: {
|
||||
auth: ['finance-finance-commission'],
|
||||
title: '佣金记录',
|
||||
},
|
||||
component: () => import('@/pages/finance/commission/index'),
|
||||
},
|
||||
{
|
||||
path: 'balance/balance',
|
||||
name: `${pre}balance`,
|
||||
|
||||
@ -101,13 +101,14 @@
|
||||
@goBuy="goBuy"
|
||||
@share="listenerActionSheet"
|
||||
></productBottom>
|
||||
<shareRedPackets
|
||||
<!-- 分销佣金悬浮窗已随主程序分销功能下线(后端不再返回 priceName),如启用分销插件需恢复 -->
|
||||
<!-- <shareRedPackets
|
||||
:sharePacket="sharePacket"
|
||||
@listenerActionSheet="listenerActionSheet"
|
||||
@closeChange="closeChange"
|
||||
:showAnimate="showAnimate"
|
||||
@boxStatus="boxStatus"
|
||||
></shareRedPackets>
|
||||
></shareRedPackets> -->
|
||||
<!-- 组件 -->
|
||||
<productWindow
|
||||
:attr="attr"
|
||||
@ -553,7 +554,7 @@ const isShowAuth = ref(false); //是否隐藏授权
|
||||
const isOpen = ref(false); //是否打开属性组件
|
||||
const posterbackgd = ref("/static/images/posterbackgd.png");
|
||||
const sharePacket = reactive({
|
||||
isState: true, //默认不显示
|
||||
isState: false, // 分销佣金已下线,默认不显示
|
||||
}); //分销商详细
|
||||
const uid = ref(0); //用户uid
|
||||
const good_list = ref([]);
|
||||
@ -937,7 +938,7 @@ function appShare(scene) {
|
||||
}
|
||||
// #endif
|
||||
function closeChange() {
|
||||
sharePacket["isState"] = true;
|
||||
sharePacket["isState"] = false;
|
||||
}
|
||||
function boxStatus(data) {
|
||||
showAnimate.value = data;
|
||||
@ -1151,8 +1152,8 @@ function getGoodsDetails() {
|
||||
attr["productAttr"] = res.data.productAttr;
|
||||
productValue.value = res.data.productValue;
|
||||
is_vip.value = res.data.storeInfo.is_vip;
|
||||
sharePacket["priceName"] = res.data.priceName;
|
||||
sharePacket["isState"] = res.data.priceName != 0 ? true : false;
|
||||
// 后端已随分销下线不再返回 priceName;undefined != 0 恒真会导致佣金悬浮窗误显示,直接关闭
|
||||
sharePacket["isState"] = false;
|
||||
storeSelfMention.value = res.data.store_self_mention;
|
||||
good_list.value = goodList;
|
||||
// 透传后端 display_sections(产品类型/营销监听器输出的展示块)
|
||||
|
||||
@ -980,7 +980,6 @@ function getDisplayText(item, dataItem) {
|
||||
if (field === "order_num") return dataItem.order_num;
|
||||
if (field === "integral") return dataItem.integral;
|
||||
if (field === "now_money") return dataItem.now_money;
|
||||
if (field === "brokerage_price") return dataItem.brokerage_price;
|
||||
if (field === "unread_msg_num") return dataItem.unread_msg_num;
|
||||
if (field === "mobile") return dataItem.mobile;
|
||||
if (field === "add_time") return dataItem.add_time;
|
||||
|
||||
@ -1211,7 +1211,6 @@ const dataList = computed(() => {
|
||||
{ id: 2, name: "积分", key: "integral" },
|
||||
{ id: 5, name: "收藏商品", key: "collection" },
|
||||
{ id: 6, name: "浏览记录", key: "visit" },
|
||||
{ id: 8, name: "推广佣金", key: "brokerage" },
|
||||
{ id: 9, name: "推广人", key: "spreadCount" },
|
||||
{ id: 10, name: "推广订单", key: "spreadOrderCount" },
|
||||
];
|
||||
@ -1245,8 +1244,6 @@ function getDataValue(key) {
|
||||
if (key === "integral") return u.integral || 0;
|
||||
if (key === "collection") return u.collectCount || u.collect_count || 0;
|
||||
if (key === "visit") return u.visitCount || u.visit_num || 0;
|
||||
if (key === "brokerage")
|
||||
return u.brokerage_price || u.brokeragePrice || u.brokerage || 0;
|
||||
if (key === "spreadCount") return u.spread_count || u.spreadCount || 0;
|
||||
if (key === "spreadOrderCount")
|
||||
return u.order_count || u.spread_order_count || u.spreadOrderCount || 0;
|
||||
|
||||
@ -335,49 +335,22 @@ erDiagram
|
||||
|---|---|
|
||||
| 实物 | `eb_pt_normal`、`eb_pt_normal_sku`、`eb_pt_normal_order` |
|
||||
| 网盘 | `eb_pt_virtual`、`eb_pt_virtual_sku`、`eb_pt_virtual_order` |
|
||||
| 卡密 | `eb_pt_cardkey`、`eb_pt_cardkey_stock`、`eb_pt_cardkey_order` |
|
||||
|
||||
产品扩展表通常通过商品中台 ID 或具体订单号关联共享表。日期库存、卡密库存等高并发资源必须有唯一索引或条件更新策略。
|
||||
产品扩展表通常通过商品中台 ID 或具体订单号关联共享表。日期库存等高并发资源必须有唯一索引或条件更新策略。
|
||||
|
||||
### 5.2 交易模式
|
||||
其他可安装模块(交易模式、分销代理、营销活动、客服、开放平台、文件管理等)的扩展表不在本文收录,随各自插件目录维护(`modules/{domain}/{name}/README.md`)。
|
||||
|
||||
### 5.2 渠道与营销(默认安装)
|
||||
|
||||
| 模块 | 主要表 |
|
||||
|---|---|
|
||||
| 砍价 | `eb_store_bargain`、`eb_store_bargain_user`、`eb_store_bargain_user_help` |
|
||||
| 拼团 | `eb_store_combination`、`eb_store_pink` |
|
||||
| 积分商城 | `eb_store_integral`、`eb_store_integral_order`、`eb_store_integral_order_status` |
|
||||
| 秒杀 | `eb_store_activity`、`eb_store_seckill`、`eb_activity_seckill_sku`、`eb_store_seckill_time` |
|
||||
|
||||
### 5.3 分销代理
|
||||
|
||||
主要表包括:
|
||||
|
||||
- `eb_distribution_user`、`eb_distribution_relation`、`eb_distribution_relation_log`;
|
||||
- `eb_distribution_level`、`eb_distribution_level_task`、`eb_distribution_level_task_record`;
|
||||
- `eb_distribution_order`、`eb_distribution_brokerage`、`eb_distribution_brokerage_frozen`、`eb_distribution_extract`;
|
||||
- `eb_distribution_admin_scope`;
|
||||
- `eb_division_agent`、`eb_division_agent_apply`、`eb_spread_apply` 等兼容/扩展表。
|
||||
|
||||
分销订单表保存成交时推广链和佣金快照,后续用户关系变化不能修改历史快照。
|
||||
|
||||
### 5.4 渠道与营销
|
||||
|
||||
| 模块 | 主要表 |
|
||||
|---|---|
|
||||
| 微信公众号 | `eb_wechat_user`、`eb_wechat_message`、`eb_wechat_reply`、`eb_wechat_key`、`eb_wechat_media`、`eb_wechat_news_category` |
|
||||
| 微信小程序 | `eb_routine_scheme`、`eb_live_anchor`、`eb_live_goods`、`eb_live_room`、`eb_live_room_goods` |
|
||||
| App | `eb_app_version` |
|
||||
| 渠道码 | `eb_wechat_qrcode`、`eb_wechat_qrcode_cate`、`eb_wechat_qrcode_record` |
|
||||
| 抽奖 | `eb_luck_lottery`、`eb_luck_prize`、`eb_luck_lottery_record` |
|
||||
| 签到 | `eb_system_sign_reward`、`eb_user_sign` |
|
||||
|
||||
### 5.5 服务与系统模块
|
||||
### 5.3 服务与系统模块(默认安装)
|
||||
|
||||
| 模块 | 主要表 |
|
||||
|---|---|
|
||||
| 客服 | `eb_store_service`、`eb_store_service_log`、`eb_store_service_record`、`eb_store_service_speechcraft`、`eb_store_service_auto_reply`、`eb_store_service_feedback` |
|
||||
| 开放平台 | `eb_out_account`、`eb_out_interface` |
|
||||
| 文件管理 | `eb_system_file`、`eb_system_file_info` |
|
||||
| AI Agent | `eb_ai_agent_conversation`、`eb_ai_agent_message`、`eb_ai_agent_task`、`eb_ai_agent_task_run`、`eb_ai_mcp_server` |
|
||||
|
||||
独立 MCP 服务主要维护系统配置、菜单与工具注册,不单独创建业务数据表;服务密钥不应以明文出现在文档和日志中。
|
||||
@ -399,7 +372,6 @@ erDiagram
|
||||
- 支付回调/退款回调的外部交易号。
|
||||
- 模块 `module_key` 的有效安装记录。
|
||||
- 产品类型 `source_type` 和订单来源标识。
|
||||
- 卡密库存中的卡号或唯一资源标识。
|
||||
- 按日期/场次等维度管理库存时的复合唯一键。
|
||||
- 优惠券用户领取记录的业务唯一条件。
|
||||
|
||||
@ -423,7 +395,6 @@ erDiagram
|
||||
- 用户资产汇总与对应账单写入。
|
||||
- 库存扣减与占用记录。
|
||||
- 售后主表与售后商品明细。
|
||||
- 分销订单快照与佣金账单。
|
||||
|
||||
外部支付、短信、对象存储等网络调用不应持有长数据库事务。可采用“本地状态提交 -> 外部调用 -> 回调/查询补偿”的方式。
|
||||
|
||||
|
||||
@ -51,6 +51,8 @@
|
||||
|
||||
其他模块和驱动在后台按需安装。默认驱动安装会写生命周期日志和驱动字典,但商户、Bucket、短信模板、机器人 Webhook 或打印机凭据仍需单独配置,不能据此认定服务已经可用。
|
||||
|
||||
> 收录边界:本文档只展开核心能力与默认安装清单内的模块/驱动;其余可安装模块(分销、交易模式、营销活动、客服、开放平台、文件管理等)的清单计入总数,功能、接口与数据表说明随各自插件目录维护(`modules/{domain}/{name}/README.md`)。
|
||||
|
||||
## 2. 模块状态模型
|
||||
|
||||
一个模块可能处于以下状态:
|
||||
@ -66,48 +68,31 @@
|
||||
|
||||
## 3. 模块清单
|
||||
|
||||
### 3.1 分销代理
|
||||
|
||||
| key | 名称 | 类型 | 目录 |
|
||||
|---|---|---|---|
|
||||
| `distribution` | 分销代理 | `distribution` | `modules/distribution` |
|
||||
|
||||
主要能力:分销员、分销等级、等级任务、推广关系、佣金订单快照、佣金冻结/解冻、提现、事业部和管理员数据范围。
|
||||
|
||||
主要扩展表包括 `distribution_user`、`distribution_relation`、`distribution_order`、`distribution_brokerage`、`distribution_extract`、`division_agent` 等。该模块同时提供管理端和用户端路由、队列任务和自动解绑定时任务。
|
||||
|
||||
### 3.2 用户渠道
|
||||
### 3.1 用户渠道
|
||||
|
||||
| key | 名称 | 目录 | 主要能力 |
|
||||
|---|---|---|---|
|
||||
| `channel/app` | APP | `modules/channel/app` | App 渠道识别、版本管理 |
|
||||
| `channel/h5` | H5 浏览器 | `modules/channel/h5` | H5 渠道识别与 OAuth 协作 |
|
||||
| `channel/wechat_mini` | 微信小程序 | `modules/channel/wechat_mini` | 小程序登录、订阅消息、Scheme、小程序码与直播 |
|
||||
| `channel/wechat_mp` | 微信公众号 | `modules/channel/wechat_mp` | OAuth、模板消息、菜单、素材、回复、粉丝 |
|
||||
|
||||
渠道模块负责“用户从哪里来、如何认证、如何推送”,不应把商品或交易规则放入渠道模块。
|
||||
|
||||
微信小程序模块创建/管理 `routine_scheme` 和直播相关表;微信公众号模块创建微信用户、消息、回复、素材和关键字相关表。
|
||||
微信小程序模块创建/管理 `routine_scheme` 和直播相关表。
|
||||
|
||||
### 3.3 营销运营
|
||||
### 3.2 营销运营
|
||||
|
||||
| key | 名称 | 目录 | 主要能力 |
|
||||
|---|---|---|---|
|
||||
| `user_channel_code` | 渠道码 | `modules/marketing/channel_code` | 二维码、分类、扫码记录与统计 |
|
||||
| `activity_lottery` | 抽奖活动 | `modules/marketing/lottery` | 活动、奖品、抽奖记录,多种展示形式 |
|
||||
| `marketing_newuser` | 新人礼 | `modules/marketing/newuser` | 注册后赠余额、积分、优惠券 |
|
||||
| `user_recharge` | 用户充值 | `modules/marketing/recharge` | 充值配置、充值记录和退款 |
|
||||
| `user_signin` | 签到活动 | `modules/marketing/signin` | 签到奖励、用户签到记录和提醒 |
|
||||
|
||||
营销模块原则上通过事件观察用户或订单行为并发放权益,不应修改核心成交契约。充值虽涉及支付,但使用独立充值流程,不注册商品交易模式。
|
||||
营销模块原则上通过事件观察用户或订单行为并发放权益,不应修改核心成交契约。
|
||||
|
||||
### 3.4 产品类型
|
||||
### 3.3 产品类型
|
||||
|
||||
| key | 名称 | 必需 | 产品类型 | 履约 | 物流 | 售后概览 |
|
||||
|---|---|:---:|---|---|:---:|---|
|
||||
| `product_normal` | 实物商品 | 是 | `normal` | 发货/配送/自提 | 是 | 退款、退货退款、换货、维修 |
|
||||
| `product_virtual` | 网盘产品 | 否 | `virtual` | 自动发放资源 | 否 | 以退款为主,受时限约束 |
|
||||
| `product_cardkey` | 卡密商品 | 否 | `cardkey` | 自动派发卡密 | 否 | 以退款为主,受时限约束 |
|
||||
|
||||
产品类型模块通过三类注册完成运行时接入:
|
||||
|
||||
@ -119,7 +104,7 @@
|
||||
|
||||
- 基础商品类型,清单标记 `required=true`。
|
||||
- 支持通用商品、SKU、物流、自提、订单和售后。
|
||||
- 可与普通、秒杀、拼团、砍价、积分等交易模式协作。
|
||||
- 可与已安装的各类交易模式协作。
|
||||
- 包含未支付取消和自动收货定时任务。
|
||||
|
||||
#### 网盘商品 `product_virtual`
|
||||
@ -129,48 +114,17 @@
|
||||
- 不需要物流,应限制可售后范围和时限。
|
||||
- 模块内包含管理端扩展页面构建产物/源码映射。
|
||||
|
||||
#### 卡密商品 `product_cardkey`
|
||||
|
||||
- 管理卡密库存并在支付后分配。
|
||||
- 履约操作必须保证并发下单时卡密唯一、重复回调不重复派发。
|
||||
- 退款时需明确已查看/已使用卡密的业务处理规则。
|
||||
|
||||
### 3.5 交易模式
|
||||
|
||||
| key | 名称 | 目录 | 定价/约束 | 订单形态 |
|
||||
|---|---|---|---|---|
|
||||
| `activity_bargain` | 砍价活动 | `modules/trade/bargain` | 助力递减、活动期限、底价 | 复用商品订单 |
|
||||
| `activity_combination` | 拼团活动 | `modules/trade/combination` | 拼团价、人数、成团时限 | 复用商品订单 |
|
||||
| `activity_integral` | 积分商城 | `modules/trade/integral` | 积分/现金混合、兑换限制 | 独立积分订单 |
|
||||
| `activity_seckill` | 秒杀活动 | `modules/trade/seckill` | 秒杀价、时段、限购、活动库存 | 复用商品订单 |
|
||||
|
||||
交易模式通过 `TradeModeRegistry` 注册处理器,参与定价、校验和订单生命周期。独立订单型模式还必须注册订单类型适配器。
|
||||
|
||||
### 3.6 服务模块
|
||||
### 3.4 服务模块
|
||||
|
||||
| key | 名称 | 目录 | 主要能力 |
|
||||
|---|---|---|---|
|
||||
| `kefu` | 在线客服 | `modules/service/kefu` | 客服账号、会话、话术、自动回复、反馈、实时通信 |
|
||||
| `outapi` | 开放平台对外接口 | `modules/service/outapi` | 第三方账号、令牌、权限、业务 API 和推送 |
|
||||
| `service_mcp` | MCP 服务 | `modules/service/mcp` | 独立 `/api/mcp` 工具入口、服务开关、密钥认证与事件扩展 |
|
||||
| `aiagent` | AI Agent | `modules/aiagent` | 后台会话、流式对话、自动化任务、MCP 连接与 Skill 管理 |
|
||||
|
||||
客服模块提供 `route/admin.php`、`route/user.php` 两类场景路由。工作台与游客组统一挂在 `/adminapi/kefu/`,客服 token 独立于管理员 token;实时服务通过统一 WebSocket 网关注册。
|
||||
|
||||
开放平台模块提供 `/api/outapi` 路由,第三方账号必须同时通过令牌认证和当前接口权限校验。
|
||||
|
||||
独立 MCP 使用 `mcp_appkey`,不复用开放平台的 `appid/appsecret`;接入和工具说明见 [MCP 接口](api/MCP接口.md)。AI Agent 的模型与工具配置均需单独验收权限和外部调用。
|
||||
|
||||
`aiagent` 提供后台 AI 对话工作台、模型/MCP/Skill 运行配置与无人值守自动化任务,依赖 `service_mcp` 的内置业务工具(软依赖);功能详述、数据表、接口与权限见 [AI Agent 模块](AI%20Agent%20模块.md)。
|
||||
|
||||
### 3.7 系统模块
|
||||
|
||||
| key | 名称 | 目录 | 主要能力 |
|
||||
|---|---|---|---|
|
||||
| `system_file_manage` | 文件管理 | `modules/system/file_manage` | 目录浏览、文件信息、编辑与校验 |
|
||||
|
||||
文件管理属于高权限能力,生产环境应只授权可信管理员,并限制可操作根目录、文件类型和编辑 token。
|
||||
|
||||
## 4. 驱动清单
|
||||
|
||||
### 4.1 支付(4)
|
||||
@ -286,7 +240,7 @@ modules/*/route/{scene}.php
|
||||
modules/*/*/route/{scene}.php
|
||||
```
|
||||
|
||||
主要场景为 `admin` 和 `user`。客服工作台组由模块 `admin.php` 加载,不再使用独立 `kefu.php` 场景。禁用模块的路由不会被加载。
|
||||
主要场景为 `admin` 和 `user`,各模块场景组由模块自身 `admin.php`/`user.php` 加载。禁用模块的路由不会被加载。
|
||||
|
||||
### 5.4 命令发现
|
||||
|
||||
|
||||
@ -4,11 +4,12 @@
|
||||
|
||||
## 1. 文档说明
|
||||
|
||||
本文件按用户角色和业务域描述系统功能。功能状态分为三层:
|
||||
本文件按用户角色和业务域描述系统功能。收录边界:核心能力(`backend/app`、`backend/crmeb`、`backend/driver`)与默认安装清单内的模块/驱动;其余可安装模块(分销、交易模式、营销活动、客服、开放平台、文件管理等)的说明随各自插件目录维护(`modules/{domain}/{name}/README.md`),不在本文档收录。
|
||||
|
||||
- 核心功能:代码位于 `backend/app`,一般随系统提供。
|
||||
- 模块功能:代码位于 `backend/modules`,需要安装并启用对应模块。
|
||||
- 驱动能力:代码位于 `backend/driver`,还需要配置对应服务商账号与参数。
|
||||
功能状态分为两层:
|
||||
|
||||
- 核心功能:代码位于 `backend/app`,随系统提供。
|
||||
- 默认安装模块:代码位于 `backend/modules`,`required=true` 或 `default_install=true`,新装自动初始化。
|
||||
|
||||
目录中存在源码只表示系统具备该能力,不代表当前数据库已经安装、后台已经开启或第三方账号已经可用。
|
||||
|
||||
@ -42,7 +43,6 @@
|
||||
|---|---|---|
|
||||
| 实物商品 | 通用商品与 SKU、物流/配送配置 | 物流、同城配送或门店自提 |
|
||||
| 网盘商品 | 资源链接、提取信息、虚拟 SKU | 支付后自动发放数字资源 |
|
||||
| 卡密商品 | 卡密库存导入、批次/库存维护 | 支付后派发卡号密码 |
|
||||
|
||||
### 2.3 订单管理
|
||||
|
||||
@ -76,7 +76,7 @@
|
||||
- 用户地址、发票、收藏、浏览、搜索与访问记录。
|
||||
- 用户注销申请和注销结果管理。
|
||||
- 会员信息、会员类型、权益、会员卡批次与卡片管理。
|
||||
- 用户推广关系和分销身份管理。
|
||||
- 用户推广关系管理。
|
||||
|
||||
### 2.6 会员体系
|
||||
|
||||
@ -99,36 +99,13 @@
|
||||
- 用户领取、后台发放和使用记录。
|
||||
- 下单可用券计算和优惠分摊。
|
||||
|
||||
可安装营销/交易活动:
|
||||
默认安装的营销活动:
|
||||
|
||||
| 功能 | 主要能力 |
|
||||
|---|---|
|
||||
| 秒杀 | 秒杀时段、活动、商品、SKU 库存、限购、参与记录与订单 |
|
||||
| 拼团 | 拼团商品、拼团价、人数、成团时限、团列表和到期处理 |
|
||||
| 砍价 | 砍价商品、活动时间、底价、用户发起、好友助力与统计 |
|
||||
| 积分商城 | 积分商品、积分/现金组合价、兑换限制、独立积分订单 |
|
||||
| 签到 | 连续/累计签到规则、奖励、记录和提醒 |
|
||||
| 抽奖 | 九宫格、大转盘、翻牌等活动、奖品与中奖记录 |
|
||||
| 新人礼 | 注册后赠送余额、积分或优惠券 |
|
||||
| 渠道码 | 公众号渠道二维码、分类、扫码记录与统计 |
|
||||
| 用户充值 | 充值配置、充值订单/记录、充值退款 |
|
||||
|
||||
### 2.8 分销与代理
|
||||
|
||||
分销代理模块提供:
|
||||
|
||||
- 分销员申请、审核、状态与资料管理。
|
||||
- 分销等级、升级任务和任务记录。
|
||||
- 用户推广关系、绑定/解绑及关系日志。
|
||||
- 分销订单佣金快照。
|
||||
- 佣金计算、冻结、解冻、账单与提现。
|
||||
- 管理员数据范围和代理业务权限。
|
||||
- 事业部、代理商、申请和统计相关能力。
|
||||
- 用户端推广码、推广海报、团队、订单和佣金排行。
|
||||
|
||||
佣金信息在订单发生时固化,历史订单不应依赖用户当前推广关系重新计算。
|
||||
|
||||
### 2.9 页面装修与内容
|
||||
### 2.8 页面装修与内容
|
||||
|
||||
- 商城 DIY 页面创建、编辑、复制、删除和发布。
|
||||
- 主题、主题组件、主题下载与一键换色。
|
||||
@ -138,7 +115,7 @@
|
||||
- 协议、版权、站点基础信息和分享配置。
|
||||
- 多语言类型、语言编码和翻译相关能力。
|
||||
|
||||
### 2.10 门店、配送与物流
|
||||
### 2.9 门店、配送与物流
|
||||
|
||||
- 门店列表、位置、营业信息和自提配置。
|
||||
- 店员/核销员绑定与权限校验。
|
||||
@ -149,19 +126,18 @@
|
||||
- 订单发货、物流轨迹和收货。
|
||||
- 云打印渠道、打印机和小票配置。
|
||||
|
||||
### 2.11 财务
|
||||
### 2.10 财务
|
||||
|
||||
- 资金流水和用户账单。
|
||||
- 余额充值、消费和退款记录。
|
||||
- 用户提现申请、审核和结果处理。
|
||||
- 分销佣金、冻结佣金与代理提现。
|
||||
- 订单支付、退款和资金节省记录。
|
||||
- 支付渠道与不同终端平台的关联配置。
|
||||
- 经营、订单、余额、积分和交易统计。
|
||||
|
||||
财务数据修改必须保留业务单号、来源类型、操作人和幂等标识,不应直接修改汇总余额而不写账单。
|
||||
|
||||
### 2.12 消息与通知
|
||||
### 2.11 消息与通知
|
||||
|
||||
- 站内信创建、列表、详情、已读和删除。
|
||||
- 通知主配置、渠道、模板和适用终端。
|
||||
@ -174,15 +150,7 @@
|
||||
|
||||
消息系统通过渠道注册中心扩展,新增渠道应实现统一消息渠道契约。
|
||||
|
||||
### 2.13 微信与多渠道
|
||||
|
||||
微信公众号:
|
||||
|
||||
- OAuth 登录和用户映射。
|
||||
- 粉丝同步、用户和标签相关能力。
|
||||
- 菜单配置、素材/图文管理。
|
||||
- 关键词、关注和默认回复。
|
||||
- 模板消息与公众号消息处理。
|
||||
### 2.12 微信与多渠道
|
||||
|
||||
微信小程序:
|
||||
|
||||
@ -191,26 +159,12 @@
|
||||
- 小程序模板、代码上传和发布相关配置。
|
||||
- 直播间、主播、直播商品和状态同步。
|
||||
|
||||
App/H5:
|
||||
H5:
|
||||
|
||||
- 渠道识别和统一认证入口。
|
||||
- App 版本管理、隐私协议与更新检查。
|
||||
- H5 微信环境识别及公众号 OAuth 协作。
|
||||
- H5 微信环境识别及公众号 OAuth 协作(公众号 OAuth 由已安装的微信公众号模块协作)。
|
||||
|
||||
### 2.14 客服
|
||||
|
||||
在线客服模块提供:
|
||||
|
||||
- 客服账号与登录。
|
||||
- 客服工作台和用户会话。
|
||||
- 聊天记录与用户历史信息。
|
||||
- 快捷话术及话术分类。
|
||||
- 自动回复规则。
|
||||
- 用户反馈提交、列表和处理。
|
||||
- 游客咨询与客服状态。
|
||||
- 基于 Workerman 的实时消息。
|
||||
|
||||
### 2.15 插件、驱动和系统维护
|
||||
### 2.13 插件、驱动和系统维护
|
||||
|
||||
- 模块/插件列表、清单信息和依赖检查。
|
||||
- 安装、卸载、启用、禁用。
|
||||
@ -225,19 +179,7 @@ App/H5:
|
||||
|
||||
卸载插件可能删除数据表;生产操作前必须确认插件 Installer 的回滚逻辑并完成备份。
|
||||
|
||||
### 2.16 开放平台
|
||||
|
||||
- 创建第三方应用账号和 `appid/appsecret`。
|
||||
- 配置账号可调用的接口权限。
|
||||
- 获取与刷新访问令牌。
|
||||
- 商品分类和商品同步。
|
||||
- 订单查询、备注、发货、拆单和收货。
|
||||
- 售后查询、审核和退款。
|
||||
- 优惠券管理。
|
||||
- 用户、等级、余额和积分操作。
|
||||
- 对外推送配置和异步推送任务。
|
||||
|
||||
### 2.17 AI Agent 与独立 MCP 服务
|
||||
### 2.14 AI Agent 与独立 MCP 服务
|
||||
|
||||
- `aiagent`:后台 AI 对话、流式响应、会话标题与置顶、自动化任务和运行记录。
|
||||
- AI 模型、MCP 服务连接、工具同步与 Skill 文件由后台配置;模型凭据不放前端。
|
||||
@ -319,30 +261,8 @@ App/H5:
|
||||
### 3.7 营销与互动
|
||||
|
||||
- 签到、签到记录和积分奖励。
|
||||
- 砍价列表、详情、发起和助力。
|
||||
- 拼团列表、详情、开团/参团与结果。
|
||||
- 秒杀列表、详情和下单。
|
||||
- 抽奖活动、抽奖记录和奖品。
|
||||
- 积分商城、商品详情、兑换和积分订单。
|
||||
- 新人礼和新人优惠券。
|
||||
- 会员专享优惠与活动。
|
||||
|
||||
### 3.8 分销
|
||||
|
||||
- 分销/推广资格和申请。
|
||||
- 推广用户、推广订单和团队数据。
|
||||
- 推广码与推广海报。
|
||||
- 佣金记录、提现和佣金排行。
|
||||
- 分销等级和升级任务。
|
||||
- 代付等扩展业务入口。
|
||||
|
||||
### 3.9 客服与反馈
|
||||
|
||||
- 客服入口和聊天页面。
|
||||
- 用户咨询、消息发送与历史记录。
|
||||
- 客服类型和在线状态。
|
||||
- 用户反馈提交。
|
||||
|
||||
## 4. 核心业务流程
|
||||
|
||||
### 4.1 新用户注册
|
||||
@ -350,9 +270,8 @@ App/H5:
|
||||
1. 客户端根据渠道选择手机号、微信、小程序、App 或统一认证入口。
|
||||
2. 服务端完成验证码/OAuth 凭证验证。
|
||||
3. 创建或关联 `eb_user` 及渠道身份。
|
||||
4. 触发 `UserRegister` 事件。
|
||||
5. 新人礼模块若启用,可发放余额、积分或优惠券。
|
||||
6. 返回 token,客户端保存并在后续请求头携带。
|
||||
4. 触发 `UserRegister` 事件(营销类模块可监听该事件发放权益)。
|
||||
5. 返回 token,客户端保存并在后续请求头携带。
|
||||
|
||||
### 4.2 普通商品下单
|
||||
|
||||
@ -362,8 +281,8 @@ App/H5:
|
||||
4. 用户确认地址、自提点、发票和支付方式。
|
||||
5. 服务端在事务内创建具体订单、商品快照、日志并同步订单中台。
|
||||
6. 用户发起支付;支付回调校验签名并幂等更新订单。
|
||||
7. 触发支付成功事件,执行库存确认、通知、分销快照和产品履约。
|
||||
8. 实物进入发货/自提,虚拟/卡密自动履约。
|
||||
7. 触发支付成功事件,执行库存确认、通知和产品履约(营销/分销类模块可监听事件扩展副作用)。
|
||||
8. 实物进入发货/自提,虚拟商品自动履约。
|
||||
|
||||
### 4.3 发货与收货
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user