feat(commission): 支持计奖商品参与设置、独立分成与多规格规则,完善团队中心与微信快捷登录
这个提交包含在:
@@ -6,6 +6,7 @@ use think\Db;
|
||||
use app\admin\model\shopro\user\User as UserModel;
|
||||
use app\admin\model\shopro\commission\Agent as AgentModel;
|
||||
use app\admin\model\shopro\goods\Goods as GoodsModel;
|
||||
use app\admin\model\shopro\partner\Reward as RewardModel;
|
||||
use addons\shopro\service\Wallet;
|
||||
|
||||
|
||||
@@ -268,11 +269,21 @@ class Agent extends Commission
|
||||
|
||||
$user->team_level = ($user->parent_user_id == $agentId) ? 1 : 2;
|
||||
$rawMobile = $user->mobile ? (string) $user->mobile : '';
|
||||
$maskedMobile = $rawMobile ? (strlen($rawMobile) == 11 ? substr_replace($rawMobile, '****', 3, 4) : $rawMobile) : '';
|
||||
$user->mobile = $maskedMobile;
|
||||
$user->mobile_hide = $maskedMobile;
|
||||
$user->mobile_raw = $rawMobile;
|
||||
$user->mobile_hide = $rawMobile ? (strlen($rawMobile) == 11 ? substr_replace($rawMobile, '****', 3, 4) : $rawMobile) : '';
|
||||
|
||||
// 间推所属推荐人手机号脱敏
|
||||
if ($user->parentUser && $user->parentUser->mobile) {
|
||||
$pMobile = (string) $user->parentUser->mobile;
|
||||
$pMasked = strlen($pMobile) == 11 ? substr_replace($pMobile, '****', 3, 4) : $pMobile;
|
||||
$user->parentUser->mobile = $pMasked;
|
||||
$user->parentUser->mobile_hide = $pMasked;
|
||||
}
|
||||
});
|
||||
|
||||
// 9. 计算整体团队健康度统计
|
||||
// 9. 计算整体团队健康度与综合收益业绩统计
|
||||
$summary = $this->getTeamHealthSummary($agentId, $level1Ids, $windowStart, $expiringStart);
|
||||
|
||||
$result = $data->toArray();
|
||||
@@ -282,7 +293,7 @@ class Agent extends Commission
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算团队保活健康度统计
|
||||
* 计算团队保活健康度与综合收益统计
|
||||
*/
|
||||
protected function getTeamHealthSummary($agentId, $level1Ids, $windowStart, $expiringStart)
|
||||
{
|
||||
@@ -298,6 +309,10 @@ class Agent extends Commission
|
||||
'expiring_count' => 0,
|
||||
'inactive_count' => 0,
|
||||
'partner_count' => 0,
|
||||
'team_total_consume' => '0.00',
|
||||
'team_total_reward' => '0.00',
|
||||
'team_month_reward' => '0.00',
|
||||
'team_order_count' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -306,6 +321,7 @@ class Agent extends Commission
|
||||
$expiringCount = 0;
|
||||
$inactiveCount = 0;
|
||||
$partnerCount = 0;
|
||||
$teamOrderCount = 0;
|
||||
|
||||
foreach ($agents as $ag) {
|
||||
$level = intval($ag->level ?? 0);
|
||||
@@ -323,6 +339,8 @@ class Agent extends Commission
|
||||
} else {
|
||||
$inactiveCount++;
|
||||
}
|
||||
|
||||
$teamOrderCount += intval($ag->child_order_count_0 ?? 0);
|
||||
}
|
||||
|
||||
$missingAgentCount = count($teamUserIds) - count($agents);
|
||||
@@ -330,12 +348,31 @@ class Agent extends Commission
|
||||
$inactiveCount += $missingAgentCount;
|
||||
}
|
||||
|
||||
// 1. 团队累计自购总业绩
|
||||
$teamTotalConsume = UserModel::whereIn('id', $teamUserIds)->sum('total_consume');
|
||||
|
||||
// 2. 团队成员为当前用户贡献的累计收益与本月收益
|
||||
$monthStart = strtotime(date('Y-m-01 00:00:00'));
|
||||
$teamTotalReward = RewardModel::where('user_id', $agentId)
|
||||
->whereIn('buyer_id', $teamUserIds)
|
||||
->where('status', 1)
|
||||
->sum('amount');
|
||||
$teamMonthReward = RewardModel::where('user_id', $agentId)
|
||||
->whereIn('buyer_id', $teamUserIds)
|
||||
->where('status', 1)
|
||||
->where('createtime', '>=', $monthStart)
|
||||
->sum('amount');
|
||||
|
||||
return [
|
||||
'total_count' => count($teamUserIds),
|
||||
'active_count' => $activeCount,
|
||||
'expiring_count' => $expiringCount,
|
||||
'inactive_count' => $inactiveCount,
|
||||
'partner_count' => $partnerCount,
|
||||
'team_total_consume' => \addons\shopro\service\partner\IdentityService::formatMoney($teamTotalConsume),
|
||||
'team_total_reward' => \addons\shopro\service\partner\IdentityService::formatMoney($teamTotalReward),
|
||||
'team_month_reward' => \addons\shopro\service\partner\IdentityService::formatMoney($teamMonthReward),
|
||||
'team_order_count' => $teamOrderCount,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ class Index extends Commission
|
||||
{
|
||||
$userId = $this->service->user->id;
|
||||
$tab = $this->request->param('tab', 'all');
|
||||
$buyerId = intval($this->request->param('buyer_id', 0));
|
||||
$query = RewardModel::where('user_id', $userId)->with(['buyer', 'order']);
|
||||
|
||||
$tabMap = [
|
||||
@@ -52,6 +53,9 @@ class Index extends Commission
|
||||
if ($tab !== 'all' && isset($tabMap[$tab])) {
|
||||
$query->whereIn('reward_type', $tabMap[$tab]);
|
||||
}
|
||||
if ($buyerId > 0) {
|
||||
$query->where('buyer_id', $buyerId);
|
||||
}
|
||||
|
||||
$list = $query->order('id', 'desc')->paginate($this->request->param('list_rows', 10));
|
||||
$rows = [];
|
||||
@@ -74,11 +78,26 @@ class Index extends Commission
|
||||
];
|
||||
}
|
||||
|
||||
$buyerInfo = null;
|
||||
if ($buyerId > 0) {
|
||||
$buyerUser = \app\admin\model\shopro\user\User::get($buyerId);
|
||||
if ($buyerUser) {
|
||||
$rawMobile = (string) ($buyerUser->mobile ?? '');
|
||||
$buyerInfo = [
|
||||
'id' => $buyerUser->id,
|
||||
'nickname' => $buyerUser->nickname,
|
||||
'avatar' => $buyerUser->avatar,
|
||||
'mobile' => $rawMobile ? (strlen($rawMobile) == 11 ? substr_replace($rawMobile, '****', 3, 4) : $rawMobile) : '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$this->success('ok', [
|
||||
'total' => $list->total(),
|
||||
'per_page' => $list->listRows(),
|
||||
'current_page' => $list->currentPage(),
|
||||
'data' => $rows,
|
||||
'buyer_info' => $buyerInfo,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,10 @@ class Wechat extends Common
|
||||
});
|
||||
|
||||
if ($result) {
|
||||
$this->success('登陆成功');
|
||||
$auth = \app\common\library\Auth::instance();
|
||||
$this->success('登陆成功', [
|
||||
'token' => $auth->isLogin() ? $auth->getToken() : ''
|
||||
]);
|
||||
}
|
||||
$this->error('登陆失败');
|
||||
}
|
||||
|
||||
@@ -39,9 +39,60 @@ class AgentFilter extends BaseFilter
|
||||
if ($value === 'county') {
|
||||
return $query->where('is_county', 1);
|
||||
}
|
||||
if ($value === 'partner') {
|
||||
return $query->where('is_partner', 1);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 等级筛选 (如 1 为品酒师)
|
||||
*/
|
||||
public function level($query, $data)
|
||||
{
|
||||
$value = $this->plainValue($data);
|
||||
if ($value === '' || $value === null) {
|
||||
return $query;
|
||||
}
|
||||
return $query->where('level', (int)$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 酒道馆筛选 (1 为酒道馆)
|
||||
*/
|
||||
public function isVenue($query, $data)
|
||||
{
|
||||
$value = $this->plainValue($data);
|
||||
if ($value === '' || $value === null) {
|
||||
return $query;
|
||||
}
|
||||
return $query->where('is_venue', (int)$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 区县代理筛选 (1 为区县代理)
|
||||
*/
|
||||
public function isCounty($query, $data)
|
||||
{
|
||||
$value = $this->plainValue($data);
|
||||
if ($value === '' || $value === null) {
|
||||
return $query;
|
||||
}
|
||||
return $query->where('is_county', (int)$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合伙股东筛选 (1 为合伙股东)
|
||||
*/
|
||||
public function isPartner($query, $data)
|
||||
{
|
||||
$value = $this->plainValue($data);
|
||||
if ($value === '' || $value === null) {
|
||||
return $query;
|
||||
}
|
||||
return $query->where('is_partner', (int)$value);
|
||||
}
|
||||
|
||||
public function active30d($query, $data)
|
||||
{
|
||||
$value = $this->plainValue($data);
|
||||
|
||||
@@ -66,7 +66,7 @@ class OrderCreate
|
||||
/**
|
||||
* 用户选择的兑换券抵扣金额
|
||||
*/
|
||||
protected $voucher_fee = 0;
|
||||
protected $voucher_fee = null;
|
||||
|
||||
/**
|
||||
* 用户备注
|
||||
@@ -158,7 +158,7 @@ class OrderCreate
|
||||
$this->invoice_id = $params['invoice_id'] ?? 0;
|
||||
|
||||
$this->coupon_id = $params['coupon_id'] ?? 0;
|
||||
$this->voucher_fee = isset($params['voucher_fee']) ? floatval($params['voucher_fee']) : 0;
|
||||
$this->voucher_fee = (isset($params['voucher_fee']) && $params['voucher_fee'] !== '') ? floatval($params['voucher_fee']) : null;
|
||||
$this->remark = $params['remark'] ?? '';
|
||||
|
||||
$this->money = (isset($params['money']) && $params['money'] > 0) ? $params['money'] : 0;
|
||||
@@ -611,7 +611,14 @@ class OrderCreate
|
||||
$available = (new \addons\shopro\service\partner\VoucherService())->available($this->user->id);
|
||||
}
|
||||
$this->orderData['voucher_available'] = $available;
|
||||
$want = max(0, round((float) $this->voucher_fee, 2));
|
||||
|
||||
// 兑换商城订单:若未显式指定抵扣额度,默认自动全额应用可用兑换券
|
||||
if ($this->voucher_fee === null) {
|
||||
$want = $this->order_type == 'score' ? (float) $available : 0;
|
||||
} else {
|
||||
$want = max(0, round((float) $this->voucher_fee, 2));
|
||||
}
|
||||
|
||||
$apply = min($want, (float) $available, (float) $this->orderData['pay_fee']);
|
||||
$apply = $apply > 0 ? $apply : 0;
|
||||
$this->orderData['voucher_fee'] = $apply;
|
||||
|
||||
@@ -7,9 +7,11 @@ namespace addons\shopro\service\partner;
|
||||
*/
|
||||
class MakerDiffReward
|
||||
{
|
||||
public function run($order, $buyerId, $chain, ActiveService $active)
|
||||
public function run($order, $buyerId, $chain, ActiveService $active, $rules = null)
|
||||
{
|
||||
$bonus = PartnerConfig::makerBonus();
|
||||
$bonus = (!empty($rules['maker_bonus']) && is_array($rules['maker_bonus']))
|
||||
? $rules['maker_bonus']
|
||||
: PartnerConfig::makerBonus();
|
||||
$seen = 0;
|
||||
$paid = [];
|
||||
|
||||
|
||||
@@ -7,11 +7,17 @@ namespace addons\shopro\service\partner;
|
||||
*/
|
||||
class MakerPeerReward
|
||||
{
|
||||
public function run($order, $buyerId, $chain, $diffResult, ActiveService $active)
|
||||
public function run($order, $buyerId, $chain, $diffResult, ActiveService $active, $rules = null)
|
||||
{
|
||||
$bonus = PartnerConfig::makerBonus();
|
||||
$peerRate = (float) PartnerConfig::get('partner_maker_peer_rate', 0.10);
|
||||
$overRate = (float) PartnerConfig::get('partner_maker_overtaken_rate', 0.05);
|
||||
$bonus = (!empty($rules['maker_bonus']) && is_array($rules['maker_bonus']))
|
||||
? $rules['maker_bonus']
|
||||
: PartnerConfig::makerBonus();
|
||||
$peerRate = (isset($rules['maker_peer_rate']) && is_numeric($rules['maker_peer_rate']))
|
||||
? (float) $rules['maker_peer_rate']
|
||||
: (float) PartnerConfig::get('partner_maker_peer_rate', 0.10);
|
||||
$overRate = (isset($rules['maker_overtaken_rate']) && is_numeric($rules['maker_overtaken_rate']))
|
||||
? (float) $rules['maker_overtaken_rate']
|
||||
: (float) PartnerConfig::get('partner_maker_overtaken_rate', 0.05);
|
||||
$minAmount = (float) PartnerConfig::get('partner_peer_min_amount', 0.10);
|
||||
if ($minAmount <= 0) {
|
||||
$minAmount = 0.10;
|
||||
|
||||
@@ -167,8 +167,143 @@ class PartnerConfig
|
||||
];
|
||||
}
|
||||
|
||||
public static function dailyTypes()
|
||||
public static function defaultRules()
|
||||
{
|
||||
return ['venue_share', 'county_share', 'madow_share', 'voucher_release'];
|
||||
return [
|
||||
'push3_amounts' => self::push3Amounts(),
|
||||
'maker_bonus' => self::makerBonus(),
|
||||
'maker_peer_rate' => (float) self::get('partner_maker_peer_rate', 0.10),
|
||||
'maker_overtaken_rate' => (float) self::get('partner_maker_overtaken_rate', 0.05),
|
||||
'venue_split' => (float) self::get('partner_venue_split', 50),
|
||||
'county_full' => (float) self::get('partner_county_full', 100),
|
||||
'venue_peer_rate' => (float) self::get('partner_venue_peer_rate', 0.10),
|
||||
'county_peer_rate' => (float) self::get('partner_county_peer_rate', 0.10),
|
||||
'voucher_times' => (float) self::get('partner_voucher_times', 1.5),
|
||||
'voucher_days' => (int) self::get('partner_voucher_days', 365),
|
||||
'venue_share' => (float) self::get('partner_venue_share_per_order', 100),
|
||||
'county_share' => (float) self::get('partner_county_share_per_order', 50),
|
||||
'is_custom' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定商品/规格的生效分成规则(优先独立规则,未设置项回退默认规则)
|
||||
*/
|
||||
public static function getEffectiveRules($goodsId, $skuPriceId = 0)
|
||||
{
|
||||
$rules = self::defaultRules();
|
||||
$goodsId = intval($goodsId);
|
||||
if ($goodsId <= 0) {
|
||||
return $rules;
|
||||
}
|
||||
|
||||
try {
|
||||
$cg = \think\Db::name('shopro_commission_goods')->where('goods_id', $goodsId)->find();
|
||||
if (!$cg || intval($cg['status'] ?? 0) !== 1) {
|
||||
// 未参与计奖
|
||||
$rules['status'] = 0;
|
||||
return $rules;
|
||||
}
|
||||
$rules['status'] = 1;
|
||||
$rules['commission_order_status'] = intval($cg['commission_order_status'] ?? 1);
|
||||
$selfRules = intval($cg['self_rules'] ?? 0);
|
||||
$rules['self_rules'] = $selfRules;
|
||||
|
||||
if ($selfRules !== 1 && $selfRules !== 2) {
|
||||
return $rules; // 默认规则
|
||||
}
|
||||
|
||||
$raw = $cg['commission_rules'] ?? '';
|
||||
$custom = is_array($raw) ? $raw : (json_decode($raw, true) ?: []);
|
||||
if (empty($custom)) {
|
||||
return $rules;
|
||||
}
|
||||
|
||||
// 多规格支持:优先匹配特定规格配置
|
||||
$skuKey = intval($skuPriceId);
|
||||
if ($skuKey > 0 && isset($custom[$skuKey]) && is_array($custom[$skuKey])) {
|
||||
$custom = $custom[$skuKey];
|
||||
} elseif ($skuKey > 0 && isset($custom['sku_' . $skuKey]) && is_array($custom['sku_' . $skuKey])) {
|
||||
$custom = $custom['sku_' . $skuKey];
|
||||
}
|
||||
|
||||
if (isset($custom['push3_amounts']) && is_array($custom['push3_amounts']) && count($custom['push3_amounts']) >= 3) {
|
||||
$rules['push3_amounts'] = array_map('floatval', array_values($custom['push3_amounts']));
|
||||
}
|
||||
if (isset($custom['maker_bonus']) && is_array($custom['maker_bonus'])) {
|
||||
$mb = [];
|
||||
foreach ($custom['maker_bonus'] as $lvl => $val) {
|
||||
$mb[intval($lvl)] = floatval($val);
|
||||
}
|
||||
$rules['maker_bonus'] = $mb;
|
||||
}
|
||||
if (isset($custom['maker_peer_rate']) && is_numeric($custom['maker_peer_rate'])) {
|
||||
$rules['maker_peer_rate'] = floatval($custom['maker_peer_rate']);
|
||||
}
|
||||
if (isset($custom['maker_overtaken_rate']) && is_numeric($custom['maker_overtaken_rate'])) {
|
||||
$rules['maker_overtaken_rate'] = floatval($custom['maker_overtaken_rate']);
|
||||
}
|
||||
if (isset($custom['venue_split']) && is_numeric($custom['venue_split'])) {
|
||||
$rules['venue_split'] = floatval($custom['venue_split']);
|
||||
}
|
||||
if (isset($custom['county_full']) && is_numeric($custom['county_full'])) {
|
||||
$rules['county_full'] = floatval($custom['county_full']);
|
||||
}
|
||||
if (isset($custom['venue_peer_rate']) && is_numeric($custom['venue_peer_rate'])) {
|
||||
$rules['venue_peer_rate'] = floatval($custom['venue_peer_rate']);
|
||||
}
|
||||
if (isset($custom['county_peer_rate']) && is_numeric($custom['county_peer_rate'])) {
|
||||
$rules['county_peer_rate'] = floatval($custom['county_peer_rate']);
|
||||
}
|
||||
if (isset($custom['voucher_times']) && is_numeric($custom['voucher_times'])) {
|
||||
$rules['voucher_times'] = floatval($custom['voucher_times']);
|
||||
}
|
||||
if (isset($custom['voucher_days']) && is_numeric($custom['voucher_days'])) {
|
||||
$rules['voucher_days'] = intval($custom['voucher_days']);
|
||||
}
|
||||
if (isset($custom['venue_share']) && is_numeric($custom['venue_share'])) {
|
||||
$rules['venue_share'] = floatval($custom['venue_share']);
|
||||
}
|
||||
if (isset($custom['county_share']) && is_numeric($custom['county_share'])) {
|
||||
$rules['county_share'] = floatval($custom['county_share']);
|
||||
}
|
||||
|
||||
$rules['is_custom'] = true;
|
||||
return $rules;
|
||||
} catch (\Exception $e) {
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析订单中主计奖商品的生效分成规则
|
||||
*/
|
||||
public static function getOrderEffectiveRules($order)
|
||||
{
|
||||
$default = self::defaultRules();
|
||||
$items = $order['items'] ?? [];
|
||||
if (empty($items) && !empty($order['id'])) {
|
||||
$fresh = \app\admin\model\shopro\order\Order::with(['items'])->where('id', $order['id'])->find();
|
||||
if ($fresh) {
|
||||
$items = $fresh['items'] ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
// 优先寻找设置了独立分成规则的计奖商品
|
||||
foreach ($items as $item) {
|
||||
$ext = is_array($item['ext'] ?? null) ? $item['ext'] : (json_decode($item['ext'] ?? '', true) ?: []);
|
||||
if (isset($ext['is_commission']) && !$ext['is_commission']) {
|
||||
continue;
|
||||
}
|
||||
$goodsId = intval($item['goods_id'] ?? 0);
|
||||
$skuId = intval($item['goods_sku_price_id'] ?? 0);
|
||||
$rules = self::getEffectiveRules($goodsId, $skuId);
|
||||
if (!empty($rules['is_custom'])) {
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,16 +40,19 @@ class PartnerRewardService
|
||||
$parentChain = $chainService->collect($buyerId, false);
|
||||
$selfChain = $chainService->collect($buyerId, true);
|
||||
|
||||
// 动态解析商品独立分成规则与全局默认配置
|
||||
$orderRules = PartnerConfig::getOrderEffectiveRules($order);
|
||||
|
||||
$parent = $parentChain[0] ?? null;
|
||||
(new Push3Reward())->run($order, $buyerId, $parent, $active);
|
||||
(new Push3Reward())->run($order, $buyerId, $parent, $active, $orderRules);
|
||||
|
||||
$diff = (new MakerDiffReward())->run($order, $buyerId, $parentChain, $active);
|
||||
(new MakerPeerReward())->run($order, $buyerId, $parentChain, $diff, $active);
|
||||
$diff = (new MakerDiffReward())->run($order, $buyerId, $parentChain, $active, $orderRules);
|
||||
(new MakerPeerReward())->run($order, $buyerId, $parentChain, $diff, $active, $orderRules);
|
||||
|
||||
$team = (new TeamSplitReward())->run($order, $buyerId, $selfChain, $active);
|
||||
(new PeerShareReward())->run($order, $buyerId, $selfChain, $team, $active);
|
||||
$team = (new TeamSplitReward())->run($order, $buyerId, $selfChain, $active, $orderRules);
|
||||
(new PeerShareReward())->run($order, $buyerId, $selfChain, $team, $active, $orderRules);
|
||||
|
||||
(new VoucherService())->grant($order, $buyerId);
|
||||
(new VoucherService())->grant($order, $buyerId, $orderRules);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -7,10 +7,14 @@ namespace addons\shopro\service\partner;
|
||||
*/
|
||||
class PeerShareReward
|
||||
{
|
||||
public function run($order, $buyerId, $chain, $teamResult, ActiveService $active)
|
||||
public function run($order, $buyerId, $chain, $teamResult, ActiveService $active, $rules = null)
|
||||
{
|
||||
$venueRate = (float) PartnerConfig::get('partner_venue_peer_rate', 0.10);
|
||||
$countyRate = (float) PartnerConfig::get('partner_county_peer_rate', 0.10);
|
||||
$venueRate = (isset($rules['venue_peer_rate']) && is_numeric($rules['venue_peer_rate']))
|
||||
? (float) $rules['venue_peer_rate']
|
||||
: (float) PartnerConfig::get('partner_venue_peer_rate', 0.10);
|
||||
$countyRate = (isset($rules['county_peer_rate']) && is_numeric($rules['county_peer_rate']))
|
||||
? (float) $rules['county_peer_rate']
|
||||
: (float) PartnerConfig::get('partner_county_peer_rate', 0.10);
|
||||
|
||||
if (!empty($teamResult['venue'])) {
|
||||
$this->peer($order, $buyerId, $chain, $teamResult['venue'], 'is_venue', 'venue_peer', 'venue', $venueRate, $active, '酒道馆');
|
||||
|
||||
@@ -9,7 +9,7 @@ use app\admin\model\shopro\commission\Agent as AgentModel;
|
||||
*/
|
||||
class Push3Reward
|
||||
{
|
||||
public function run($order, $buyerId, $parent, ActiveService $active)
|
||||
public function run($order, $buyerId, $parent, ActiveService $active, $rules = null)
|
||||
{
|
||||
if (!$parent) {
|
||||
PartnerRewardService::writeSkip($order, $buyerId, 0, 'push3', 'taster', 'no_parent', '暂无推荐人,不计提推荐开拓奖');
|
||||
@@ -35,7 +35,9 @@ class Push3Reward
|
||||
$agent->direct_order_seq = intval($agent->direct_order_seq) + 1;
|
||||
$agent->save();
|
||||
|
||||
$amounts = PartnerConfig::push3Amounts();
|
||||
$amounts = (!empty($rules['push3_amounts']) && is_array($rules['push3_amounts']))
|
||||
? $rules['push3_amounts']
|
||||
: PartnerConfig::push3Amounts();
|
||||
$seq = intval($agent->direct_order_seq);
|
||||
$amount = $amounts[($seq - 1) % 3];
|
||||
|
||||
|
||||
@@ -7,10 +7,14 @@ namespace addons\shopro\service\partner;
|
||||
*/
|
||||
class TeamSplitReward
|
||||
{
|
||||
public function run($order, $buyerId, $chain, ActiveService $active)
|
||||
public function run($order, $buyerId, $chain, ActiveService $active, $rules = null)
|
||||
{
|
||||
$venueSplit = (float) PartnerConfig::get('partner_venue_split', 50);
|
||||
$countyFull = (float) PartnerConfig::get('partner_county_full', 100);
|
||||
$venueSplit = (isset($rules['venue_split']) && is_numeric($rules['venue_split']))
|
||||
? (float) $rules['venue_split']
|
||||
: (float) PartnerConfig::get('partner_venue_split', 50);
|
||||
$countyFull = (isset($rules['county_full']) && is_numeric($rules['county_full']))
|
||||
? (float) $rules['county_full']
|
||||
: (float) PartnerConfig::get('partner_county_full', 100);
|
||||
$result = ['venue' => null, 'county' => null];
|
||||
|
||||
$venue = $this->nearest($chain, 'is_venue', $active);
|
||||
|
||||
@@ -123,15 +123,19 @@ class VoucherService
|
||||
}
|
||||
}
|
||||
|
||||
public function grant($order, $buyerId)
|
||||
public function grant($order, $buyerId, $rules = null)
|
||||
{
|
||||
$exists = VoucherPlan::where('order_id', $order['id'])->find();
|
||||
if ($exists) {
|
||||
return $exists;
|
||||
}
|
||||
|
||||
$times = (float) PartnerConfig::get('partner_voucher_times', 1.5);
|
||||
$days = intval(PartnerConfig::get('partner_voucher_days', 365));
|
||||
$times = (isset($rules['voucher_times']) && is_numeric($rules['voucher_times']))
|
||||
? (float) $rules['voucher_times']
|
||||
: (float) PartnerConfig::get('partner_voucher_times', 1.5);
|
||||
$days = (isset($rules['voucher_days']) && is_numeric($rules['voucher_days']))
|
||||
? intval($rules['voucher_days'])
|
||||
: intval(PartnerConfig::get('partner_voucher_days', 365));
|
||||
$days = $days > 0 ? $days : 365;
|
||||
$rewardService = new PartnerRewardService();
|
||||
$qualifiedPayFee = $rewardService->getQualifiedPayFee($order);
|
||||
|
||||
@@ -249,6 +249,19 @@ class PayOper
|
||||
// 订单支付完成
|
||||
$user = User::where('id', $order->user_id)->find();
|
||||
if ($order_type == 'order') {
|
||||
// 兑换券同步核销(利用行锁即时扣减并标记,彻底消除异步队列延迟导致的并发双花漏洞)
|
||||
$ext = $order->ext;
|
||||
if (is_string($ext)) {
|
||||
$ext = json_decode($ext, true) ?: [];
|
||||
}
|
||||
if (!empty($ext['voucher_fee']) && empty($ext['voucher_consumed'])) {
|
||||
try {
|
||||
(new \addons\shopro\service\partner\VoucherService())->consumeByOrder($order);
|
||||
} catch (\Exception $e) {
|
||||
\think\Log::record('[PayOper.consumeByOrder] 兑换券同步核销异常: ' . $e->getMessage(), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
if ($pay_mode == 'offline') {
|
||||
Action::add($order, null, auth_admin(), 'admin', '管理员操作自动货到付款支付成功');
|
||||
// 在控制器执行后续内容,这里不再处理
|
||||
|
||||
@@ -34,10 +34,16 @@ class MiniProgram
|
||||
error_stop('登陆态已过期, 请重试', -1);
|
||||
}
|
||||
|
||||
// 如果前端传了手机号凭证(新版 code 或传统 iv+encryptedData),解析手机号
|
||||
$mobile = '';
|
||||
if (!empty($this->payload['code']) || (!empty($this->payload['iv']) && !empty($this->payload['encryptedData']))) {
|
||||
$mobile = $this->getUserPhoneNumber();
|
||||
}
|
||||
|
||||
$wechatUser = [
|
||||
'openid' => $sessionData['openid'],
|
||||
'unionid' => $sessionData['unionid'] ?? '',
|
||||
'mobile' => '',
|
||||
'mobile' => $mobile,
|
||||
'avatar' => '',
|
||||
'nickname' => '',
|
||||
];
|
||||
@@ -65,9 +71,32 @@ class MiniProgram
|
||||
return $wechatUser;
|
||||
}
|
||||
|
||||
// 解密微信小程序手机号
|
||||
// 解密微信小程序手机号(支持新版 code 与传统 iv + encryptedData)
|
||||
public function getUserPhoneNumber()
|
||||
{
|
||||
// 1. 优先使用新版微信小程序通过 code 获取手机号 API
|
||||
if (!empty($this->payload['code'])) {
|
||||
try {
|
||||
$response = $this->wechat->auth->httpPostJson('wxa/business/getuserphonenumber', [
|
||||
'code' => $this->payload['code']
|
||||
]);
|
||||
if (isset($response['errcode']) && (int)$response['errcode'] === 0 && !empty($response['phone_info']['purePhoneNumber'])) {
|
||||
if (!empty($response['phone_info']['countryCode']) && $response['phone_info']['countryCode'] !== '86') {
|
||||
error_stop('仅支持大陆地区手机号');
|
||||
}
|
||||
return $response['phone_info']['purePhoneNumber'];
|
||||
}
|
||||
if (!empty($response['errmsg']) && empty($this->payload['encryptedData'])) {
|
||||
error_stop('获取手机号失败: ' . $response['errmsg']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if (empty($this->payload['encryptedData'])) {
|
||||
error_stop('获取手机号异常: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 兼容传统 iv + encryptedData 模式
|
||||
if (empty($this->payload['sessionId'])) {
|
||||
error_stop('未获取到登陆态, 请重试', -1);
|
||||
}
|
||||
@@ -78,6 +107,10 @@ class MiniProgram
|
||||
error_stop('登陆态已过期, 请重试', -1);
|
||||
}
|
||||
|
||||
if (empty($this->payload['iv']) || empty($this->payload['encryptedData'])) {
|
||||
error_stop('缺少手机号授权凭证');
|
||||
}
|
||||
|
||||
$phoneInfo = $this->wechat->encryptor->decryptData($sessionData['session_key'], $this->payload['iv'], $this->payload['encryptedData']);
|
||||
|
||||
if (empty($phoneInfo['purePhoneNumber'])) {
|
||||
|
||||
@@ -146,6 +146,23 @@ class Wechat
|
||||
// 用户被删除,则重置第三方信息所绑定的用户id
|
||||
if (!$user) {
|
||||
$oauthInfo->user_id = 0;
|
||||
} else {
|
||||
// 如果本次快捷登录提供了手机号,且用户原本没有手机号,则为用户补充手机号
|
||||
$isChanged = false;
|
||||
if (!empty($extend['mobile']) && empty($user->mobile)) {
|
||||
$user->mobile = $extend['mobile'];
|
||||
$isChanged = true;
|
||||
}
|
||||
$verification = $user->verification;
|
||||
$verificationObj = is_object($verification) ? $verification : (object)$verification;
|
||||
if ((!empty($user->mobile) || !empty($extend['mobile'])) && empty($verificationObj->mobile)) {
|
||||
$verificationObj->mobile = 1;
|
||||
$user->verification = $verificationObj;
|
||||
$isChanged = true;
|
||||
}
|
||||
if ($isChanged) {
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,8 +178,18 @@ class Wechat
|
||||
return false;
|
||||
}
|
||||
|
||||
// 注册新用户
|
||||
// 注册新用户或匹配已有手机号用户
|
||||
if ($oauthInfo->user_id === 0 && $user_id === 0) {
|
||||
// 如果获取到了手机号,检查系统是否已有该手机号用户
|
||||
if (!empty($extend['mobile'])) {
|
||||
$existingUser = UserModel::where('mobile', $extend['mobile'])->find();
|
||||
if ($existingUser) {
|
||||
// 直接将此第三方账号关联到该已有手机号用户
|
||||
$oauthInfo->user_id = $existingUser->id;
|
||||
return $oauthInfo->save();
|
||||
}
|
||||
}
|
||||
|
||||
$auth = (new UserAuth())->register([
|
||||
'nickname' => $oauthInfo->nickname ?? '',
|
||||
'avatar' => $oauthInfo->avatar ?? '',
|
||||
|
||||
在新工单中引用
屏蔽一个用户