Merge branch 'feature/inv' into develop
This commit is contained in:
@@ -52,4 +52,82 @@ class InvItemController extends BaseController
|
||||
$data['manage_user_name'] = User::where('id', $data['manage_user_id'])->value('name');
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成物品二维码
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function generateQrCode(Request $request)
|
||||
{
|
||||
try {
|
||||
$id = $request->post('id');
|
||||
if (!$id) {
|
||||
return $this->error('物品ID不能为空');
|
||||
}
|
||||
|
||||
$item = \app\common\model\InvItem::find($id);
|
||||
if (!$item) {
|
||||
return $this->error('物品不存在');
|
||||
}
|
||||
|
||||
// 获取应用访问地址
|
||||
$appUrl = config('app.app_url', 'http://127.0.0.1:25616');
|
||||
|
||||
$qrCodeService = new \app\common\service\QrCodeService();
|
||||
$content = $qrCodeService->generateItemQrCode($id, $appUrl);
|
||||
$fileName = 'qr_item_' . $id . '_' . time() . '.png';
|
||||
|
||||
$result = $qrCodeService->generateAndUploadQrCode($id, $content, $fileName);
|
||||
|
||||
// 更新物品记录
|
||||
$item->qr_code = $result['path'];
|
||||
$item->qr_code_url = $result['url'];
|
||||
$item->save();
|
||||
|
||||
return $this->success([
|
||||
'qr_code_url' => $result['url'],
|
||||
'content' => $content,
|
||||
], '二维码生成成功');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除物品二维码
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function deleteQrCode(Request $request)
|
||||
{
|
||||
try {
|
||||
$id = $request->post('id');
|
||||
if (!$id) {
|
||||
return $this->error('物品ID不能为空');
|
||||
}
|
||||
|
||||
$item = \app\common\model\InvItem::find($id);
|
||||
if (!$item) {
|
||||
return $this->error('物品不存在');
|
||||
}
|
||||
|
||||
// 删除 OSS 文件
|
||||
if ($item->qr_code) {
|
||||
$qrCodeService = new \app\common\service\QrCodeService();
|
||||
$qrCodeService->deleteQrCode($item->qr_code);
|
||||
}
|
||||
|
||||
// 清空物品记录中的二维码字段
|
||||
$item->qr_code = null;
|
||||
$item->qr_code_url = null;
|
||||
$item->save();
|
||||
|
||||
return $this->success([], '二维码删除成功');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,4 +145,36 @@ class InvRequisitionController extends BaseController
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员代领
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function proxyClaim(Request $request)
|
||||
{
|
||||
try {
|
||||
$data = $this->service->proxyClaim($request, $request->post());
|
||||
return $this->success($data, '代领成功');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫码/代领记录查询
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function qrClaims(Request $request)
|
||||
{
|
||||
try {
|
||||
$data = $this->service->qrClaimsList($request);
|
||||
return $this->success($data);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\BaseController;
|
||||
use app\common\service\InvStockLogService;
|
||||
use support\Request;
|
||||
|
||||
class InvStockLogController extends BaseController
|
||||
{
|
||||
protected $service;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->service = new InvStockLogService();
|
||||
}
|
||||
|
||||
/**
|
||||
* 库存变化记录列表
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function list(Request $request)
|
||||
{
|
||||
try {
|
||||
$data = $this->service->getList($request->get());
|
||||
return $this->success($data);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,4 +98,22 @@ class InvRequisitionService
|
||||
//comment:审批意见
|
||||
return $appro->approve($param['requisition_id'], $param['approve_id'], $request->user->uid, $param['action'], $param['comment'], $attachment);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员代领
|
||||
* 调用 common 服务层方法
|
||||
*/
|
||||
public function proxyClaim(Request $request, array $data): array
|
||||
{
|
||||
return (new \app\common\service\InvRequisitionService())->proxyClaim($request, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫码/代领记录查询
|
||||
* 调用 common 服务层方法
|
||||
*/
|
||||
public function qrClaimsList(Request $request): array
|
||||
{
|
||||
return (new \app\common\service\InvRequisitionService())->qrClaimsList($request);
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,8 @@ class InvItem extends Model
|
||||
* 允许自动填充模型字段
|
||||
*/
|
||||
protected $fillable = ['id', 'company_id', 'item_code', 'item_name',
|
||||
'category_id', 'spec_model', 'unit', 'is_consumable', 'stock_qty', 'manage_user_id', 'location', 'remark',
|
||||
'category_id', 'spec_model', 'unit', 'is_consumable', 'allow_qr_claim', 'qr_code', 'qr_code_url',
|
||||
'stock_qty', 'manage_user_id', 'location', 'remark',
|
||||
'sort', 'status',
|
||||
'created_at', 'updated_at'];
|
||||
|
||||
|
||||
@@ -40,24 +40,42 @@ class InvRequisition extends Model
|
||||
/**
|
||||
* 允许自动填充模型字段
|
||||
*/
|
||||
protected $fillable = ['id', 'order_no', 'applicant_company_id', 'dept_id', 'applicant_id', 'applicant_name', 'leader_id', 'manager_id', 'borrow_reason', 'use_start_date', 'use_end_date', 'status', 'is_returned', 'remark', 'created_at', 'updated_at'];
|
||||
protected $fillable = ['id', 'order_no', 'applicant_company_id', 'dept_id', 'applicant_id', 'applicant_name', 'leader_id', 'manager_id', 'borrow_reason', 'use_start_date', 'use_end_date', 'status', 'claim_type', 'qr_claim_time', 'proxy_claim_user_id', 'proxy_claim_user_name', 'is_returned', 'remark', 'created_at', 'updated_at'];
|
||||
|
||||
/**
|
||||
* 不允许自动填充模型字段
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
// 领取方式常量
|
||||
const CLAIM_TYPE_NORMAL = 1; // 正常申请
|
||||
const CLAIM_TYPE_QR = 2; // 扫码领取
|
||||
const CLAIM_TYPE_PROXY = 3; // 管理员代领
|
||||
|
||||
const STATUS_DRAFT = 0; // 草稿
|
||||
const STATUS_LEADER = 10; // 待直属上级审批
|
||||
const STATUS_MANAGE = 20; // 待管理员审批
|
||||
const STATUS_APPROVED = 30; // 已通过
|
||||
const STATUS_REJECTEDT = 40; // 已驳回
|
||||
const STATUS_CANCELED = 50; // 已取消
|
||||
/**
|
||||
* 领取方式文本
|
||||
*/
|
||||
public static function getClaimTypeText($type)
|
||||
{
|
||||
return [
|
||||
self::CLAIM_TYPE_NORMAL => '正常申请',
|
||||
self::CLAIM_TYPE_QR => '扫码领取',
|
||||
self::CLAIM_TYPE_PROXY => '管理员代领',
|
||||
][$type] ?? '未知';
|
||||
}
|
||||
|
||||
public function approval()
|
||||
{
|
||||
return $this->hasMany('app\common\model\InvRequisitionApprove',
|
||||
'requisition_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 领用明细关联
|
||||
*/
|
||||
public function items()
|
||||
{
|
||||
return $this->hasMany('app\common\model\InvRequisitionItem',
|
||||
'requisition_id', 'id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
use support\Model;
|
||||
|
||||
/**
|
||||
* 物品库存变化记录模型
|
||||
*/
|
||||
class InvStockLog extends Model
|
||||
{
|
||||
protected $table = 'inv_stock_log';
|
||||
|
||||
protected $fillable = [
|
||||
'company_id',
|
||||
'item_id',
|
||||
'item_name_snap',
|
||||
'change_type',
|
||||
'change_qty',
|
||||
'before_qty',
|
||||
'after_qty',
|
||||
'biz_type',
|
||||
'biz_no',
|
||||
'operator_id',
|
||||
'operator_name',
|
||||
'remark',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
];
|
||||
|
||||
public const CHANGE_TYPE_IN = 1; // 入库
|
||||
public const CHANGE_TYPE_OUT_CLAIM = 2; // 出库(领用)
|
||||
public const CHANGE_TYPE_OUT_LOSS = 3; // 出库(损耗)
|
||||
public const CHANGE_TYPE_IN_RETURN = 4; // 入库(归还)
|
||||
|
||||
public const BIZ_TYPE_PURCHASE = 1; // 采购入库
|
||||
public const BIZ_TYPE_QR_CLAIM = 2; // 扫码领取
|
||||
public const BIZ_TYPE_PROXY_CLAIM = 3; // 管理员代领
|
||||
public const BIZ_TYPE_NORMAL_CLAIM = 4; // 正常领用(审批后)
|
||||
public const BIZ_TYPE_RETURN = 5; // 归还入库
|
||||
public const BIZ_TYPE_LOSS = 6; // 损耗出库
|
||||
|
||||
/**
|
||||
* 变动类型文本
|
||||
*/
|
||||
public static function getChangeTypeText($type)
|
||||
{
|
||||
return [
|
||||
self::CHANGE_TYPE_IN => '入库',
|
||||
self::CHANGE_TYPE_OUT_CLAIM => '出库(领用)',
|
||||
self::CHANGE_TYPE_OUT_LOSS => '出库(损耗)',
|
||||
self::CHANGE_TYPE_IN_RETURN => '入库(归还)',
|
||||
][$type] ?? '未知';
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务类型文本
|
||||
*/
|
||||
public static function getBizTypeText($type)
|
||||
{
|
||||
return [
|
||||
self::BIZ_TYPE_PURCHASE => '采购入库',
|
||||
self::BIZ_TYPE_QR_CLAIM => '扫码领取',
|
||||
self::BIZ_TYPE_PROXY_CLAIM => '管理员代领',
|
||||
self::BIZ_TYPE_NORMAL_CLAIM => '正常领用',
|
||||
self::BIZ_TYPE_RETURN => '归还入库',
|
||||
self::BIZ_TYPE_LOSS => '损耗出库',
|
||||
][$type] ?? '未知';
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联物品
|
||||
*/
|
||||
public function item()
|
||||
{
|
||||
return $this->belongsTo(InvItem::class, 'item_id', 'id');
|
||||
}
|
||||
}
|
||||
@@ -124,16 +124,28 @@ class InvItemService
|
||||
* @param int $itemId 物品ID
|
||||
* @param int $changeQty 变动数量:>0 入库,<0 出库
|
||||
* @param bool $allowNegative 是否允许库存变成负数,默认不允许
|
||||
*
|
||||
* @param array $options 额外选项(用于记录日志)
|
||||
* - biz_type: 业务类型
|
||||
* - biz_no: 单据号
|
||||
* - operator_id: 操作人ID
|
||||
* - operator_name: 操作人姓名
|
||||
* - remark: 备注
|
||||
* @return InvItem 最新的物品记录
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function changeStock(int $itemId, int $changeQty, bool $allowNegative = false): InvItem
|
||||
public function changeStock(int $itemId, int $changeQty, bool $allowNegative = false, array $options = []): InvItem
|
||||
{
|
||||
if ($changeQty === 0) {
|
||||
throw new BusinessException('库存变动数量不能为 0');
|
||||
}
|
||||
|
||||
// 获取变动前的库存(用于日志)
|
||||
$itemBefore = InvItem::query()->find($itemId);
|
||||
if (!$itemBefore) {
|
||||
throw new BusinessException('物品不存在');
|
||||
}
|
||||
$beforeQty = $itemBefore->stock_qty;
|
||||
|
||||
$now = time();
|
||||
|
||||
// 构建查询
|
||||
@@ -154,39 +166,101 @@ class InvItemService
|
||||
// 更新不到行:
|
||||
// 1) 物品不存在
|
||||
// 2) 或者库存不足(不允许负数时)
|
||||
$exists = InvItem::query()->where('id', $itemId)->exists();
|
||||
if (! $exists) {
|
||||
throw new BusinessException('物品不存在');
|
||||
}
|
||||
throw new BusinessException('库存不足,无法完成库存变动');
|
||||
}
|
||||
|
||||
// 再查一次最新数据返回(在外层事务中也会保持一致)
|
||||
/** @var InvItem $item */
|
||||
$item = InvItem::query()->find($itemId);
|
||||
|
||||
// 记录库存变化日志
|
||||
$this->recordStockLog([
|
||||
'item_id' => $itemId,
|
||||
'item_name_snap' => $item->item_name,
|
||||
'change_qty' => $changeQty,
|
||||
'before_qty' => $beforeQty,
|
||||
'after_qty' => $item->stock_qty,
|
||||
'biz_type' => $options['biz_type'] ?? null,
|
||||
'biz_no' => $options['biz_no'] ?? null,
|
||||
'operator_id' => $options['operator_id'] ?? null,
|
||||
'operator_name' => $options['operator_name'] ?? null,
|
||||
'remark' => $options['remark'] ?? null,
|
||||
], $changeQty);
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 出库(减库存,默认不允许负数)
|
||||
*
|
||||
* @param int $itemId 物品ID
|
||||
* @param int $qty 出库数量
|
||||
* @param bool $allowNegative 是否允许负数
|
||||
* @param array $options 日志选项(同 changeStock)
|
||||
* @return InvItem
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function stockOut(int $itemId, int $qty, bool $allowNegative = false): InvItem
|
||||
public function stockOut(int $itemId, int $qty, bool $allowNegative = false, array $options = []): InvItem
|
||||
{
|
||||
if ($qty <= 0) {
|
||||
throw new BusinessException('出库数量必须大于 0');
|
||||
}
|
||||
return $this->changeStock($itemId, -$qty, $allowNegative);
|
||||
// 确定变动类型:2-出库(领用)或 3-出库(损耗)
|
||||
$changeType = $options['change_type'] ?? \app\common\model\InvStockLog::CHANGE_TYPE_OUT_CLAIM;
|
||||
|
||||
return $this->changeStock($itemId, -$qty, $allowNegative, array_merge($options, [
|
||||
'change_type' => $changeType,
|
||||
'remark' => $options['remark'] ?? '领用出库',
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 入库(加库存)
|
||||
*
|
||||
* @param int $itemId 物品ID
|
||||
* @param int $qty 入库数量
|
||||
* @param array $options 日志选项(同 changeStock)
|
||||
* @return InvItem
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function stockIn(int $itemId, int $qty): InvItem
|
||||
public function stockIn(int $itemId, int $qty, array $options = []): InvItem
|
||||
{
|
||||
if ($qty <= 0) {
|
||||
throw new BusinessException('入库数量必须大于 0');
|
||||
}
|
||||
return $this->changeStock($itemId, $qty, true);
|
||||
// 确定变动类型:1-入库 或 4-入库(归还)
|
||||
$changeType = $options['change_type'] ?? \app\common\model\InvStockLog::CHANGE_TYPE_IN;
|
||||
|
||||
return $this->changeStock($itemId, $qty, true, array_merge($options, [
|
||||
'change_type' => $changeType,
|
||||
'remark' => $options['remark'] ?? '入库',
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录库存变化日志
|
||||
*
|
||||
* @param array $logData
|
||||
* @param int $changeQty 变动数量
|
||||
* @return void
|
||||
*/
|
||||
private function recordStockLog(array $logData, int $changeQty): void
|
||||
{
|
||||
try {
|
||||
// 根据变动数量确定变动类型(如果未指定)
|
||||
if (!isset($logData['change_type'])) {
|
||||
if ($changeQty > 0) {
|
||||
$logData['change_type'] = \app\common\model\InvStockLog::CHANGE_TYPE_IN;
|
||||
} else {
|
||||
$logData['change_type'] = \app\common\model\InvStockLog::CHANGE_TYPE_OUT_CLAIM;
|
||||
}
|
||||
}
|
||||
|
||||
(new InvStockLogService())->record($logData);
|
||||
} catch (\Throwable $e) {
|
||||
// 日志记录失败不影响主业务,仅记录错误
|
||||
error_log('库存日志记录失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -394,7 +394,7 @@ class InvRequisitionItemReturnService
|
||||
public function serviceReturnItemConfirm(Request $request, array $data, string $scene = 'returnConfirm')
|
||||
{
|
||||
try {
|
||||
$attachment = $request->post('attachment');
|
||||
$attachment = $request->post('attachment') ?? [];
|
||||
unset($data['attachment']);
|
||||
// 归还确认是业务顶层方法,这里统一使用事务
|
||||
return Db::transaction(function () use ($request, $data, $scene, $attachment) {
|
||||
@@ -555,7 +555,7 @@ class InvRequisitionItemReturnService
|
||||
public function serviceReturnConfirm(Request $request, array $data, string $scene = 'returnConfirm')
|
||||
{
|
||||
try {
|
||||
$attachment = $request->post('attachment');
|
||||
$attachment = $request->post('attachment') ?? [];
|
||||
unset($data['attachment']);
|
||||
// 归还确认是业务顶层方法,这里统一使用事务
|
||||
return Db::transaction(function () use ($request, $data, $scene, $attachment) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace app\common\service;
|
||||
|
||||
use app\common\model\InvItem;
|
||||
use app\common\model\InvStockLog;
|
||||
use app\common\validate\InvRequisitionValidate;
|
||||
use app\common\traits\crud\CrudServiceTrait;
|
||||
use app\common\model\InvRequisition;
|
||||
@@ -313,4 +314,268 @@ class InvRequisitionService
|
||||
{
|
||||
return 'RQ' . date('Ymd') . strtoupper(substr(uniqid(), -6));
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫码领取物品
|
||||
*
|
||||
* @param Request $request
|
||||
* @param array $data
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function qrClaim(Request $request, array $data): array
|
||||
{
|
||||
return Db::transaction(function () use ($request, $data) {
|
||||
$itemId = (int)($data['item_id'] ?? 0);
|
||||
$qty = (int)($data['qty'] ?? 1);
|
||||
|
||||
if ($itemId <= 0) {
|
||||
throw new BusinessException('物品ID无效');
|
||||
}
|
||||
if ($qty <= 0) {
|
||||
throw new BusinessException('领取数量必须大于0');
|
||||
}
|
||||
|
||||
// 获取物品信息
|
||||
$item = InvItem::find($itemId);
|
||||
if (!$item) {
|
||||
throw new BusinessException('物品不存在');
|
||||
}
|
||||
|
||||
// 检查是否允许扫码领取
|
||||
if (!$item->allow_qr_claim) {
|
||||
throw new BusinessException('该物品不允许扫码领取');
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
$user = (new UserService())->getUserInfo($request->user->uid, '');
|
||||
$applicantId = (int)$user['user']['id'];
|
||||
$applicantName = (string)$user['user']['name'];
|
||||
|
||||
// 检查库存
|
||||
if ($item->stock_qty < $qty) {
|
||||
throw new BusinessException('库存不足,当前库存: ' . $item->stock_qty);
|
||||
}
|
||||
|
||||
// 创建领用申请记录(直接完成)
|
||||
$requisition = InvRequisition::create([
|
||||
'order_no' => $this->generateOrderNo(),
|
||||
'applicant_company_id' => $user['user']['company_id'] ?? 0,
|
||||
'dept_id' => $user['user']['dept_id'] ?? 0,
|
||||
'applicant_id' => $applicantId,
|
||||
'applicant_name' => $applicantName,
|
||||
'leader_id' => $user['user']['report_to'] ?? 0,
|
||||
'manager_id' => $item->manage_user_id,
|
||||
'borrow_reason' => '扫码领取',
|
||||
'use_start_date' => date('Y-m-d', time()),
|
||||
'use_end_date' => date('Y-m-d', time()),
|
||||
'status' => 19, // 已审批(无需归还)
|
||||
'claim_type' => InvRequisition::CLAIM_TYPE_QR,
|
||||
'qr_claim_time' => time(),
|
||||
'remark' => $data['remark'] ?? '扫码领取',
|
||||
]);
|
||||
|
||||
// 创建领用明细
|
||||
InvRequisitionItem::create([
|
||||
'requisition_id' => $requisition->id,
|
||||
'item_id' => $itemId,
|
||||
'item_name_snap' => $item->item_name,
|
||||
'spec_model_snap' => $item->spec_model,
|
||||
'unit_snap' => $item->unit,
|
||||
'is_consumable_snap' => $item->is_consumable,
|
||||
'request_qty' => $qty,
|
||||
'approved_qty' => $qty,
|
||||
'remark' => '扫码领取',
|
||||
]);
|
||||
|
||||
// 扣减库存
|
||||
(new InvItemService())->stockOut($itemId, $qty, false, [
|
||||
'biz_type' => InvStockLog::BIZ_TYPE_QR_CLAIM,
|
||||
'biz_no' => $requisition->order_no,
|
||||
'operator_id' => $applicantId,
|
||||
'operator_name' => $applicantName,
|
||||
'remark' => '扫码领取',
|
||||
]);
|
||||
|
||||
return $requisition->toArray();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员代领
|
||||
*
|
||||
* @param Request $request
|
||||
* @param array $data
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function proxyClaim(Request $request, array $data): array
|
||||
{
|
||||
return Db::transaction(function () use ($request, $data) {
|
||||
$itemId = (int)($data['item_id'] ?? 0);
|
||||
$qty = (int)($data['qty'] ?? 1);
|
||||
$targetUserId = (int)($data['target_user_id'] ?? 0);
|
||||
|
||||
if ($itemId <= 0) {
|
||||
throw new BusinessException('物品ID无效');
|
||||
}
|
||||
if ($qty <= 0) {
|
||||
throw new BusinessException('领取数量必须大于0');
|
||||
}
|
||||
if ($targetUserId <= 0) {
|
||||
throw new BusinessException('请选择代领用户');
|
||||
}
|
||||
|
||||
// 获取物品信息
|
||||
$item = InvItem::find($itemId);
|
||||
if (!$item) {
|
||||
throw new BusinessException('物品不存在');
|
||||
}
|
||||
|
||||
// 获取操作人(管理员)信息
|
||||
$proxyUser = (new UserService())->getUserInfo($request->user->uid, '');
|
||||
$proxyUserId = (int)$proxyUser['user']['id'];
|
||||
$proxyUserName = (string)$proxyUser['user']['name'];
|
||||
|
||||
// 验证操作人是否是物品管理员
|
||||
if ($item->manage_user_id != $proxyUserId) {
|
||||
throw new BusinessException('您不是该物品的管理员,无权代领');
|
||||
}
|
||||
|
||||
// 获取目标用户信息
|
||||
$targetUser = (new UserService())->getUserInfo($targetUserId, '');
|
||||
if (!$targetUser['user']) {
|
||||
throw new BusinessException('目标用户不存在');
|
||||
}
|
||||
|
||||
// 检查库存
|
||||
if ($item->stock_qty < $qty) {
|
||||
throw new BusinessException('库存不足,当前库存: ' . $item->stock_qty);
|
||||
}
|
||||
|
||||
// 创建领用申请记录(直接完成)
|
||||
$requisition = InvRequisition::create([
|
||||
'order_no' => $this->generateOrderNo(),
|
||||
'applicant_company_id' => $targetUser['user']['company_id'] ?? 0,
|
||||
'dept_id' => $targetUser['user']['dept_id'] ?? 0,
|
||||
'applicant_id' => $targetUserId,
|
||||
'applicant_name' => $targetUser['user']['name'],
|
||||
'leader_id' => $targetUser['user']['report_to'] ?? 0,
|
||||
'manager_id' => $item->manage_user_id,
|
||||
'borrow_reason' => $data['borrow_reason'] ?? '管理员代领',
|
||||
'use_start_date' => date('Y-m-d', time()),
|
||||
'use_end_date' => date('Y-m-d', time()),
|
||||
'status' => 19, // 已审批(无需归还)
|
||||
'claim_type' => InvRequisition::CLAIM_TYPE_PROXY,
|
||||
'proxy_claim_user_id' => $proxyUserId,
|
||||
'proxy_claim_user_name' => $proxyUserName,
|
||||
'remark' => $data['remark'] ?? '管理员代领',
|
||||
]);
|
||||
|
||||
// 创建领用明细
|
||||
InvRequisitionItem::create([
|
||||
'requisition_id' => $requisition->id,
|
||||
'item_id' => $itemId,
|
||||
'item_name_snap' => $item->item_name,
|
||||
'spec_model_snap' => $item->spec_model,
|
||||
'unit_snap' => $item->unit,
|
||||
'is_consumable_snap' => $item->is_consumable,
|
||||
'request_qty' => $qty,
|
||||
'approved_qty' => $qty,
|
||||
'remark' => $data['remark'] ?? '管理员代领',
|
||||
]);
|
||||
|
||||
// 扣减库存
|
||||
(new InvItemService())->stockOut($itemId, $qty, false, [
|
||||
'biz_type' => InvStockLog::BIZ_TYPE_PROXY_CLAIM,
|
||||
'biz_no' => $requisition->order_no,
|
||||
'operator_id' => $proxyUserId,
|
||||
'operator_name' => $proxyUserName,
|
||||
'remark' => '管理员代领',
|
||||
]);
|
||||
|
||||
return $requisition->toArray();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询扫码/代领记录列表
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function qrClaimsList(Request $request): array
|
||||
{
|
||||
$query = InvRequisition::query()->with(['items']);
|
||||
|
||||
// 只查询扫码领取和代领记录
|
||||
$query->whereIn('claim_type', [
|
||||
InvRequisition::CLAIM_TYPE_QR,
|
||||
InvRequisition::CLAIM_TYPE_PROXY
|
||||
]);
|
||||
|
||||
// 按申请人筛选(移动端"我的记录"功能)
|
||||
if ($request->get('applicant_id') === 'self') {
|
||||
$query->where('applicant_id', $request->user->uid);
|
||||
} elseif (!empty($request->get('applicant_id'))) {
|
||||
$query->where('applicant_id', $request->get('applicant_id'));
|
||||
}
|
||||
|
||||
// 按领取方式筛选
|
||||
if (!empty($request->get('claim_type'))) {
|
||||
$query->where('claim_type', $request->get('claim_type'));
|
||||
}
|
||||
|
||||
// 按代领操作人筛选
|
||||
if (!empty($request->get('proxy_user_id'))) {
|
||||
$query->where('proxy_claim_user_id', $request->get('proxy_user_id'));
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if (!empty($request->get('start_time'))) {
|
||||
$query->where('qr_claim_time', '>=', strtotime($request->get('start_time')));
|
||||
}
|
||||
if (!empty($request->get('end_time'))) {
|
||||
$query->where('qr_claim_time', '<=', strtotime($request->get('end_time')));
|
||||
}
|
||||
|
||||
// 排序
|
||||
$query->orderBy('id', 'desc');
|
||||
|
||||
// 分页
|
||||
$perPage = $request->get('size', 20);
|
||||
$page = $request->get('current', 1);
|
||||
|
||||
$result = $query->paginate($perPage, ['*'], 'page', $page);
|
||||
|
||||
return [
|
||||
'records' => $result->getCollection()->map(function ($req) {
|
||||
return [
|
||||
'id' => $req->id,
|
||||
'order_no' => $req->order_no,
|
||||
'applicant_id' => $req->applicant_id,
|
||||
'applicant_name' => $req->applicant_name,
|
||||
'claim_type' => $req->claim_type,
|
||||
'claim_type_text' => InvRequisition::getClaimTypeText($req->claim_type),
|
||||
'qr_claim_time' => $req->qr_claim_time ? date('Y-m-d H:i:s', $req->qr_claim_time) : null,
|
||||
'proxy_claim_user_id' => $req->proxy_claim_user_id,
|
||||
'proxy_claim_user_name' => $req->proxy_claim_user_name,
|
||||
'status' => $req->status,
|
||||
'borrow_reason' => $req->borrow_reason,
|
||||
'remark' => $req->remark,
|
||||
'created_at' => $req->created_at ? $req->created_at->format('Y-m-d H:i:s') : null,
|
||||
'items' => $req->items->map(function ($item) {
|
||||
return [
|
||||
'item_id' => $item->item_id,
|
||||
'item_name_snap' => $item->item_name_snap,
|
||||
'approved_qty' => $item->approved_qty,
|
||||
];
|
||||
})->toArray(),
|
||||
];
|
||||
})->toArray(),
|
||||
'total' => $result->total(),
|
||||
'current' => $result->currentPage(),
|
||||
'size' => $result->perPage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
use app\common\model\InvStockLog;
|
||||
use support\exception\BusinessException;
|
||||
|
||||
/**
|
||||
* 物品库存变化记录服务
|
||||
*/
|
||||
class InvStockLogService
|
||||
{
|
||||
/**
|
||||
* 记录库存变化
|
||||
*
|
||||
* @param array $data 日志数据
|
||||
* @return InvStockLog
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function record(array $data): InvStockLog
|
||||
{
|
||||
$required = ['item_id', 'change_type', 'change_qty', 'before_qty', 'after_qty'];
|
||||
foreach ($required as $field) {
|
||||
if (!isset($data[$field])) {
|
||||
throw new BusinessException("库存日志缺少必需字段: {$field}");
|
||||
}
|
||||
}
|
||||
|
||||
return InvStockLog::create([
|
||||
'company_id' => $data['company_id'] ?? 0,
|
||||
'item_id' => $data['item_id'],
|
||||
'item_name_snap' => $data['item_name_snap'] ?? null,
|
||||
'change_type' => $data['change_type'],
|
||||
'change_qty' => $data['change_qty'],
|
||||
'before_qty' => $data['before_qty'],
|
||||
'after_qty' => $data['after_qty'],
|
||||
'biz_type' => $data['biz_type'] ?? null,
|
||||
'biz_no' => $data['biz_no'] ?? null,
|
||||
'operator_id' => $data['operator_id'] ?? null,
|
||||
'operator_name' => $data['operator_name'] ?? null,
|
||||
'remark' => $data['remark'] ?? null,
|
||||
'created_at' => time(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询库存变化记录列表
|
||||
*
|
||||
* @param array $params 查询参数
|
||||
* @return array
|
||||
*/
|
||||
public function getList(array $params): array
|
||||
{
|
||||
$query = InvStockLog::query()->with(['item']);
|
||||
|
||||
// 物品ID筛选
|
||||
if (!empty($params['item_id'])) {
|
||||
$query->where('item_id', $params['item_id']);
|
||||
}
|
||||
|
||||
// 变动类型筛选
|
||||
if (!empty($params['change_type'])) {
|
||||
$query->where('change_type', $params['change_type']);
|
||||
}
|
||||
|
||||
// 业务类型筛选
|
||||
if (!empty($params['biz_type'])) {
|
||||
$query->where('biz_type', $params['biz_type']);
|
||||
}
|
||||
|
||||
// 单据号筛选
|
||||
if (!empty($params['biz_no'])) {
|
||||
$query->where('biz_no', 'like', '%' . $params['biz_no'] . '%');
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if (!empty($params['start_time'])) {
|
||||
$query->where('created_at', '>=', strtotime($params['start_time']));
|
||||
}
|
||||
if (!empty($params['end_time'])) {
|
||||
$query->where('created_at', '<=', strtotime($params['end_time']));
|
||||
}
|
||||
|
||||
// 排序
|
||||
$query->orderBy('id', 'desc');
|
||||
|
||||
// 分页
|
||||
$perPage = $params['size'] ?? 20;
|
||||
$page = $params['current'] ?? 1;
|
||||
|
||||
$result = $query->paginate($perPage, ['*'], 'page', $page);
|
||||
|
||||
return [
|
||||
'records' => $result->getCollection()->map(function ($log) {
|
||||
return [
|
||||
'id' => $log->id,
|
||||
'item_id' => $log->item_id,
|
||||
'item_name' => $log->item_name_snap,
|
||||
'change_type' => $log->change_type,
|
||||
'change_type_text' => InvStockLog::getChangeTypeText($log->change_type),
|
||||
'change_qty' => $log->change_qty,
|
||||
'before_qty' => $log->before_qty,
|
||||
'after_qty' => $log->after_qty,
|
||||
'biz_type' => $log->biz_type,
|
||||
'biz_type_text' => $log->biz_type ? InvStockLog::getBizTypeText($log->biz_type) : null,
|
||||
'biz_no' => $log->biz_no,
|
||||
'operator_id' => $log->operator_id,
|
||||
'operator_name' => $log->operator_name,
|
||||
'remark' => $log->remark,
|
||||
'created_at' => date('Y-m-d H:i:s', $log->created_at),
|
||||
];
|
||||
})->toArray(),
|
||||
'total' => $result->total(),
|
||||
'current' => $result->currentPage(),
|
||||
'size' => $result->perPage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
use support\exception\BusinessException;
|
||||
|
||||
/**
|
||||
* 二维码生成服务
|
||||
*/
|
||||
class QrCodeService
|
||||
{
|
||||
/**
|
||||
* 生成物品二维码
|
||||
*
|
||||
* @param int $itemId 物品ID
|
||||
* @param string $appUrl 应用访问地址
|
||||
* @return string 二维码内容URL
|
||||
*/
|
||||
public function generateItemQrCode(int $itemId, string $appUrl): string
|
||||
{
|
||||
return rtrim($appUrl, '/') . '/mobile/inv/claim/' . $itemId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成二维码图片并上传到 OSS
|
||||
*
|
||||
* @param int $itemId 物品ID
|
||||
* @param string $content 二维码内容
|
||||
* @param string $fileName 文件名
|
||||
* @return array 返回OSS路径和完整URL
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function generateAndUploadQrCode(int $itemId, string $content, string $fileName): array
|
||||
{
|
||||
// 使用 endroid/qr-code 库生成二维码
|
||||
// 如果未安装,使用 API 方式生成
|
||||
|
||||
$tempPath = $this->generateQrCodeImage($content, $fileName);
|
||||
|
||||
if (!$tempPath) {
|
||||
throw new BusinessException('二维码生成失败');
|
||||
}
|
||||
|
||||
try {
|
||||
// 上传到 OSS
|
||||
$ossService = new OssService();
|
||||
$ossPath = 'qr/' . $fileName;
|
||||
$ossUrl = $ossService->uploadImage($tempPath, $ossPath);
|
||||
|
||||
// 删除临时文件
|
||||
if (file_exists($tempPath)) {
|
||||
unlink($tempPath);
|
||||
}
|
||||
|
||||
return [
|
||||
'path' => $ossPath,
|
||||
'url' => $ossUrl,
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
// 删除临时文件
|
||||
if (file_exists($tempPath)) {
|
||||
unlink($tempPath);
|
||||
}
|
||||
throw new BusinessException('二维码上传失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成二维码图片(使用 QR Server API 或本地库)
|
||||
*
|
||||
* @param string $content 二维码内容
|
||||
* @param string $fileName 文件名
|
||||
* @return string 临时文件路径,失败返回 false
|
||||
*/
|
||||
private function generateQrCodeImage(string $content, string $fileName)
|
||||
{
|
||||
$tempDir = runtime_path() . '/qr_temp/';
|
||||
if (!is_dir($tempDir)) {
|
||||
mkdir($tempDir, 0755, true);
|
||||
}
|
||||
|
||||
$tempPath = $tempDir . $fileName;
|
||||
|
||||
// 方式1: 使用 endroid/qr-code 库(推荐)
|
||||
if (class_exists('\Endroid\QrCode\QrCode')) {
|
||||
return $this->generateWithEndroid($content, $tempPath);
|
||||
}
|
||||
|
||||
// 方式2: 使用在线 API(备用方案)
|
||||
return $this->generateWithApi($content, $tempPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 endroid/qr-code 生成二维码
|
||||
*/
|
||||
private function generateWithEndroid(string $content, string $tempPath): string
|
||||
{
|
||||
try {
|
||||
// 5.x 版本使用静态工厂方法
|
||||
if (method_exists(\Endroid\QrCode\QrCode::class, 'create')) {
|
||||
$qrCode = \Endroid\QrCode\QrCode::create($content)
|
||||
->setSize(300)
|
||||
->setMargin(10);
|
||||
} else {
|
||||
// 4.x 及以下版本
|
||||
$qrCode = new \Endroid\QrCode\QrCode($content);
|
||||
if (method_exists($qrCode, 'setSize')) {
|
||||
$qrCode->setSize(300);
|
||||
}
|
||||
if (method_exists($qrCode, 'setMargin')) {
|
||||
$qrCode->setMargin(10);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 Writer
|
||||
if (class_exists('\Endroid\QrCode\Writer\PngWriter')) {
|
||||
$writer = new \Endroid\QrCode\Writer\PngWriter();
|
||||
} else {
|
||||
// 旧版本可能使用不同的命名空间
|
||||
$writer = new \Endroid\QrCode\Writer();
|
||||
}
|
||||
|
||||
$result = $writer->write($qrCode);
|
||||
|
||||
// 保存文件
|
||||
if (method_exists($result, 'getString')) {
|
||||
file_put_contents($tempPath, $result->getString());
|
||||
} else {
|
||||
// 更旧版本可能直接返回字符串
|
||||
file_put_contents($tempPath, $result);
|
||||
}
|
||||
|
||||
return $tempPath;
|
||||
} catch (\Throwable $e) {
|
||||
// 如果 endroid 库使用失败,回退到在线 API
|
||||
error_log('endroid/qr-code 生成失败: ' . $e->getMessage() . ',使用在线 API 备用方案');
|
||||
return $this->generateWithApi($content, $tempPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用在线 API 生成二维码(备用方案)
|
||||
*/
|
||||
private function generateWithApi(string $content, string $tempPath): string
|
||||
{
|
||||
$apiUrl = 'https://api.qrserver.com/v1/create-qr-code/?';
|
||||
$params = [
|
||||
'size' => '300x300',
|
||||
'data' => $content,
|
||||
'margin' => '10',
|
||||
];
|
||||
|
||||
$url = $apiUrl . http_build_query($params);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||
$imageData = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode !== 200 || !$imageData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file_put_contents($tempPath, $imageData);
|
||||
return $tempPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 OSS 上的二维码文件
|
||||
*
|
||||
* @param string $ossPath OSS路径
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteQrCode(string $ossPath): bool
|
||||
{
|
||||
try {
|
||||
$ossService = new OssService();
|
||||
return $ossService->deleteFile($ossPath);
|
||||
} catch (\Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,22 +8,27 @@ class InvItemValidate extends Validate
|
||||
{
|
||||
// 规则
|
||||
protected $rule = [
|
||||
|
||||
'id' => 'require|integer',
|
||||
];
|
||||
|
||||
// 提示
|
||||
protected $message = [
|
||||
|
||||
'id.require' => '物品ID不能为空',
|
||||
'id.integer' => '物品ID必须为整数',
|
||||
];
|
||||
|
||||
// 场景
|
||||
protected $scene = [
|
||||
'store' => [
|
||||
|
||||
'id',
|
||||
],
|
||||
|
||||
'update' => [
|
||||
'id',
|
||||
],
|
||||
|
||||
]
|
||||
'qrCode' => [
|
||||
'id',
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -15,6 +15,11 @@ class InvRequisitionValidate extends Validate
|
||||
'items' => 'require|array',
|
||||
'items.*.item_id' => 'require|number',
|
||||
'items.*.request_qty' => 'require|number|gt:0',
|
||||
// 扫码领取规则
|
||||
'item_id' => 'require|integer|gt:0',
|
||||
'qty' => 'require|integer|gt:0',
|
||||
// 管理员代领规则
|
||||
'target_user_id' => 'require|integer|gt:0',
|
||||
];
|
||||
|
||||
// 提示
|
||||
@@ -32,16 +37,33 @@ class InvRequisitionValidate extends Validate
|
||||
'items.*.request_qty.require' => '申请数量不能为空',
|
||||
'items.*.request_qty.number' => '申请数量格式错误',
|
||||
'items.*.request_qty.gt' => '申请数量必须大于0',
|
||||
'item_id.require' => '物品ID不能为空',
|
||||
'item_id.integer' => '物品ID必须为整数',
|
||||
'item_id.gt' => '物品ID必须大于0',
|
||||
'qty.require' => '领取数量不能为空',
|
||||
'qty.integer' => '领取数量必须为整数',
|
||||
'qty.gt' => '领取数量必须大于0',
|
||||
'target_user_id.require' => '代领用户不能为空',
|
||||
'target_user_id.integer' => '代领用户ID必须为整数',
|
||||
'target_user_id.gt' => '代领用户ID必须大于0',
|
||||
];
|
||||
|
||||
// 场景
|
||||
protected $scene = [
|
||||
'store' => [
|
||||
|
||||
'applicant_id',
|
||||
'borrow_reason',
|
||||
'use_start_date',
|
||||
'use_end_date',
|
||||
'items'
|
||||
],
|
||||
|
||||
'update' => [
|
||||
|
||||
'applicant_id',
|
||||
'borrow_reason',
|
||||
'use_start_date',
|
||||
'use_end_date',
|
||||
'items'
|
||||
],
|
||||
|
||||
'apply' => [
|
||||
@@ -50,6 +72,18 @@ class InvRequisitionValidate extends Validate
|
||||
'use_start_date',
|
||||
'use_end_date',
|
||||
'items'
|
||||
]
|
||||
],
|
||||
|
||||
'qrClaim' => [
|
||||
'item_id',
|
||||
'qty',
|
||||
],
|
||||
|
||||
'proxyClaim' => [
|
||||
'item_id',
|
||||
'qty',
|
||||
'target_user_id',
|
||||
'borrow_reason',
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -147,4 +147,38 @@ class InvRequisitionController extends BaseController
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫码领取物品
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function qrClaim(Request $request)
|
||||
{
|
||||
try {
|
||||
$data = $this->service->qrClaim($request, $request->post());
|
||||
return $this->success($data, '领取成功');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 我的扫码/代领记录
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function myQrClaims(Request $request)
|
||||
{
|
||||
try {
|
||||
// 只查询当前用户的扫码记录,通过设置 applicant_id=self 实现
|
||||
$request->setGet('applicant_id','self');
|
||||
$data = $this->service->qrClaimsList($request);
|
||||
return $this->success($data);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -49,7 +49,8 @@
|
||||
"mpdf/mpdf": "^8.2",
|
||||
"jaeger/querylist": "^4.4",
|
||||
"zoujingli/ip2region": "^3.0",
|
||||
"webman/log": "^2.1"
|
||||
"webman/log": "^2.1",
|
||||
"endroid/qr-code": "^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-event": "For better performance. "
|
||||
|
||||
Generated
+154
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "bf987c3cd5510fc0637d6b64d460dde1",
|
||||
"content-hash": "28a80d57e5dbc1da167b66780b6dfd00",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aliyuncs/oss-sdk-php",
|
||||
@@ -51,6 +51,55 @@
|
||||
},
|
||||
"time": "2024-10-28T10:41:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
"version": "v3.0.3",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://mirrors.cloud.tencent.com/repository/composer/bacon/bacon-qr-code/v3.0.3/bacon-bacon-qr-code-v3.0.3.zip",
|
||||
"reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"dasprid/enum": "^1.0.3",
|
||||
"ext-iconv": "*",
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phly/keep-a-changelog": "^2.12",
|
||||
"phpunit/phpunit": "^10.5.11 || ^11.0.4",
|
||||
"spatie/phpunit-snapshot-assertions": "^5.1.5",
|
||||
"spatie/pixelmatch-php": "^1.2.0",
|
||||
"squizlabs/php_codesniffer": "^3.9"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-imagick": "to generate QR code images"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BaconQrCode\\": "src/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ben Scholzen 'DASPRiD'",
|
||||
"email": "mail@dasprids.de",
|
||||
"homepage": "https://dasprids.de/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "BaconQrCode is a QR code generator for PHP.",
|
||||
"homepage": "https://github.com/Bacon/BaconQrCode",
|
||||
"support": {
|
||||
"issues": "https://github.com/Bacon/BaconQrCode/issues",
|
||||
"source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.3"
|
||||
},
|
||||
"time": "2025-11-19T17:15:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.14.1",
|
||||
@@ -259,6 +308,50 @@
|
||||
],
|
||||
"time": "2024-11-12T16:29:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dasprid/enum",
|
||||
"version": "1.0.7",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://mirrors.cloud.tencent.com/repository/composer/dasprid/enum/1.0.7/dasprid-enum-1.0.7.zip",
|
||||
"reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1 <9.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
|
||||
"squizlabs/php_codesniffer": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DASPRiD\\Enum\\": "src/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ben Scholzen 'DASPRiD'",
|
||||
"email": "mail@dasprids.de",
|
||||
"homepage": "https://dasprids.de/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "PHP 7.1 enum implementation",
|
||||
"keywords": [
|
||||
"enum",
|
||||
"map"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/DASPRiD/Enum/issues",
|
||||
"source": "https://github.com/DASPRiD/Enum/tree/1.0.7"
|
||||
},
|
||||
"time": "2025-09-16T12:23:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/inflector",
|
||||
"version": "2.1.0",
|
||||
@@ -349,6 +442,66 @@
|
||||
],
|
||||
"time": "2025-08-10T19:31:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "endroid/qr-code",
|
||||
"version": "6.0.9",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://mirrors.cloud.tencent.com/repository/composer/endroid/qr-code/6.0.9/endroid-qr-code-6.0.9.zip",
|
||||
"reference": "21e888e8597440b2205e2e5c484b6c8e556bcd1a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"bacon/bacon-qr-code": "^3.0",
|
||||
"php": "^8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"endroid/quality": "dev-main",
|
||||
"ext-gd": "*",
|
||||
"khanamiryan/qrcode-detector-decoder": "^2.0.2",
|
||||
"setasign/fpdf": "^1.8.2"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gd": "Enables you to write PNG images",
|
||||
"khanamiryan/qrcode-detector-decoder": "Enables you to use the image validator",
|
||||
"roave/security-advisories": "Makes sure package versions with known security issues are not installed",
|
||||
"setasign/fpdf": "Enables you to use the PDF writer"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "6.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Endroid\\QrCode\\": "src/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jeroen van den Enden",
|
||||
"email": "info@endroid.nl"
|
||||
}
|
||||
],
|
||||
"description": "Endroid QR Code",
|
||||
"homepage": "https://github.com/endroid/qr-code",
|
||||
"keywords": [
|
||||
"code",
|
||||
"endroid",
|
||||
"php",
|
||||
"qr",
|
||||
"qrcode"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/endroid/qr-code/issues",
|
||||
"source": "https://github.com/endroid/qr-code/tree/6.0.9"
|
||||
},
|
||||
"time": "2025-07-13T19:59:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "firebase/php-jwt",
|
||||
"version": "v6.11.1",
|
||||
|
||||
+14
-3
@@ -526,11 +526,21 @@ Route::group('/admin', function () {
|
||||
Route::any('/item/update', [app\admin\controller\InvItemController::class, 'update']);
|
||||
Route::any('/item/show', [app\admin\controller\InvItemController::class, 'show']);
|
||||
Route::any('/item/destroy', [app\admin\controller\InvItemController::class, 'destroy']);
|
||||
Route::any('/item/qr-code', [app\admin\controller\InvItemController::class, 'generateQrCode']);
|
||||
Route::any('/item/qr-code/delete', [app\admin\controller\InvItemController::class, 'deleteQrCode']);
|
||||
});
|
||||
|
||||
// 库存变化记录
|
||||
Route::group('/stock-log', function () {
|
||||
Route::any('/list', [app\admin\controller\InvStockLogController::class, 'list']);
|
||||
});
|
||||
|
||||
Route::group('/requisition', function () {
|
||||
Route::any('/list', [app\admin\controller\InvRequisitionController::class, 'index']);
|
||||
Route::any('/show', [app\admin\controller\InvRequisitionController::class, 'show']);
|
||||
Route::any('/approve', [app\admin\controller\InvRequisitionController::class, 'approve']);
|
||||
Route::any('/proxy-claim', [app\admin\controller\InvRequisitionController::class, 'proxyClaim']);
|
||||
Route::any('/qr-claims', [app\admin\controller\InvRequisitionController::class, 'qrClaims']);
|
||||
});
|
||||
|
||||
// 物品归还
|
||||
@@ -540,8 +550,7 @@ Route::group('/admin', function () {
|
||||
Route::any('/list', [app\common\controller\InvRequisitionItemReturnController::class, 'index']);
|
||||
Route::any('/show', [app\common\controller\InvRequisitionItemReturnController::class, 'show']);
|
||||
Route::any('/confirm', [app\common\controller\InvRequisitionItemReturnController::class, 'returnConfirm']);
|
||||
Route::any('/confirmItem', [app\common\controller\InvRequisitionItemReturnController::class, 'returnItemConfirm']);
|
||||
});
|
||||
// Route::any('/confirmItem', [app\common\controller\InvRequisitionItemReturnController::class, 'returnItemConfirm']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -684,6 +693,8 @@ Route::group('/mobile', function () {
|
||||
Route::any('/list', [app\mobile\controller\InvRequisitionController::class, 'index']);
|
||||
Route::any('/show', [app\mobile\controller\InvRequisitionController::class, 'show']);
|
||||
// Route::any('/approve', [app\mobile\controller\InvRequisitionController::class, 'approve']);
|
||||
Route::any('/qr-claim', [app\mobile\controller\InvRequisitionController::class, 'qrClaim']);
|
||||
Route::any('/my-qr-claims', [app\mobile\controller\InvRequisitionController::class, 'myQrClaims']);
|
||||
});
|
||||
|
||||
// 物品归还
|
||||
@@ -693,7 +704,7 @@ Route::group('/mobile', function () {
|
||||
Route::any('/list', [app\mobile\controller\InvRequisitionItemReturnController::class, 'index']);
|
||||
Route::any('/show', [app\mobile\controller\InvRequisitionItemReturnController::class, 'show']);
|
||||
Route::any('/confirm', [app\mobile\controller\InvRequisitionItemReturnController::class, 'returnConfirm']);
|
||||
Route::any('/confirmItem', [app\mobile\controller\InvRequisitionItemReturnController::class, 'returnItemConfirm']);
|
||||
// Route::any('/confirmItem', [app\mobile\controller\InvRequisitionItemReturnController::class, 'returnItemConfirm']);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user