小程序登录优化
This commit is contained in:
@@ -5,7 +5,6 @@ namespace App\Http\Controllers\Customer;
|
||||
use App\Exceptions\RepositoryException;
|
||||
use App\Http\Requests\Customer\MiniUserBindRequest;
|
||||
use App\Models\StoreModel;
|
||||
use App\Models\SupplierModel;
|
||||
use App\Models\UserModel;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -21,9 +20,7 @@ use Modules\Common\Http\Controllers\BaseController;
|
||||
class MiniUserController extends BaseController
|
||||
{
|
||||
protected array $searchField = [
|
||||
'type' => '=',
|
||||
'store_id' => '=',
|
||||
'supplier_id' => '=',
|
||||
'status' => '=',
|
||||
];
|
||||
|
||||
@@ -35,7 +32,7 @@ class MiniUserController extends BaseController
|
||||
{
|
||||
$params = $request->all();
|
||||
$pageSize = $params['pageSize'] ?? 10;
|
||||
$data = $this->buildSearch($params, UserModel::query()->with('store:id,name', 'supplier:id,name'))
|
||||
$data = $this->buildSearch($params, UserModel::query()->with('store:id,name'))
|
||||
->orderBy('id', 'desc')
|
||||
->paginate($pageSize)
|
||||
->toArray();
|
||||
@@ -43,7 +40,7 @@ class MiniUserController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定门店/供应商(一个门店可绑多个账号,一个账号只绑一个主体)
|
||||
* 绑定门店(一个门店可绑多个账号,一个账号只绑一个门店)
|
||||
*/
|
||||
#[PutRoute(route: '/{id}/bind', authorize: 'bind', where: ['id' => '[0-9]+'])]
|
||||
public function bind(int $id, MiniUserBindRequest $request): JsonResponse
|
||||
@@ -53,29 +50,16 @@ class MiniUserController extends BaseController
|
||||
if (empty($user)) {
|
||||
throw new RepositoryException('用户不存在');
|
||||
}
|
||||
|
||||
if ((int) $validated['type'] === UserModel::TYPE_STORE) {
|
||||
$store = StoreModel::find((int) $validated['store_id']);
|
||||
if (empty($store)) {
|
||||
throw new RepositoryException('门店不存在');
|
||||
}
|
||||
$user->type = UserModel::TYPE_STORE;
|
||||
$user->store_id = $store->id;
|
||||
$user->supplier_id = 0;
|
||||
} else {
|
||||
$supplier = SupplierModel::find((int) $validated['supplier_id']);
|
||||
if (empty($supplier)) {
|
||||
throw new RepositoryException('供应商不存在');
|
||||
}
|
||||
$user->type = UserModel::TYPE_SUPPLIER;
|
||||
$user->supplier_id = $supplier->id;
|
||||
$user->store_id = 0;
|
||||
$store = StoreModel::find((int) $validated['store_id']);
|
||||
if (empty($store)) {
|
||||
throw new RepositoryException('门店不存在');
|
||||
}
|
||||
$user->store_id = $store->id;
|
||||
$user->save();
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 启用/停用(停用后登录时检查 status 拒绝,token 鉴权拦截) */
|
||||
/** 启用/停用 */
|
||||
#[PutRoute(route: '/{id}/status', authorize: 'update', where: ['id' => '[0-9]+'])]
|
||||
public function status(int $id, Request $request): JsonResponse
|
||||
{
|
||||
|
||||
@@ -46,7 +46,9 @@ class StoreController extends BaseController
|
||||
#[PostRoute(authorize: 'create')]
|
||||
public function create(StoreFormRequest $request): JsonResponse
|
||||
{
|
||||
StoreModel::create($request->validated());
|
||||
$validated = $request->validated();
|
||||
$validated['code'] = generate_unique_code(StoreModel::class);
|
||||
StoreModel::create($validated);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,134 +3,125 @@
|
||||
namespace App\Http\Controllers\Mini;
|
||||
|
||||
use App\Exceptions\RepositoryException;
|
||||
use App\Http\Requests\UserUpdateInfoRequest;
|
||||
use App\Models\StoreModel;
|
||||
use App\Models\SupplierModel;
|
||||
use App\Models\UserModel;
|
||||
use App\Services\WechatService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
|
||||
/**
|
||||
* 小程序认证(微信登录 / 手机号绑定 / 当前用户信息)
|
||||
*
|
||||
* authGuard: users(provider 指向 UserModel,与后台 sys_users 天然隔离);
|
||||
* authorize: true 仅要求登录(sanctum + authGuard:users),不做细粒度权限点;
|
||||
* token abilities ['mini'] 作来源标记。
|
||||
* 小程序认证
|
||||
*/
|
||||
#[RequestAttribute('/mini', 'mini', authGuard: 'users')]
|
||||
class AuthController extends BaseMiniController
|
||||
{
|
||||
/** 小程序登录:wx.login 的 code → openid → 自动注册/登录 → 签发 token */
|
||||
/** 小程序登录 */
|
||||
#[PostRoute('/auth/login', authorize: false)]
|
||||
public function login(Request $request): JsonResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'code' => 'required|string',
|
||||
$validated = $request->validate([
|
||||
'code' => 'required|string'
|
||||
], [
|
||||
'code.required' => '缺少登录凭证 code',
|
||||
'code.required' => '登录参数格式错误',
|
||||
'code.string' => '登录参数格式错误',
|
||||
]);
|
||||
|
||||
$session = app(WechatService::class)->code2Session($data['code']);
|
||||
$session = app(WechatService::class)->code2Session($validated['code']);
|
||||
|
||||
$user = UserModel::firstOrNew(['openid' => $session['openid']]);
|
||||
$isNew = ! $user->exists;
|
||||
$user = UserModel::where('openid', $session['openid'])->first();
|
||||
|
||||
if ($user->status === UserModel::STATUS_DISABLED) {
|
||||
throw new RepositoryException('账号已被停用,请联系客服');
|
||||
if ($user === null || $user->status === UserModel::STATUS_DISABLED) {
|
||||
return $this->error('账号不存在或已被停用');
|
||||
}
|
||||
if ($isNew) {
|
||||
$user->type = UserModel::TYPE_PENDING;
|
||||
$user->status = UserModel::STATUS_NORMAL;
|
||||
}
|
||||
if (! empty($session['unionid'])) {
|
||||
$user->unionid = $session['unionid'];
|
||||
}
|
||||
$user->last_login_at = now();
|
||||
|
||||
$user->last_login_at = date('Y-m-d H:i:s');
|
||||
$user->save();
|
||||
|
||||
$token = $user->createToken('mini', ['mini'])->plainTextToken;
|
||||
|
||||
$token = $user->createToken($user->openid)->toArray();
|
||||
return $this->success([
|
||||
'token' => $token,
|
||||
'user' => $this->formatUser($user->fresh(['store.level:id,name', 'supplier:id,name'])),
|
||||
], $isNew ? '注册成功' : '登录成功');
|
||||
'token' => $token['plainTextToken'],
|
||||
'user' => $user->toArray(),
|
||||
], __('user.login_success'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定手机号:phoneCode 换手机号 → 按手机号自动匹配门店/供应商
|
||||
* (命中门店 → type=1+store_id;命中供应商 → type=2+supplier_id;都不命中 → 保持待绑定,后台人工处理)
|
||||
*/
|
||||
#[PostRoute('/auth/phone', authorize: true)]
|
||||
public function phone(Request $request): JsonResponse
|
||||
/** 小程序注册 */
|
||||
#[PostRoute('/auth/register', authorize: false)]
|
||||
public function register(Request $request): JsonResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
$validated = $request->validate([
|
||||
'code' => 'required|string',
|
||||
'phoneCode' => 'required|string',
|
||||
'storeCode' => 'required|string'
|
||||
], [
|
||||
'phoneCode.required' => '缺少手机号授权凭证 phoneCode',
|
||||
'code.required' => '注册参数格式错误',
|
||||
'code.string' => '注册参数格式错误',
|
||||
'phoneCode.required' => '注册参数格式错误',
|
||||
'phoneCode.string' => '注册参数格式错误',
|
||||
'storeCode.required' => '门店编码必须填写',
|
||||
'storeCode.string' => '注册参数格式错误',
|
||||
]);
|
||||
|
||||
$phone = app(WechatService::class)->getPhone($data['phoneCode']);
|
||||
|
||||
$user = $this->currentUser($request);
|
||||
$user->phone = $phone;
|
||||
|
||||
if (! $user->isBound()) {
|
||||
$store = StoreModel::where('phone', $phone)
|
||||
->where('status', StoreModel::STATUS_NORMAL)
|
||||
->first();
|
||||
if ($store !== null) {
|
||||
$user->type = UserModel::TYPE_STORE;
|
||||
$user->store_id = $store->id;
|
||||
$user->supplier_id = 0;
|
||||
} else {
|
||||
$supplier = SupplierModel::where('phone', $phone)
|
||||
->where('status', SupplierModel::STATUS_NORMAL)
|
||||
->first();
|
||||
if ($supplier !== null) {
|
||||
$user->type = UserModel::TYPE_SUPPLIER;
|
||||
$user->supplier_id = $supplier->id;
|
||||
$user->store_id = 0;
|
||||
}
|
||||
}
|
||||
$store = StoreModel::where('code', $validated['storeCode'])->first();
|
||||
if (!$store) {
|
||||
return $this->error('门店不存在!');
|
||||
}
|
||||
$user->save();
|
||||
|
||||
// 通过 code 换取 openid、session_key、unionid
|
||||
$session = app(WechatService::class)->code2Session($validated['code']);
|
||||
|
||||
$user = UserModel::where('openid', $session['openid'])->first();
|
||||
|
||||
if ($user) {
|
||||
return $this->error('你的微信已经注册,请直接登录!');
|
||||
}
|
||||
|
||||
$userData = [
|
||||
'openid' => $session['openid'],
|
||||
'unionid' => $session['unionid'] ?? '',
|
||||
'username' => 'wx_'.uniqid(),
|
||||
'nickname' => '微信用户',
|
||||
'store_id' => $store->id,
|
||||
'avatar' => '',
|
||||
'password' => '',
|
||||
'last_login_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
$phone = app(WechatService::class)->getPhone($validated['phoneCode']);
|
||||
$userData['phone'] = $phone ?? '';
|
||||
|
||||
$user = UserModel::create($userData);
|
||||
|
||||
$token = $user->createToken($user->username)->toArray();
|
||||
|
||||
return $this->success([
|
||||
'user' => $this->formatUser($user->fresh(['store.level:id,name', 'supplier:id,name'])),
|
||||
]);
|
||||
'token' => $token['plainTextToken'],
|
||||
'user' => $user->toArray(),
|
||||
], __('user.login_success'));
|
||||
}
|
||||
|
||||
/** 当前用户信息(含门店客户等级 —— 全局价格体系依据 / 供应商信息) */
|
||||
#[GetRoute('/auth/info', authorize: true)]
|
||||
/** 当前用户信息 */
|
||||
#[GetRoute('/auth/info')]
|
||||
public function info(Request $request): JsonResponse
|
||||
{
|
||||
$user = UserModel::with(['store.level:id,name', 'supplier:id,name'])
|
||||
$user = UserModel::with(['store.level:id,name'])
|
||||
->find($request->user()->id);
|
||||
if ($user === null) {
|
||||
throw new RepositoryException('账号不存在');
|
||||
}
|
||||
|
||||
return $this->success(['user' => $this->formatUser($user)]);
|
||||
return $this->success($user->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序端用户信息输出结构
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function formatUser(UserModel $user): array
|
||||
#[PutRoute('auth/info')]
|
||||
public function setUserInfo(UserUpdateInfoRequest $request): JsonResponse
|
||||
{
|
||||
return [
|
||||
'id' => $user->id,
|
||||
'nickname' => $user->nickname,
|
||||
'avatar' => $user->avatar,
|
||||
'phone' => $user->phone,
|
||||
'type' => $user->type,
|
||||
'store' => $user->store,
|
||||
'supplier' => $user->supplier,
|
||||
];
|
||||
UserModel::where('user_id', auth('user')->id())->update($request->validated());
|
||||
|
||||
return $this->error('更新成功');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Http\Controllers\Mini;
|
||||
|
||||
use App\Exceptions\RepositoryException;
|
||||
use App\Models\StoreModel;
|
||||
use App\Models\SupplierModel;
|
||||
use App\Models\UserModel;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
@@ -13,7 +12,7 @@ use Modules\Common\Http\Controllers\BaseController;
|
||||
* 小程序端控制器基类
|
||||
*
|
||||
* 无 #[RequestAttribute],不会被 AnnoRoute 注册为路由。
|
||||
* 提供当前用户获取与门店/供应商绑定前置校验。
|
||||
* 提供当前用户获取与门店绑定前置校验。
|
||||
*/
|
||||
abstract class BaseMiniController extends BaseController
|
||||
{
|
||||
@@ -26,7 +25,6 @@ abstract class BaseMiniController extends BaseController
|
||||
if ($user === null || $user->status === UserModel::STATUS_DISABLED) {
|
||||
throw new RepositoryException('账号不存在或已被停用');
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
@@ -35,7 +33,7 @@ abstract class BaseMiniController extends BaseController
|
||||
*/
|
||||
protected function ensureStoreBound(UserModel $user): StoreModel
|
||||
{
|
||||
if ($user->type !== UserModel::TYPE_STORE || $user->store_id <= 0) {
|
||||
if ($user->store_id <= 0) {
|
||||
throw new RepositoryException('尚未绑定门店,请联系客服处理');
|
||||
}
|
||||
$store = StoreModel::find($user->store_id);
|
||||
@@ -47,18 +45,20 @@ abstract class BaseMiniController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* 供应商端前置校验:type=供应商 且 supplier_id>0 且供应商正常
|
||||
* 用户当前绑定的正常门店(未绑定/已停用返回 null,不抛错)
|
||||
*
|
||||
* 供商品浏览等「弱前置」场景使用:未绑定门店仍可浏览商品,仅价格不可见。
|
||||
*/
|
||||
protected function ensureSupplierBound(UserModel $user): SupplierModel
|
||||
protected function boundStore(UserModel $user): ?StoreModel
|
||||
{
|
||||
if ($user->type !== UserModel::TYPE_SUPPLIER || $user->supplier_id <= 0) {
|
||||
throw new RepositoryException('尚未绑定供应商,请联系客服处理');
|
||||
if ($user->store_id <= 0) {
|
||||
return null;
|
||||
}
|
||||
$supplier = SupplierModel::find($user->supplier_id);
|
||||
if ($supplier === null || $supplier->status !== SupplierModel::STATUS_NORMAL) {
|
||||
throw new RepositoryException('供应商不存在或已停用,请联系客服处理');
|
||||
$store = StoreModel::find($user->store_id);
|
||||
if ($store === null || $store->status !== StoreModel::STATUS_NORMAL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $supplier;
|
||||
return $store;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,34 +16,34 @@ use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\SystemTool\Models\SysFileModel;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 小程序购物车(门店订货车:加购 / 列表 / 改数量 / 删项 / 清空)
|
||||
* 提交订货单复用 POST /mini/order,购物车仅作前置编辑容器
|
||||
* 小程序购物车
|
||||
*/
|
||||
#[RequestAttribute('/mini', 'mini', authGuard: 'users')]
|
||||
class CartController extends BaseMiniController
|
||||
{
|
||||
/** decimal(10,2) 上限 */
|
||||
private const MAX_QUANTITY = '99999999.99';
|
||||
private const string MAX_QUANTITY = '99999999.99';
|
||||
|
||||
/**
|
||||
* 加购:商品上架 + 门店有等级价(与下单一致 fail-fast),同商品合并累加
|
||||
* 加购
|
||||
* @throws Throwable
|
||||
*/
|
||||
#[PostRoute('/cart', authorize: true)]
|
||||
#[PostRoute('/cart')]
|
||||
public function store(MiniCartRequest $request): JsonResponse
|
||||
{
|
||||
$user = $this->currentUser($request);
|
||||
$store = $this->ensureStoreBound($user);
|
||||
if ($store->level_id <= 0) {
|
||||
throw new RepositoryException('门店未设置客户等级,无法加购,请联系客服');
|
||||
return $this->error('门店未设置客户等级,无法加购,请联系客服');
|
||||
}
|
||||
|
||||
$productId = (int) $request->validated('product_id');
|
||||
// Eloquent 查询自带 SoftDeletes 全局作用域:软删除/下架一并在内
|
||||
$product = ProductModel::where('status', ProductModel::STATUS_ON)->find($productId);
|
||||
if ($product === null) {
|
||||
throw new RepositoryException('商品不存在或已下架,请刷新后重试');
|
||||
return $this->error('商品不存在或已下架,请刷新后重试');
|
||||
}
|
||||
// 存在性校验与计价类型无关(百分比行 price 可能为 0 也能加购)
|
||||
$hasPrice = ProductPriceModel::query()
|
||||
@@ -51,7 +51,7 @@ class CartController extends BaseMiniController
|
||||
->where('level_id', $store->level_id)
|
||||
->exists();
|
||||
if (! $hasPrice) {
|
||||
throw new RepositoryException('商品「' . $product->name . '」未设置您所在等级的价格,无法加购');
|
||||
return $this->error('商品「' . $product->name . '」价格未设置,无法加购');
|
||||
}
|
||||
|
||||
$quantity = (string) $request->validated('quantity');
|
||||
@@ -88,8 +88,7 @@ class CartController extends BaseMiniController
|
||||
}
|
||||
|
||||
/**
|
||||
* 购物车列表:当前用户全部项 + 实时等级价,逐项服务端 bcmul 算金额;
|
||||
* status=1 可购 / 0 商品下架、缺失或未设等级价;汇总只统计可购项
|
||||
* 购物车列表
|
||||
*/
|
||||
#[GetRoute('/cart', authorize: true)]
|
||||
public function index(Request $request): JsonResponse
|
||||
|
||||
@@ -12,7 +12,10 @@ use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
|
||||
/**
|
||||
* 小程序商品(分类树 + 列表,价格 = 当前门店客户等级价)
|
||||
* 小程序商品(分类树 + 列表)
|
||||
*
|
||||
* 商品浏览仅需登录:未绑定门店/门店未设客户等级的用户也可查看商品,仅价格不可见(price=null);
|
||||
* 加购、下单仍由购物车/订单前置校验拦截,要求绑定门店并已设客户等级。
|
||||
*/
|
||||
#[RequestAttribute('/mini', 'mini', authGuard: 'users')]
|
||||
class ProductController extends BaseMiniController
|
||||
@@ -53,22 +56,23 @@ class ProductController extends BaseMiniController
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品列表:价格取当前门店客户等级价(未绑等级的门店报错);
|
||||
* 商品列表:价格取当前门店客户等级价;
|
||||
* 未绑定门店/门店未设客户等级 → 仍可浏览,price 为 null(不可见价格,加购/下单另由前置校验拦截);
|
||||
* ?category_id=&keyword=&page=&pageSize=
|
||||
*/
|
||||
#[GetRoute('/product/list', authorize: true)]
|
||||
public function products(Request $request): JsonResponse
|
||||
{
|
||||
$user = $this->currentUser($request);
|
||||
$store = $this->ensureStoreBound($user);
|
||||
if ($store->level_id <= 0) {
|
||||
throw new RepositoryException('门店未设置客户等级,无法展示价格,请联系客服');
|
||||
}
|
||||
$levelId = $this->boundStore($user)?->level_id ?? 0;
|
||||
|
||||
$query = ProductModel::query()
|
||||
->where('status', ProductModel::STATUS_ON)
|
||||
->with('category:id,name')
|
||||
->with(['prices' => static fn ($q) => $q->where('level_id', $store->level_id)]);
|
||||
->with('category:id,name');
|
||||
|
||||
if ($levelId > 0) {
|
||||
$query->with(['prices' => static fn ($q) => $q->where('level_id', $levelId)]);
|
||||
}
|
||||
|
||||
$categoryId = (int) $request->input('category_id', 0);
|
||||
if ($categoryId > 0) {
|
||||
@@ -88,7 +92,8 @@ class ProductController extends BaseMiniController
|
||||
->paginate($pageSize)
|
||||
->toArray();
|
||||
|
||||
// 扁平化价格:prices[0].actual_price → price(访问器经 toArray 自动输出换算后实际价;未设等级价为 null)
|
||||
// 扁平化价格:prices[0].actual_price → price(访问器经 toArray 自动输出换算后实际价;
|
||||
// 未绑定门店或未设等级价为 null——未加载 prices 时 ?? null 兜底)
|
||||
// unset prices 同时移除 price_type/percent,门店端无法反推成本;cost_price 已被 $hidden 过滤
|
||||
foreach ($data['data'] as &$row) {
|
||||
$row['price'] = $row['prices'][0]['actual_price'] ?? null;
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Mini;
|
||||
|
||||
use App\Exceptions\RepositoryException;
|
||||
use App\Models\PurchaseOrderItemModel;
|
||||
use App\Models\PurchaseOrderModel;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
|
||||
/**
|
||||
* 小程序供应商端(接收采购单 / 明细 / 确认接单)
|
||||
*/
|
||||
#[RequestAttribute('/mini', 'mini', authGuard: 'users')]
|
||||
class SupplierController extends BaseMiniController
|
||||
{
|
||||
/** 收到的采购单:含本供应商 is_sent=1 明细的采购单(去重) */
|
||||
#[GetRoute('/supplier/purchases', authorize: true)]
|
||||
public function purchases(Request $request): JsonResponse
|
||||
{
|
||||
$user = $this->currentUser($request);
|
||||
$supplier = $this->ensureSupplierBound($user);
|
||||
|
||||
$purchaseIds = PurchaseOrderItemModel::query()
|
||||
->where('supplier_id', $supplier->id)
|
||||
->where('is_sent', PurchaseOrderItemModel::SENT)
|
||||
->distinct()
|
||||
->pluck('purchase_id');
|
||||
|
||||
$data = PurchaseOrderModel::query()
|
||||
->whereIn('id', $purchaseIds)
|
||||
->orderBy('purchase_date', 'desc')
|
||||
->orderBy('id', 'desc')
|
||||
->paginate((int) $request->input('pageSize', 10))
|
||||
->toArray();
|
||||
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/** 采购单明细:仅本供应商且已发送的明细行 */
|
||||
#[GetRoute('/supplier/purchases/{id}', authorize: true, where: ['id' => '[0-9]+'])]
|
||||
public function detail(int $id, Request $request): JsonResponse
|
||||
{
|
||||
$user = $this->currentUser($request);
|
||||
$supplier = $this->ensureSupplierBound($user);
|
||||
|
||||
$purchase = PurchaseOrderModel::find($id);
|
||||
if ($purchase === null) {
|
||||
throw new RepositoryException('采购单不存在');
|
||||
}
|
||||
|
||||
$items = $purchase->items()
|
||||
->where('supplier_id', $supplier->id)
|
||||
->where('is_sent', PurchaseOrderItemModel::SENT)
|
||||
->get();
|
||||
if ($items->isEmpty()) {
|
||||
throw new RepositoryException('该采购单无贵司的采购明细');
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'id' => $purchase->id,
|
||||
'purchase_no' => $purchase->purchase_no,
|
||||
'purchase_date' => $purchase->purchase_date?->toDateString(),
|
||||
'remark' => $purchase->remark,
|
||||
'items' => $items->toArray(),
|
||||
]);
|
||||
}
|
||||
|
||||
/** 确认接单:本供应商已发送明细批量记录 supplier_confirmed_at(幂等) */
|
||||
#[PutRoute('/supplier/purchases/{id}/confirm', authorize: true, where: ['id' => '[0-9]+'])]
|
||||
public function confirm(int $id, Request $request): JsonResponse
|
||||
{
|
||||
$user = $this->currentUser($request);
|
||||
$supplier = $this->ensureSupplierBound($user);
|
||||
|
||||
$purchase = PurchaseOrderModel::find($id);
|
||||
if ($purchase === null) {
|
||||
throw new RepositoryException('采购单不存在');
|
||||
}
|
||||
|
||||
$items = $purchase->items()
|
||||
->where('supplier_id', $supplier->id)
|
||||
->where('is_sent', PurchaseOrderItemModel::SENT)
|
||||
->get();
|
||||
if ($items->isEmpty()) {
|
||||
throw new RepositoryException('该采购单无贵司的采购明细');
|
||||
}
|
||||
|
||||
$now = now();
|
||||
$confirmed = 0;
|
||||
foreach ($items as $item) {
|
||||
if ($item->supplier_confirmed_at === null) {
|
||||
$item->supplier_confirmed_at = $now;
|
||||
$item->save();
|
||||
$confirmed++;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success(['confirmed' => $confirmed], '已确认接单');
|
||||
}
|
||||
}
|
||||
@@ -146,7 +146,6 @@ class StoreOrderController extends BaseController
|
||||
private function notifyStore(StoreOrderModel $order): void
|
||||
{
|
||||
$userIds = UserModel::query()
|
||||
->where('type', UserModel::TYPE_STORE)
|
||||
->where('store_id', $order->store_id)
|
||||
->where('status', UserModel::STATUS_NORMAL)
|
||||
->pluck('id');
|
||||
|
||||
@@ -257,7 +257,6 @@ class ProductController extends BaseController
|
||||
|
||||
// 受影响门店:客户等级在受影响等级范围内的正常门店,通知其绑定的正常用户
|
||||
$userIds = UserModel::query()
|
||||
->where('type', UserModel::TYPE_STORE)
|
||||
->where('status', UserModel::STATUS_NORMAL)
|
||||
->whereIn('store_id', function ($q) use ($percentLevelIds) {
|
||||
$q->select('id')
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Http\Requests\Customer;
|
||||
use Modules\Common\Http\Requests\BaseFormRequest;
|
||||
|
||||
/**
|
||||
* 小程序用户绑定 验证(type=1 门店需 store_id,type=2 供应商需 supplier_id)
|
||||
* 小程序用户绑定 验证(绑定门店)
|
||||
*/
|
||||
class MiniUserBindRequest extends BaseFormRequest
|
||||
{
|
||||
@@ -14,21 +14,15 @@ class MiniUserBindRequest extends BaseFormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'type' => 'required|integer|in:1,2',
|
||||
'store_id' => 'required_if:type,1|nullable|integer|min:1',
|
||||
'supplier_id' => 'required_if:type,2|nullable|integer|min:1',
|
||||
'store_id' => 'required|integer|min:1',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'type.required' => '请选择用户类型',
|
||||
'type.in' => '用户类型只能是门店或供应商',
|
||||
'store_id.required_if' => '绑定门店时必须选择门店',
|
||||
'store_id.required' => '绑定门店时必须选择门店',
|
||||
'store_id.min' => '门店ID不正确',
|
||||
'supplier_id.required_if' => '绑定供应商时必须选择供应商',
|
||||
'supplier_id.min' => '供应商ID不正确',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,8 @@ class StoreFormRequest extends BaseFormRequest
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
$unique = Rule::unique('store', 'code');
|
||||
if ($this->isUpdate()) {
|
||||
$unique = $unique->ignore($this->route('id'));
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => 'required|string|max:100',
|
||||
'code' => ['required', 'string', 'max:50', $unique],
|
||||
'level_id' => 'required|integer|exists:customer_level,id',
|
||||
'contact' => 'nullable|string|max:50',
|
||||
'phone' => 'nullable|string|max:20',
|
||||
@@ -37,8 +31,6 @@ class StoreFormRequest extends BaseFormRequest
|
||||
return [
|
||||
'name.required' => '门店名称不能为空',
|
||||
'name.max' => '门店名称最长 100 个字符',
|
||||
'code.required' => '门店编码不能为空',
|
||||
'code.unique' => '门店编码已存在',
|
||||
'level_id.required' => '请选择客户等级',
|
||||
'level_id.exists' => '客户等级不存在',
|
||||
'payment_cycle_days.integer' => '回款周期必须为整数',
|
||||
|
||||
Reference in New Issue
Block a user