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

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

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

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

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

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

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

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

View File

@@ -0,0 +1,51 @@
<?php
namespace crmeb\services\erp\storage\jushuitan;
use crmeb\services\erp\AccessToken;
use crmeb\services\erp\storage\Jushuitan;
/**
* 公用接口
* Class Comment
* @package crmeb\services\erp\storage\jushuitan
*/
class Comment
{
/**
* token句柄
* @var AccessToken
*/
protected $accessToken;
/*** @var Jushuitan */
protected $jushuitan;
/**
* Comment constructor.
* @param AccessToken $accessToken
* @param Jushuitan $jushuitan
*/
public function __construct(AccessToken $accessToken, Jushuitan $jushuitan)
{
$this->accessToken = $accessToken;
$this->jushuitan = $jushuitan;
}
/**
* 获取商铺列表
* @param int $page
* @param int $limit
* @return mixed
* @throws \Exception
*/
public function getShopList(int $page = 1, int $limit = 10)
{
$api = $this->accessToken->getApiUrl('/open/shops/query');
$biz['items'] = ['page_index' => $page, 'page_size' => $limit];
$params = $this->jushuitan->getParams($biz);
return $this->jushuitan->postRequest($api, $params);
}
}

View File

@@ -0,0 +1,151 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace crmeb\services\erp\storage\jushuitan;
use crmeb\exceptions\AdminException;
use crmeb\services\erp\AccessToken;
use crmeb\services\erp\storage\Jushuitan;
use Exception;
class Order
{
/**
* token句柄
* @var AccessToken
*/
protected $accessToken;
/*** @var Jushuitan */
protected $jushuitan;
/**
* @param Jushuitan $jushuitan
*/
public function __construct(AccessToken $accessToken, Jushuitan $jushuitan)
{
$this->accessToken = $accessToken;
$this->jushuitan = $jushuitan;
}
/**
* 订单上传(推荐)
* @param array $data
* @return array
* @throws Exception
*/
public function ordersUpload(array $data): array
{
$url = $this->accessToken->getApiUrl("/open/jushuitan/orders/upload");
//拼装请求参数
$params = $this->jushuitan->getParams($data);
//请求平台接口
$request = $this->jushuitan->postRequest($url, $params);
return $request["data"];
}
/**
* 订单查询
* @param array $data
* @return array
* @throws Exception
*/
public function ordersSingleQuery(array $data): array
{
$url = $this->accessToken->getApiUrl("/open/orders/single/query");
//业务参数
$biz = [];
if (isset($data["page_index"])) {
//int 第几页从第一页开始默认1
$biz["page_index"] = intval($data["page_index"]);
}
if (isset($data["page_size"])) {
//int 每页多少条默认30条最大50条
$biz["page_size"] = intval($data["page_size"]);
}
if (isset($data["modified_begin"])) {
if (empty($data["modified_end"])) {
throw new AdminException("起始和结束时间必须同时存在");
}
//string日志起始时间,起始时间和 结束时间必须同时存在,时间间隔不能超过七天
$biz["modified_begin"] = strval($data["modified_begin"]);
$biz["modified_end"] = strval($data["modified_end"]);
}
///list 线上单号号最大限制20条
if (isset($data["so_ids"])) {
$biz["so_ids"] = $data["so_ids"];
} elseif (empty($biz["modified_begin"]) || empty($biz["modified_end"])) {
//线上单号与修改时间不能同时为空
throw new AdminException("线上单号,与修改时间不能同时为空");
}
//店铺编号
if (!empty($data["shop_id"])) {
$biz["shop_id"] = $data["shop_id"];
}
//shop_id为0且is_offline_shop为true查询线下店铺单据 非必填 bool
if (isset($data["is_offline_shop"]) && !is_null($data["is_offline_shop"])) {
$biz["is_offline_shop"] = $data["is_offline_shop"];
}
if (!empty($data["status"])) {
$biz["status"] = $data["status"];
}
//拼装请求参数
$params = $this->jushuitan->getParams($biz);
//请求平台接口
$request = $this->jushuitan->postRequest($url, $params);
return $request["data"];
}
/**
* 订单取消-按内部单号取消
* @param array $data
* @return bool
* @throws Exception
*/
public function orderByOIdCancel(array $data): bool
{
$url = $this->accessToken->getApiUrl("/open/jushuitan/orderbyoid/cancel");
//拼装请求参数
$params = $this->jushuitan->getParams($data);
//请求平台接口
$this->jushuitan->postRequest($url, $params);
return true;
}
/**
* 实际收货上传
* @param array $list
* @return array
* @throws Exception
*/
public function afterSaleUpload(array $list): array
{
$url = $this->accessToken->getApiUrl("/open/aftersale/upload");
//拼装请求参数
$params = $this->jushuitan->getParams($list);
//请求平台接口
$request = $this->jushuitan->postRequest($url, $params);
return $request["data"];
}
}

View File

@@ -0,0 +1,127 @@
<?php
namespace crmeb\services\erp\storage\jushuitan;
use crmeb\services\erp\AccessToken;
use crmeb\services\erp\storage\Jushuitan;
class Product extends Jushuitan
{
/**
* token句柄
* @var AccessToken
*/
protected $accessToken;
/*** @var Jushuitan */
protected $jushuitan;
/**
* @param AccessToken $accessToken
* @param Jushuitan $jushuitan
*/
public function __construct(AccessToken $accessToken, Jushuitan $jushuitan)
{
$this->accessToken = $accessToken;
$this->jushuitan = $jushuitan;
}
/**
* 上传商品
* @param $data
* @return mixed
* @throws \Exception
*/
public function updateProduct($data)
{
$url = $this->accessToken->getApiUrl("/open/jushuitan/itemsku/upload");
//业务参数
$biz['items'] = $data;
//拼装请求参数
$params = $this->getParams($biz);
//请求平台接口
$request = $this->postRequest($url, $params);
if ($request['code'] == 0) {
return true;
} else {
return false;
}
}
/**
* 上传店铺商品
* @param $data
* @return mixed
* @throws \Exception
*/
public function updateShopProduct($data)
{
$url = $this->accessToken->getApiUrl("/open/jushuitan/skumap/upload");
//业务参数
$biz['items'] = $data;
//拼装请求参数
$params = $this->getParams($biz);
//请求平台接口
$request = $this->postRequest($url, $params);
if ($request['code'] == 0) {
return true;
} else {
return false;
}
}
/**
* 同步商品
* @param $spuArr
* @return mixed
* @throws \Exception
*/
public function syncProduct($spuArr)
{
$url = $this->accessToken->getApiUrl("/open/mall/item/query");
//业务参数
$biz['i_ids'] = $spuArr;
//拼装请求参数
$params = $this->getParams($biz);
//请求平台接口
$request = $this->postRequest($url, $params);
//获取ERP商品信息
return $request["data"];
}
/**
* 库存查询
* @param string $codeStr
* @return mixed
* @throws \Exception
*/
public function syncStock(string $codeStr)
{
$url = $this->accessToken->getApiUrl("/open/inventory/query");
//业务参数
$biz = [];
$biz["sku_ids"] = $codeStr;
//拼装请求参数
$params = $this->getParams($biz);
//请求平台接口
$request = $this->postRequest($url, $params);
return $request["data"];
}
}

View File

@@ -0,0 +1,151 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace crmeb\services\erp\storage\jushuitan;
use app\services\product\product\StoreProductServices;
use app\services\product\sku\StoreProductAttrValueServices;
use crmeb\exceptions\AdminException;
use crmeb\services\erp\AccessToken;
use crmeb\services\erp\storage\Jushuitan;
class Stock
{
/**
* token句柄
* @var AccessToken
*/
protected $accessToken;
/*** @var Jushuitan */
protected $jushuitan;
/**
* @param AccessToken $accessToken
* @param Jushuitan $jushuitan
*/
public function __construct(AccessToken $accessToken, Jushuitan $jushuitan)
{
$this->accessToken = $accessToken;
$this->jushuitan = $jushuitan;
}
/**
* 同步商品库存
* @param array $ids
* @return void
* @throws \Exception
*/
public function syncStock(string $ids = '')
{
/** @var StoreProductServices $storeProductServices */
$storeProductServices = app()->make(StoreProductServices::class);
/** @var StoreProductAttrValueServices $storeProductAttrValueServices */
$storeProductAttrValueServices = app()->make(StoreProductAttrValueServices::class);
//查询ids下的所有规格对应的sku
$ids = array_unique(array_map('intval', explode(',', $ids)));
$list = $storeProductAttrValueServices->getSkuArray(['product_id' => $ids, 'type' => 0], 'code', 'id');
$values = array_filter(array_values($list));
if (empty($values)) {
throw new AdminException('没有符合同步库存的商品');
}
$skuData = $skuMap = [];
$basic = 20; // 单次查询数量最多20
$num = count($values);
$rate = ceil($num / $basic);
for ($i = 0; $i < $rate; $i++) {
$code = array_slice($values, $i * $basic, $basic);
$skuMap = $skuMap + $this->getSkuStockByCode($code);
}
// 拼装规格数据
if (!empty($skuMap)) {
foreach ($skuMap as $key => $item) {
if ($id = array_search($key, $list)) {
$skuData[] = ['id' => $id, 'stock' => $item, 'sum_stock' => $item];
}
}
}
// 同步库存 TODO:待添加至队列
$storeProductServices->transaction(function () use ($ids, $skuData, $skuMap, $storeProductAttrValueServices, $storeProductServices) {
// 同步规格库存
$storeProductAttrValueServices->saveAll($skuData);
// 同步商品库存
$productData = $storeProductAttrValueServices->getProductStockByValues($ids);
$storeProductServices->saveAll($productData);
//同步门店库存
foreach ($skuMap as $item) {
}
});
return true;
}
/**
* 库存查询
* @param array $data
* @return array
* @throws \Exception
*/
public function inventoryQuery(string $codeStr): array
{
$url = $this->accessToken->getApiUrl("/open/inventory/query");
//业务参数
$biz = [];
$biz["sku_ids"] = $codeStr;
//拼装请求参数
$params = $this->getParams($biz);
//请求平台接口
$request = $this->postRequest($url, $params);
return $request["data"];
}
/**
* 获取erp库存
* @param array $code
* @return array
* @throws \Exception
*/
public function getSkuStockByCode(array $code = []): array
{
$skuMap = [];
$codeStr = implode(',', $code);
$result = $this->inventoryQuery($codeStr);
if (!empty($result['inventorys'])) {
foreach ($result['inventorys'] as $inventory) {
$skuMap[$inventory['sku_id']] = $inventory['qty'];
}
}
return $skuMap;
}
/**
* @param $name
* @param $arguments
* @return mixed
*/
public function __call($name, $arguments)
{
return call_user_func_array([$this->jushuitan, $name], $arguments);
}
}