CompanyPackageService.php 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414
  1. <?php
  2. namespace app\common\service;
  3. use think\facade\Db;
  4. use think\facade\Log;
  5. class CompanyPackageService
  6. {
  7. /**
  8. * 验证企业套餐购买资格
  9. */
  10. public function validateCompanyPurchase($userId, $packagePriceId, $quantity)
  11. {
  12. try {
  13. // 1. 获取套餐信息
  14. $package = Db::table('ba_package_price')
  15. ->alias('pp')
  16. ->leftJoin('ba_package p', 'p.id = pp.package_id')
  17. ->where('pp.id', $packagePriceId)
  18. ->field('pp.*, p.name as package_name')
  19. ->find();
  20. if (!$package) {
  21. throw new \Exception('套餐不存在');
  22. }
  23. // 2. 如果是企业套餐,检查用户是否属于企业
  24. if ($package['type'] == 2) { // 企业套餐
  25. $companyInfo = $this->getUserCompany($userId);
  26. if (!$companyInfo) {
  27. throw new \Exception('只有企业成员才能购买企业套餐');
  28. }
  29. // 3. 检查日期卡的最低购买数量
  30. if ($package['is_date_card'] == 1 && $quantity < $package['min_quantity']) {
  31. throw new \Exception('企业日期卡至少需要购买' . $package['min_quantity'] . '份');
  32. }
  33. return [
  34. 'valid' => true,
  35. 'company_id' => $companyInfo['id'],
  36. 'company_name' => $companyInfo['name'],
  37. 'min_quantity' => $package['min_quantity']
  38. ];
  39. }
  40. return ['valid' => true, 'company_id' => null];
  41. } catch (\Throwable $e) {
  42. Log::error('验证企业套餐购买资格失败: ' . $e->getMessage());
  43. throw $e;
  44. }
  45. }
  46. /**
  47. * 获取用户所在企业
  48. */
  49. public function getUserCompany($userId)
  50. {
  51. // 检查用户是否是企业创建人或成员
  52. $company = Db::table('ba_company')
  53. ->where('creator_id', $userId)
  54. ->where('status', 1)
  55. ->field('id, name,creator_id')
  56. ->find();
  57. if ($company) {
  58. return $company;
  59. }
  60. // 检查是否是成员
  61. $member = Db::table('ba_company_member')
  62. ->alias('cm')
  63. ->leftJoin('ba_company c', 'c.id = cm.company_id')
  64. ->where('cm.user_id', $userId)
  65. ->where('cm.status', 1)
  66. ->where('c.status', 1)
  67. ->field('c.id, c.name')
  68. ->find();
  69. return $member;
  70. }
  71. /**
  72. * 处理企业套餐购买(创建席位记录)
  73. */
  74. public function processCompanyPackagePurchase($orderId, $companyId, $packagePriceId, $quantity, $cardType, $unitValue, $pointsValue)
  75. {
  76. Db::startTrans();
  77. try {
  78. // 1. 获取或创建企业套餐席位记录
  79. $seatsRecord = Db::table('ba_company_package_seats')
  80. ->where('company_id', $companyId)
  81. ->where('package_price_id', $packagePriceId)
  82. ->find();
  83. if ($seatsRecord) {
  84. // 更新现有记录
  85. $updateData = [
  86. 'total_seats' => Db::raw('total_seats + ' . $quantity),
  87. 'available_seats' => Db::raw('available_seats + ' . $quantity),
  88. 'is_shared' => $cardType < 3 ? 1 : 0, // 次卡/积分卡为共享资源
  89. 'update_time' => date('Y-m-d H:i:s')
  90. ];
  91. // 如果是次卡,累加次数
  92. if ($cardType == 1) {
  93. $updateData['total_times'] = Db::raw('total_times + ' . ($unitValue * $quantity));
  94. $updateData['remaining_times'] = Db::raw('remaining_times + ' . ($unitValue * $quantity));
  95. }
  96. // 如果是积分卡,累加积分
  97. if ($cardType == 2) {
  98. $updateData['total_points'] = Db::raw('total_points + ' . ($pointsValue * $quantity));
  99. $updateData['remaining_points'] = Db::raw('remaining_points + ' . ($pointsValue * $quantity));
  100. }
  101. Db::table('ba_company_package_seats')
  102. ->where('id', $seatsRecord['id'])
  103. ->update($updateData);
  104. $seatsId = $seatsRecord['id'];
  105. } else {
  106. // 创建新记录
  107. $seatsData = [
  108. 'company_id' => $companyId,
  109. 'package_price_id' => $packagePriceId,
  110. 'product_id' => $this->getProductIdByPackage($packagePriceId),
  111. 'total_seats' => $quantity,
  112. 'assigned_seats' => 0,
  113. 'available_seats' => $quantity,
  114. 'is_shared' => $cardType < 3 ? 1 : 0, // 次卡/积分卡为共享资源
  115. 'card_type' => $cardType,
  116. 'total_times' => $cardType == 1 ? $unitValue * $quantity : 0,
  117. 'remaining_times' => $cardType == 1 ? $unitValue * $quantity : 0,
  118. 'total_points' => $cardType == 2 ? $pointsValue * $quantity : 0,
  119. 'remaining_points' => $cardType == 2 ? $pointsValue * $quantity : 0,
  120. 'status' => 1,
  121. 'create_time' => date('Y-m-d H:i:s'),
  122. 'update_time' => date('Y-m-d H:i:s')
  123. ];
  124. $seatsId = Db::table('ba_company_package_seats')->insertGetId($seatsData);
  125. }
  126. // 2. 获取购买者ID
  127. $buyerId = $this->getOrderBuyerId($orderId);
  128. // 3. 查询订单商品
  129. $orderItems = Db::table('ba_order_item')
  130. ->where('order_id', $orderId)
  131. ->where('package_price_id', $packagePriceId)
  132. ->select();
  133. foreach ($orderItems as $item) {
  134. if ($cardType >= 3) {
  135. // 日期卡:为每个购买的数量创建独立的分配记录
  136. for ($i = 0; $i < $quantity; $i++) {
  137. $allocationData = [
  138. 'order_item_id' => $item['id'],
  139. 'order_id' => $orderId,
  140. 'package_price_id' => $packagePriceId,
  141. 'product_id' => $item['product_id'],
  142. 'buyer_id' => $buyerId,
  143. 'company_id' => $companyId,
  144. 'assigned_to_user_id' => null, // 初始不分配
  145. 'assigned_by_user_id' => $buyerId,
  146. 'record_type' => 2, // 购买记录
  147. 'is_general' => 0,
  148. 'seat_index' => $i + 1, // 席位索引
  149. 'allocation_status' => 0, // 未分配
  150. 'allocation_time' => null,
  151. 'start_time' => null,
  152. 'end_time' => null,
  153. 'expire_time' => $this->calculateEndTime($cardType, date('Y-m-d H:i:s')), // 冗余字段
  154. 'remaining_times' => 0, // 日期卡没有次数
  155. 'remaining_points' => 0, // 日期卡没有积分
  156. 'is_used' => 0,
  157. 'create_time' => date('Y-m-d H:i:s'),
  158. 'update_time' => date('Y-m-d H:i:s')
  159. ];
  160. $allocationId = Db::table('ba_company_package_allocation')->insertGetId($allocationData);
  161. }
  162. } else {
  163. // 次卡和积分卡:
  164. // 1. 创建通用记录(用于管理总资源)
  165. $generalAllocationData = [
  166. 'order_item_id' => $item['id'],
  167. 'order_id' => $orderId,
  168. 'package_price_id' => $packagePriceId,
  169. 'product_id' => $item['product_id'],
  170. 'buyer_id' => $buyerId,
  171. 'company_id' => $companyId,
  172. 'assigned_to_user_id' => null, // 通用记录不分配
  173. 'assigned_by_user_id' => $buyerId,
  174. 'record_type' => 0, // 通用记录
  175. 'is_general' => 1, // 是通用记录
  176. 'seat_index' => 0,
  177. 'allocation_status' => 0, // 状态:通用记录
  178. 'allocation_time' => null,
  179. 'start_time' => null,
  180. 'end_time' => null,
  181. 'expire_time' => null,
  182. 'remaining_times' => $cardType == 1 ? $unitValue * $quantity : 0,
  183. 'remaining_points' => $cardType == 2 ? $pointsValue * $quantity : 0,
  184. 'is_used' => 0,
  185. 'create_time' => date('Y-m-d H:i:s'),
  186. 'update_time' => date('Y-m-d H:i:s')
  187. ];
  188. $generalAllocationId = Db::table('ba_company_package_allocation')->insertGetId($generalAllocationData);
  189. // 2. 为购买者创建独立的分配记录
  190. $buyerAllocationData = [
  191. 'order_item_id' => $item['id'],
  192. 'order_id' => $orderId,
  193. 'package_price_id' => $packagePriceId,
  194. 'product_id' => $item['product_id'],
  195. 'buyer_id' => $buyerId,
  196. 'company_id' => $companyId,
  197. 'assigned_to_user_id' => $buyerId,
  198. 'assigned_by_user_id' => $buyerId,
  199. 'record_type' => 1, // 用户分配记录
  200. 'is_general' => 0,
  201. 'seat_index' => 0,
  202. 'parent_id' => $generalAllocationId, // 关联通用记录
  203. 'allocation_status' => 1, // 已分配
  204. 'allocation_time' => date('Y-m-d H:i:s'),
  205. 'start_time' => date('Y-m-d H:i:s'),
  206. 'end_time' => null,
  207. 'expire_time' => null,
  208. 'remaining_times' => $cardType == 1 ? $unitValue : 0,
  209. 'remaining_points' => $cardType == 2 ? $pointsValue : 0,
  210. 'is_used' => 0,
  211. 'create_time' => date('Y-m-d H:i:s'),
  212. 'update_time' => date('Y-m-d H:i:s')
  213. ];
  214. $buyerAllocationId = Db::table('ba_company_package_allocation')->insertGetId($buyerAllocationData);
  215. // 3. 将购买者添加到授权列表
  216. $seats = Db::table('ba_company_package_seats')
  217. ->where('id', $seatsId)
  218. ->find();
  219. $authorizedUsers = json_decode($seats['authorized_users'] ?? '[]', true);
  220. if (!in_array($buyerId, $authorizedUsers)) {
  221. $authorizedUsers[] = $buyerId;
  222. Db::table('ba_company_package_seats')
  223. ->where('id', $seatsId)
  224. ->update([
  225. 'authorized_users' => json_encode($authorizedUsers),
  226. 'assigned_member_count' => Db::raw('assigned_member_count + 1')
  227. ]);
  228. }
  229. }
  230. // 更新订单商品的分配记录ID(对于日期卡,记录第一个分配记录的ID)
  231. if ($cardType >= 3) {
  232. // 获取第一个分配记录ID
  233. $firstAllocation = Db::table('ba_company_package_allocation')
  234. ->where('order_item_id', $item['id'])
  235. ->where('record_type', 2)
  236. ->order('seat_index', 'asc')
  237. ->find();
  238. if ($firstAllocation) {
  239. Db::table('ba_order_item')
  240. ->where('id', $item['id'])
  241. ->update(['allocation_id' => $firstAllocation['id']]);
  242. }
  243. } else {
  244. // 次卡/积分卡使用购买者分配记录ID
  245. Db::table('ba_order_item')
  246. ->where('id', $item['id'])
  247. ->update(['allocation_id' => $buyerAllocationId]);
  248. }
  249. }
  250. Db::commit();
  251. return true;
  252. } catch (\Throwable $e) {
  253. Db::rollback();
  254. Log::error('处理企业套餐购买失败: ' . $e->getMessage());
  255. throw $e;
  256. }
  257. }
  258. /**
  259. * 获取可分配的企业套餐列表(区分类型)
  260. */
  261. public function getAvailableCompanyPackages($companyId, $operatorId)
  262. {
  263. try {
  264. // 获取用户角色(创建者或成员)
  265. $isCreator = Db::table('ba_company')
  266. ->where('id', $companyId)
  267. ->where('creator_id', $operatorId)
  268. ->count() > 0;
  269. // 获取企业所有套餐席位
  270. $seats = Db::table('ba_company_package_seats')
  271. ->alias('cs')
  272. ->leftJoin('ba_package_price pp', 'pp.id = cs.package_price_id')
  273. ->leftJoin('ba_product p', 'p.id = cs.product_id')
  274. ->where('cs.company_id', $companyId)
  275. ->where('cs.available_seats', '>', 0)
  276. ->where('cs.status', 1)
  277. ->field('cs.*, pp.spec_name, pp.card_type, pp.type as package_type, p.name as product_name, p.type as product_type, p.link,pp.image')
  278. ->select();
  279. // 获取企业所有成员(包括创建者)
  280. $allMembers = $this->getAllCompanyMembers($companyId);
  281. // 处理数据
  282. $result = [];
  283. foreach ($seats as $seat) {
  284. // 获取该套餐已分配的成员
  285. $allocatedMembers = $this->getAllocatedMembersForPackage($companyId, $seat['package_price_id']);
  286. // 获取该套餐已分配的成员ID列表
  287. $allocatedMemberIds = array_column($allocatedMembers, 'user_id');
  288. // 筛选未分配的成员
  289. $unallocatedMembers = [];
  290. foreach ($allMembers as $member) {
  291. if (!in_array($member['user_id'], $allocatedMemberIds)) {
  292. $unallocatedMembers[] = $member;
  293. }
  294. }
  295. // 组织返回数据
  296. $seatItem = [
  297. 'seats_id' => $seat['id'],
  298. 'product_id' => $seat['product_id'],
  299. 'product_name' => $seat['product_name'],
  300. 'product_type' => $seat['product_type'], // 1:网页,2:软件
  301. 'product_link' => $seat['link'],
  302. 'product_image' => $seat['image'],
  303. 'package_price_id' => $seat['package_price_id'],
  304. 'spec_name' => $seat['spec_name'],
  305. 'card_type' => $seat['card_type'],
  306. 'card_type_text' => $this->getCardTypeText($seat['card_type']),
  307. 'package_type' => $seat['package_type'], // 1个人,2企业
  308. 'total_seats' => $seat['total_seats'],
  309. 'assigned_seats' => $seat['assigned_seats'],
  310. 'available_seats' => $seat['available_seats'],
  311. // 次卡和积分卡相关
  312. 'total_times' => $seat['total_times'],
  313. 'remaining_times' => $seat['remaining_times'],
  314. 'total_points' => $seat['total_points'],
  315. 'remaining_points' => $seat['remaining_points'],
  316. 'end_time' => $seat['end_time'],
  317. // 已分配成员信息
  318. 'allocated_members' => $allocatedMembers,
  319. // 未分配成员信息
  320. 'unallocated_members' => $unallocatedMembers,
  321. // 统计信息
  322. 'allocated_count' => count($allocatedMembers),
  323. 'unallocated_count' => count($unallocatedMembers),
  324. 'total_members' => count($allMembers)
  325. ];
  326. $result[] = $seatItem;
  327. }
  328. return [
  329. 'packages' => $result,
  330. 'is_creator' => $isCreator,
  331. 'total_members' => count($allMembers)
  332. ];
  333. } catch (\Throwable $e) {
  334. Log::error('获取可分配企业套餐失败: ' . $e->getMessage());
  335. return [
  336. 'packages' => [],
  337. 'is_creator' => false,
  338. 'total_members' => 0
  339. ];
  340. }
  341. }
  342. /**
  343. * 获取企业所有成员(包括创建者)
  344. */
  345. private function getAllCompanyMembers($companyId)
  346. {
  347. $members = [];
  348. // 1. 获取企业创建者
  349. $company = Db::table('ba_company')
  350. ->where('id', $companyId)
  351. ->field('id, name as company_name, creator_id')
  352. ->find();
  353. if ($company && $company['creator_id']) {
  354. // 获取创建者用户信息
  355. $creator = Db::table('ba_user')
  356. ->where('id', $company['creator_id'])
  357. ->field('id as user_id, nickname as member_name, avatar, email, mobile')
  358. ->find();
  359. if ($creator) {
  360. $creator['is_creator'] = 1;
  361. $creator['role'] = 1; // 创建者/管理员
  362. $members[] = $creator;
  363. }
  364. }
  365. // 2. 获取企业成员表中的成员
  366. $companyMembers = Db::table('ba_company_member')
  367. ->alias('cm')
  368. ->leftJoin('ba_user u', 'u.id = cm.user_id')
  369. ->where('cm.company_id', $companyId)
  370. ->where('cm.status', 1)
  371. ->field('
  372. cm.id as member_id,
  373. cm.user_id,
  374. COALESCE(cm.name, u.nickname) as member_name,
  375. COALESCE(cm.email, u.email) as email,
  376. u.avatar,
  377. u.mobile,
  378. cm.role
  379. ')
  380. ->select()
  381. ->toArray();
  382. // 过滤掉重复的成员(创建者可能也在成员表中)
  383. foreach ($companyMembers as $member) {
  384. // 检查是否已经是创建者
  385. $isCreator = false;
  386. foreach ($members as $existingMember) {
  387. if ($existingMember['user_id'] == $member['user_id']) {
  388. $isCreator = true;
  389. break;
  390. }
  391. }
  392. if (!$isCreator) {
  393. $member['is_creator'] = 0;
  394. $members[] = $member;
  395. }
  396. }
  397. return $members;
  398. }
  399. /**
  400. * 获取指定套餐已分配的成员
  401. */
  402. private function getAllocatedMembersForPackage($companyId, $packagePriceId)
  403. {
  404. // 获取套餐信息,判断卡类型
  405. $package = Db::table('ba_package_price')
  406. ->where('id', $packagePriceId)
  407. ->field('card_type')
  408. ->find();
  409. if (!$package) {
  410. return [];
  411. }
  412. $cardType = $package['card_type'];
  413. if ($cardType >= 3) {
  414. // 日期卡:从分配记录中获取已分配成员
  415. return Db::table('ba_company_package_allocation')
  416. ->alias('cpa')
  417. ->leftJoin('ba_user u', 'u.id = cpa.assigned_to_user_id')
  418. ->leftJoin('ba_company_member cm', 'cm.user_id = cpa.assigned_to_user_id AND cm.company_id = cpa.company_id')
  419. ->where('cpa.company_id', $companyId)
  420. ->where('cpa.package_price_id', $packagePriceId)
  421. ->where('cpa.allocation_status', 1) // 已分配
  422. ->whereNotNull('cpa.assigned_to_user_id')
  423. ->field('
  424. cpa.assigned_to_user_id as user_id,
  425. COALESCE(cm.name, u.nickname) as member_name,
  426. COALESCE(cm.email, u.email) as email,
  427. u.avatar,
  428. u.mobile,
  429. cpa.allocation_time,
  430. cpa.start_time,
  431. cpa.end_time,
  432. cpa.remaining_times,
  433. cpa.remaining_points
  434. ')
  435. ->select()
  436. ->toArray();
  437. } else {
  438. // 次卡/积分卡:从席位表的授权用户列表中获取
  439. $seats = Db::table('ba_company_package_seats')
  440. ->where('company_id', $companyId)
  441. ->where('package_price_id', $packagePriceId)
  442. ->field('authorized_users')
  443. ->find();
  444. if (!$seats || empty($seats['authorized_users'])) {
  445. return [];
  446. }
  447. $authorizedUserIds = json_decode($seats['authorized_users'], true);
  448. if (empty($authorizedUserIds)) {
  449. return [];
  450. }
  451. // 获取授权用户的详细信息
  452. $user=Db::table('ba_user')
  453. ->alias('u')
  454. // 使用字符串条件替代闭包
  455. ->leftJoin('ba_company_member cm', 'cm.user_id = u.id AND cm.company_id = ' . $companyId)
  456. ->whereIn('u.id', $authorizedUserIds)
  457. ->field('
  458. u.id as user_id,
  459. COALESCE(cm.name, u.nickname) as member_name,
  460. COALESCE(cm.email, u.email) as email,
  461. u.avatar,
  462. u.mobile,
  463. cm.role
  464. ')
  465. ->select()
  466. ->toArray();
  467. return $user;
  468. }
  469. }
  470. /**
  471. * 分配套餐给企业成员
  472. */
  473. public function allocatePackage($seatsId, $assignedToUserId, $operatorId, $remark = '')
  474. {
  475. Db::startTrans();
  476. try {
  477. // 1. 获取席位信息
  478. $seats = Db::table('ba_company_package_seats')
  479. ->where('id', $seatsId)
  480. ->lock(true)
  481. ->find();
  482. if (!$seats || $seats['available_seats'] <= 0) {
  483. throw new \Exception('没有可用席位');
  484. }
  485. // 2. 检查是否是日期卡
  486. if ($seats['card_type'] < 3) {
  487. throw new \Exception('非日期卡类型,请使用assignPackagePermission方法');
  488. }
  489. // 3. 检查权限(只有创建者可以分配其他人的,成员只能分配自己的)
  490. $isCreator = Db::table('ba_company')
  491. ->where('id', $seats['company_id'])
  492. ->where('creator_id', $operatorId)
  493. ->count() > 0;
  494. // 4. 获取一个未分配的购买记录
  495. $availableAllocation = Db::table('ba_company_package_allocation')
  496. ->where('company_id', $seats['company_id'])
  497. ->where('package_price_id', $seats['package_price_id'])
  498. ->where('record_type', 2) // 购买记录
  499. ->where('allocation_status', 0) // 未分配
  500. ->where('assigned_to_user_id', null) // 未指定用户
  501. ->lock(true)
  502. ->order('seat_index', 'asc') // 按席位索引排序
  503. ->find();
  504. if (!$availableAllocation) {
  505. throw new \Exception('没有可分配的套餐记录');
  506. }
  507. // 如果不是创建者,检查是否是购买者
  508. if (!$isCreator && $availableAllocation['buyer_id'] != $operatorId) {
  509. throw new \Exception('您只能分配自己购买的套餐');
  510. }
  511. // 5. 检查成员是否属于该企业
  512. $isMember = Db::table('ba_company_member')
  513. ->where('company_id', $seats['company_id'])
  514. ->where('user_id', $assignedToUserId)
  515. ->where('status', 1)
  516. ->count() > 0;
  517. if (!$isMember) {
  518. throw new \Exception('该用户不是企业成员');
  519. }
  520. // 6. 检查该成员是否已分配此套餐(日期卡只能分配给一个成员)
  521. if ($seats['card_type'] >= 3) { // 日期卡
  522. $alreadyAllocated = Db::table('ba_company_package_allocation')
  523. ->where('company_id', $seats['company_id'])
  524. ->where('package_price_id', $seats['package_price_id'])
  525. ->where('assigned_to_user_id', $assignedToUserId)
  526. ->where('allocation_status', 1)
  527. ->count();
  528. if ($alreadyAllocated > 0) {
  529. throw new \Exception('该成员已分配此套餐,如需再次分配请重新购买');
  530. }
  531. }
  532. // 7. 执行分配
  533. $now = date('Y-m-d H:i:s');
  534. // 计算到期时间
  535. $endTime = $this->calculateEndTime($seats['card_type'], $now);
  536. // 更新分配记录
  537. Db::table('ba_company_package_allocation')
  538. ->where('id', $availableAllocation['id'])
  539. ->update([
  540. 'assigned_to_user_id' => $assignedToUserId,
  541. 'assigned_by_user_id' => $operatorId,
  542. 'allocation_status' => 1,
  543. 'allocation_time' => $now,
  544. 'start_time' => $now,
  545. 'end_time' => $endTime,
  546. 'expire_time' => $endTime, // 更新冗余字段
  547. 'update_time' => $now
  548. ]);
  549. // 8.更新席位表
  550. Db::table('ba_company_package_seats')
  551. ->where('id', $seatsId)
  552. ->update([
  553. 'assigned_seats' => Db::raw('assigned_seats + 1'),
  554. 'available_seats' => Db::raw('available_seats - 1'),
  555. 'update_time' => $now
  556. ]);
  557. // 9. 检查是否所有席位都已分配
  558. $unallocatedCount = Db::table('ba_company_package_allocation')
  559. ->where('order_item_id', $availableAllocation['order_item_id'])
  560. ->where('allocation_status', 0)
  561. ->where('record_type', 2)
  562. ->count();
  563. if ($unallocatedCount == 0) {
  564. // 所有席位都已分配,更新订单商品
  565. Db::table('ba_order_item')
  566. ->where('id', $availableAllocation['order_item_id'])
  567. ->update([
  568. 'is_allocated' => 1,
  569. 'company_id' => $seats['company_id']
  570. ]);
  571. }
  572. // 10. 为用户创建产品使用记录
  573. $userProductService = new UserProductService();
  574. $userProductService->createCompanyProductUsage($availableAllocation, $assignedToUserId, $seats['card_type']);
  575. // 11. 记录分配日志
  576. Db::table('ba_allocation_log')->insert([
  577. 'allocation_id' => $availableAllocation['id'],
  578. 'action' => 1, // 分配
  579. 'operator_id' => $operatorId,
  580. 'to_user_id' => $assignedToUserId,
  581. 'remark' => $remark,
  582. 'create_time' => $now
  583. ]);
  584. Db::commit();
  585. return [
  586. 'success' => true,
  587. 'allocation_id' => $availableAllocation['id'],
  588. 'message' => '分配成功'
  589. ];
  590. } catch (\Throwable $e) {
  591. Db::rollback();
  592. Log::error('分配套餐失败: ' . $e->getMessage());
  593. throw $e;
  594. }
  595. }
  596. /**
  597. * 取消分配(日期卡独占席位模式)
  598. */
  599. public function cancelAllocation($allocationId, $operatorId, $remark = '')
  600. {
  601. Db::startTrans();
  602. try {
  603. // 1. 获取分配记录
  604. $allocation = Db::table('ba_company_package_allocation')
  605. ->where('id', $allocationId)
  606. ->where('record_type', 2) // 购买记录
  607. ->where('allocation_status', 1) // 已分配
  608. ->lock(true)
  609. ->find();
  610. if (!$allocation) {
  611. throw new \Exception('分配记录不存在或未分配');
  612. }
  613. // 2. 检查权限
  614. $isCreator = Db::table('ba_company')
  615. ->where('id', $allocation['company_id'])
  616. ->where('creator_id', $operatorId)
  617. ->count() > 0;
  618. if (!$isCreator && $allocation['assigned_by_user_id'] != $operatorId) {
  619. throw new \Exception('您没有取消分配的权限');
  620. }
  621. // 3. 获取席位信息
  622. $seats = Db::table('ba_company_package_seats')
  623. ->where('company_id', $allocation['company_id'])
  624. ->where('package_price_id', $allocation['package_price_id'])
  625. ->find();
  626. if (!$seats) {
  627. throw new \Exception('企业套餐席位不存在');
  628. }
  629. // 4. 执行取消分配
  630. $now = date('Y-m-d H:i:s');
  631. // 更新分配记录状态
  632. Db::table('ba_company_package_allocation')
  633. ->where('id', $allocationId)
  634. ->update([
  635. 'assigned_to_user_id' => null,
  636. 'allocation_status' => 2,
  637. 'cancel_allocation_time' => $now,
  638. 'update_time' => $now
  639. ]);
  640. // 5. 更新席位表
  641. Db::table('ba_company_package_seats')
  642. ->where('id', $seats['id'])
  643. ->update([
  644. 'assigned_seats' => Db::raw('assigned_seats - 1'),
  645. 'available_seats' => Db::raw('available_seats + 1'),
  646. 'update_time' => $now
  647. ]);
  648. // 6. 检查是否还有已分配的记录
  649. $allocatedCount = Db::table('ba_company_package_allocation')
  650. ->where('order_item_id', $allocation['order_item_id'])
  651. ->where('allocation_status', 1)
  652. ->count();
  653. if ($allocatedCount == 0) {
  654. // 没有已分配的记录,更新订单商品
  655. Db::table('ba_order_item')
  656. ->where('id', $allocation['order_item_id'])
  657. ->update([
  658. 'is_allocated' => 0,
  659. 'allocation_id' => null
  660. ]);
  661. }
  662. // 7. 删除对应的用户产品使用记录
  663. Db::table('ba_user_product_usage')
  664. ->where('user_id', $allocation['assigned_to_user_id'])
  665. ->where('package_price_id', $allocation['package_price_id'])
  666. ->where('company_id', $allocation['company_id'])
  667. ->where('package_type', 2) // 企业套餐
  668. ->delete();
  669. // 8. 记录取消分配日志
  670. Db::table('ba_allocation_log')->insert([
  671. 'allocation_id' => $allocationId,
  672. 'action' => 2, // 取消分配
  673. 'operator_id' => $operatorId,
  674. 'from_user_id' => $allocation['assigned_to_user_id'],
  675. 'remark' => $remark,
  676. 'create_time' => $now
  677. ]);
  678. Db::commit();
  679. return true;
  680. } catch (\Throwable $e) {
  681. Db::rollback();
  682. Log::error('取消分配失败: ' . $e->getMessage());
  683. throw $e;
  684. }
  685. }
  686. /**
  687. * 分配次卡/积分卡使用权限给成员(授权访问共享资源池)
  688. */
  689. public function assignPackagePermission($seatsId, $memberUserId, $allocatorId, $remark = '')
  690. {
  691. Db::startTrans();
  692. try {
  693. // 1. 获取席位信息
  694. $seats = Db::table('ba_company_package_seats')
  695. ->where('id', $seatsId)
  696. ->lock(true)
  697. ->find();
  698. if (!$seats) {
  699. throw new \Exception('企业套餐不存在');
  700. }
  701. // 2. 检查权限(只有企业创建者或购买者可以分配)
  702. $canAssign = $this->checkAssignPermission($allocatorId, $seats['company_id'], $seatsId);
  703. if (!$canAssign) {
  704. throw new \Exception('没有分配权限');
  705. }
  706. // 3. 检查成员是否属于企业
  707. if (!$this->isCompanyMember($memberUserId, $seats['company_id'])) {
  708. throw new \Exception('该用户不是企业成员');
  709. }
  710. // 4. 检查是否已经是次卡或积分卡
  711. if (!in_array($seats['card_type'], [1, 2])) {
  712. throw new \Exception('只有次卡和积分卡支持权限分配');
  713. }
  714. // 5. 检查该成员是否已被授权
  715. $authorizedUsers = json_decode($seats['authorized_users'] ?? '[]', true);
  716. if (in_array($memberUserId, $authorizedUsers)) {
  717. throw new \Exception('该成员已被授权使用此套餐');
  718. }
  719. // 6. 查找通用记录
  720. $generalRecord = Db::table('ba_company_package_allocation')
  721. ->where('company_id', $seats['company_id'])
  722. ->where('package_price_id', $seats['package_price_id'])
  723. ->where('record_type', 0) // 通用记录
  724. ->where('is_general', 1) // 是通用记录
  725. ->order('id', 'asc')
  726. ->find();
  727. if (!$generalRecord) {
  728. throw new \Exception('未找到套餐购买记录');
  729. }
  730. // 7. 将成员添加到授权列表
  731. $authorizedUsers[] = $memberUserId;
  732. Db::table('ba_company_package_seats')
  733. ->where('id', $seatsId)
  734. ->update([
  735. 'authorized_users' => json_encode($authorizedUsers),
  736. 'assigned_member_count' => Db::raw('assigned_member_count + 1'),
  737. 'update_time' => date('Y-m-d H:i:s')
  738. ]);
  739. // 8. 创建新的分配记录
  740. $now = date('Y-m-d H:i:s');
  741. $newAllocationData = [
  742. 'order_item_id' => $generalRecord['order_item_id'],
  743. 'order_id' => $generalRecord['order_id'],
  744. 'package_price_id' => $seats['package_price_id'],
  745. 'product_id' => $seats['product_id'],
  746. 'buyer_id' => $generalRecord['buyer_id'],
  747. 'company_id' => $seats['company_id'],
  748. 'assigned_to_user_id' => $memberUserId,
  749. 'assigned_by_user_id' => $allocatorId,
  750. 'record_type' => 1, // 用户分配记录
  751. 'is_general' => 0,
  752. 'seat_index' => 0,
  753. 'parent_id' => $generalRecord['id'], // 关联通用记录
  754. 'allocation_status' => 1, // 已分配
  755. 'allocation_time' => $now,
  756. 'start_time' => $now,
  757. 'end_time' => null,
  758. 'expire_time' => null,
  759. 'remaining_times' => $seats['remaining_times'],
  760. 'remaining_points' => $seats['remaining_points'],
  761. 'is_used' => 0,
  762. 'create_time' => $now,
  763. 'update_time' => $now
  764. ];
  765. $allocationId = Db::table('ba_company_package_allocation')->insertGetId($newAllocationData);
  766. // 9. 为用户创建产品使用记录
  767. $userProductService = new UserProductService();
  768. $orderItem = Db::table('ba_order_item')->where('id', $generalRecord['order_item_id'])->find();
  769. if ($orderItem) {
  770. $package = Db::table('ba_package_price')->where('id', $seats['package_price_id'])->find();
  771. if ($package) {
  772. $userProductService->createUserProductRecord($memberUserId, $orderItem, $package);
  773. }
  774. }
  775. // 10. 记录分配日志
  776. Db::table('ba_allocation_log')->insert([
  777. 'allocation_id' => $allocationId,
  778. 'action' => 1, // 分配
  779. 'operator_id' => $allocatorId,
  780. 'to_user_id' => $memberUserId,
  781. 'remark' => $remark ?: '分配次卡/积分卡使用权限',
  782. 'create_time' => $now
  783. ]);
  784. Db::commit();
  785. return [
  786. 'success' => true,
  787. 'allocation_id' => $allocationId,
  788. 'message' => '分配成功'
  789. ];
  790. } catch (\Throwable $e) {
  791. Db::rollback();
  792. throw $e;
  793. }
  794. }
  795. /**
  796. * 检查分配权限
  797. */
  798. private function checkAssignPermission($userId, $companyId, $seatsId = null)
  799. {
  800. // 1. 检查是否是公司创建者
  801. $isCreator = Db::table('ba_company')
  802. ->where('id', $companyId)
  803. ->where('creator_id', $userId)
  804. ->count() > 0;
  805. if ($isCreator) {
  806. return true;
  807. }
  808. // 2. 检查是否是购买者(对于该套餐)
  809. if ($seatsId) {
  810. $isBuyer = Db::table('ba_company_package_allocation')
  811. ->where('seats_id', $seatsId)
  812. ->where('buyer_id', $userId)
  813. ->count() > 0;
  814. if ($isBuyer) {
  815. return true;
  816. }
  817. }
  818. // 3. 检查是否是管理员(根据企业成员表)
  819. $isAdmin = Db::table('ba_company_member')
  820. ->where('company_id', $companyId)
  821. ->where('user_id', $userId)
  822. ->where('role', 1) // 1=管理员
  823. ->where('status', 1)
  824. ->count() > 0;
  825. return $isAdmin;
  826. }
  827. /**
  828. * 检查用户是否是企业成员
  829. */
  830. private function isCompanyMember($userId, $companyId)
  831. {
  832. // 检查是否是创建者
  833. $isCreator = Db::table('ba_company')
  834. ->where('id', $companyId)
  835. ->where('creator_id', $userId)
  836. ->count() > 0;
  837. if ($isCreator) {
  838. return true;
  839. }
  840. // 检查是否是成员
  841. $isMember = Db::table('ba_company_member')
  842. ->where('company_id', $companyId)
  843. ->where('user_id', $userId)
  844. ->where('status', 1)
  845. ->count() > 0;
  846. return $isMember;
  847. }
  848. /**
  849. * 自动分配席位给购买者
  850. */
  851. public function autoAllocateToBuyer($buyerId, $companyId, $packagePriceId, $orderId)
  852. {
  853. try {
  854. Log::info('开始自动分配席位给购买者', [
  855. 'buyer_id' => $buyerId,
  856. 'company_id' => $companyId,
  857. 'package_price_id' => $packagePriceId,
  858. 'order_id' => $orderId
  859. ]);
  860. // 1. 检查购买者是否已分配此套餐
  861. $alreadyAllocated = Db::table('ba_company_package_allocation')
  862. ->where('company_id', $companyId)
  863. ->where('package_price_id', $packagePriceId)
  864. ->where('assigned_to_user_id', $buyerId)
  865. ->where('allocation_status', 1) // 已分配
  866. ->count();
  867. Log::info('检查购买者是否已分配此套餐结果', [
  868. 'already_allocated' => $alreadyAllocated
  869. ]);
  870. if ($alreadyAllocated > 0) {
  871. Log::info('购买者已分配此套餐,跳过自动分配', [
  872. 'buyer_id' => $buyerId,
  873. 'company_id' => $companyId,
  874. 'package_price_id' => $packagePriceId
  875. ]);
  876. return false; // 已分配,跳过
  877. }
  878. // 2. 获取可用的席位ID(从席位表中获取)
  879. $seats = Db::table('ba_company_package_seats')
  880. ->where('company_id', $companyId)
  881. ->where('package_price_id', $packagePriceId)
  882. ->where('available_seats', '>', 0)
  883. ->find();
  884. Log::info('查询可用席位结果', [
  885. 'seats' => $seats ? $seats['id'] : null,
  886. 'available_seats' => $seats ? $seats['available_seats'] : 0
  887. ]);
  888. if (!$seats) {
  889. Log::error('没有可用席位进行自动分配', [
  890. 'company_id' => $companyId,
  891. 'package_price_id' => $packagePriceId
  892. ]);
  893. return false;
  894. }
  895. // 3. 获取一个未分配的分配记录
  896. $allocation = Db::table('ba_company_package_allocation')
  897. ->where('order_id', $orderId)
  898. ->where('company_id', $companyId)
  899. ->where('package_price_id', $packagePriceId)
  900. ->where('allocation_status', 0) // 未分配
  901. ->order('id', 'asc')
  902. ->find();
  903. Log::info('查询未分配记录结果', [
  904. 'allocation' => $allocation ? $allocation['id'] : null,
  905. 'order_id' => $orderId
  906. ]);
  907. if (!$allocation) {
  908. // 尝试查找任何属于该订单的未分配记录
  909. $allocation = Db::table('ba_company_package_allocation')
  910. ->where('order_id', $orderId)
  911. ->where('allocation_status', 0)
  912. ->order('id', 'asc')
  913. ->find();
  914. Log::info('二次查询未分配记录结果', [
  915. 'allocation' => $allocation ? $allocation['id'] : null
  916. ]);
  917. if (!$allocation) {
  918. Log::error('没有找到未分配的分配记录', [
  919. 'order_id' => $orderId,
  920. 'company_id' => $companyId,
  921. 'package_price_id' => $packagePriceId
  922. ]);
  923. return false;
  924. }
  925. }
  926. // 4. 获取套餐信息(用于卡类型判断)
  927. $package = Db::table('ba_package_price')
  928. ->where('id', $packagePriceId)
  929. ->find();
  930. if (!$package) {
  931. Log::error('套餐信息不存在', ['package_price_id' => $packagePriceId]);
  932. return false;
  933. }
  934. Db::startTrans();
  935. try {
  936. // 5. 执行分配
  937. $now = date('Y-m-d H:i:s');
  938. // 更新分配记录
  939. Db::table('ba_company_package_allocation')
  940. ->where('id', $allocation['id'])
  941. ->update([
  942. 'assigned_to_user_id' => $buyerId,
  943. 'assigned_by_user_id' => $buyerId,
  944. 'allocation_status' => 1,
  945. 'allocation_time' => $now,
  946. 'start_time' => $now,
  947. 'end_time' => $this->calculateEndTime($package['card_type'], $now),
  948. 'update_time' => $now
  949. ]);
  950. // 更新席位表
  951. Db::table('ba_company_package_seats')
  952. ->where('id', $seats['id'])
  953. ->update([
  954. 'assigned_seats' => Db::raw('assigned_seats + 1'),
  955. 'available_seats' => Db::raw('available_seats - 1'),
  956. 'update_time' => $now
  957. ]);
  958. // 更新订单商品
  959. Db::table('ba_order_item')
  960. ->where('id', $allocation['order_item_id'])
  961. ->update([
  962. 'is_allocated' => 1,
  963. 'company_id' => $companyId
  964. ]);
  965. // 6. 记录分配日志
  966. Db::table('ba_allocation_log')->insert([
  967. 'allocation_id' => $allocation['id'],
  968. 'action' => 1, // 分配
  969. 'operator_id' => $buyerId,
  970. 'to_user_id' => $buyerId,
  971. 'remark' => '购买后自动分配给购买者',
  972. 'create_time' => $now
  973. ]);
  974. // 7. 根据卡类型创建用户产品记录
  975. $userProductService = new UserProductService();
  976. // 所有卡类型都需要创建使用记录
  977. if ($package['card_type'] >= 3) { // 日期卡
  978. // 日期卡分配时创建使用记录
  979. $userProductService->createCompanyProductUsage($allocation, $buyerId, $package['card_type']);
  980. } else { // 次卡(1)和积分卡(2)
  981. // 获取订单商品信息
  982. $orderItem = Db::table('ba_order_item')
  983. ->where('id', $allocation['order_item_id'])
  984. ->find();
  985. if ($orderItem) {
  986. // 创建用户产品记录(次卡/积分卡)
  987. $userProductService->createUserProductRecord($buyerId, $orderItem, $package);
  988. }
  989. }
  990. Db::commit();
  991. Log::info('企业套餐自动分配给购买者成功', [
  992. 'buyer_id' => $buyerId,
  993. 'company_id' => $companyId,
  994. 'package_price_id' => $packagePriceId,
  995. 'allocation_id' => $allocation['id']
  996. ]);
  997. return true;
  998. } catch (\Throwable $e) {
  999. Db::rollback();
  1000. Log::error('企业套餐自动分配给购买者失败: ' . $e->getMessage());
  1001. throw $e;
  1002. }
  1003. } catch (\Throwable $e) {
  1004. Log::error('自动分配企业套餐给购买者失败: ' . $e->getMessage());
  1005. return false;
  1006. }
  1007. }
  1008. /**
  1009. * 计算到期时间
  1010. */
  1011. private function calculateEndTime($cardType, $startTime)
  1012. {
  1013. $start = strtotime($startTime);
  1014. switch ($cardType) {
  1015. case 3: // 日卡
  1016. return date('Y-m-d 23:59:59', strtotime('+1 day', $start));
  1017. case 4: // 周卡
  1018. return date('Y-m-d 23:59:59', strtotime('+7 days', $start));
  1019. case 5: // 季卡
  1020. return date('Y-m-d 23:59:59', strtotime('+90 days', $start));
  1021. case 6: // 月卡
  1022. return date('Y-m-d 23:59:59', strtotime('+30 days', $start));
  1023. case 7: // 年卡
  1024. return date('Y-m-d 23:59:59', strtotime('+365 days', $start));
  1025. default:
  1026. return null; // 次卡和积分卡没有固定到期时间
  1027. }
  1028. }
  1029. /**
  1030. * 获取卡类型文本
  1031. */
  1032. private function getCardTypeText($type)
  1033. {
  1034. $map = [
  1035. 1 => '次卡',
  1036. 2 => '积分卡',
  1037. 3 => '日卡',
  1038. 4 => '周卡',
  1039. 5 => '季卡',
  1040. 6 => '月卡',
  1041. 7 => '年卡'
  1042. ];
  1043. return $map[$type] ?? '其他';
  1044. }
  1045. private function getProductIdByPackage($packagePriceId) {
  1046. // 实现获取产品ID的逻辑
  1047. $package = Db::table('ba_package_price')->where('id', $packagePriceId)->find();
  1048. $product = Db::table('ba_product')->where('package_id', $package['package_id'])->find();
  1049. return $product ? $product['id'] : 0;
  1050. }
  1051. private function getOrderBuyerId($orderId) {
  1052. $order = Db::table('ba_order')->where('id', $orderId)->find();
  1053. return $order ? $order['user_id'] : 0;
  1054. }
  1055. /**
  1056. * 取消次卡/积分卡权限分配
  1057. */
  1058. public function cancelPackagePermission($allocationId, $operatorId, $remark = '')
  1059. {
  1060. Db::startTrans();
  1061. try {
  1062. // 1. 获取分配记录
  1063. $allocation = Db::table('ba_company_package_allocation')
  1064. ->where('id', $allocationId)
  1065. ->where('record_type', 1) // 用户分配记录
  1066. ->where('allocation_status', 1) // 已分配
  1067. ->lock(true)
  1068. ->find();
  1069. if (!$allocation) {
  1070. throw new \Exception('分配记录不存在或未分配');
  1071. }
  1072. // 2. 检查权限
  1073. $canCancel = $this->checkAssignPermission($operatorId, $allocation['company_id']);
  1074. if (!$canCancel) {
  1075. throw new \Exception('您没有取消分配的权限');
  1076. }
  1077. // 3. 获取席位信息
  1078. $seats = Db::table('ba_company_package_seats')
  1079. ->where('company_id', $allocation['company_id'])
  1080. ->where('package_price_id', $allocation['package_price_id'])
  1081. ->find();
  1082. if (!$seats) {
  1083. throw new \Exception('企业套餐席位不存在');
  1084. }
  1085. // 4. 从授权列表中移除用户
  1086. $authorizedUsers = json_decode($seats['authorized_users'] ?? '[]', true);
  1087. $key = array_search($allocation['assigned_to_user_id'], $authorizedUsers);
  1088. if ($key !== false) {
  1089. unset($authorizedUsers[$key]);
  1090. $authorizedUsers = array_values($authorizedUsers); // 重新索引数组
  1091. // 更新席位表
  1092. Db::table('ba_company_package_seats')
  1093. ->where('id', $seats['id'])
  1094. ->update([
  1095. 'authorized_users' => json_encode($authorizedUsers),
  1096. 'assigned_member_count' => Db::raw('assigned_member_count - 1'),
  1097. 'update_time' => date('Y-m-d H:i:s')
  1098. ]);
  1099. }
  1100. // 5. 更新分配记录状态
  1101. $now = date('Y-m-d H:i:s');
  1102. Db::table('ba_company_package_allocation')
  1103. ->where('id', $allocationId)
  1104. ->update([
  1105. 'allocation_status' => 2, // 已取消
  1106. 'cancel_allocation_time' => $now,
  1107. 'update_time' => $now
  1108. ]);
  1109. // 6. 删除对应的用户产品使用记录
  1110. Db::table('ba_user_product_usage')
  1111. ->where('user_id', $allocation['assigned_to_user_id'])
  1112. ->where('package_price_id', $allocation['package_price_id'])
  1113. ->where('company_id', $allocation['company_id'])
  1114. ->where('package_type', 2) // 企业套餐
  1115. ->delete();
  1116. // 7. 记录取消分配日志
  1117. Db::table('ba_allocation_log')->insert([
  1118. 'allocation_id' => $allocationId,
  1119. 'action' => 2, // 取消分配
  1120. 'operator_id' => $operatorId,
  1121. 'from_user_id' => $allocation['assigned_to_user_id'],
  1122. 'remark' => $remark ?: '取消次卡/积分卡使用权限',
  1123. 'create_time' => $now
  1124. ]);
  1125. Db::commit();
  1126. return true;
  1127. } catch (\Throwable $e) {
  1128. Db::rollback();
  1129. Log::error('取消次卡/积分卡权限失败: ' . $e->getMessage());
  1130. throw $e;
  1131. }
  1132. }
  1133. /**
  1134. * 使用企业次卡/积分卡(从共享资源池扣除)
  1135. */
  1136. public function useSharedCompanyPackage($userId, $packagePriceId, $useQuantity, $productId = null, $remark = '')
  1137. {
  1138. Db::startTrans();
  1139. try {
  1140. // 1. 获取用户所在企业
  1141. $companyInfo = $this->getUserCompany($userId);
  1142. if (!$companyInfo) {
  1143. throw new \Exception('您不属于任何企业');
  1144. }
  1145. // 2. 获取套餐信息
  1146. $package = Db::table('ba_package_price')
  1147. ->where('id', $packagePriceId)
  1148. ->find();
  1149. if (!$package) {
  1150. throw new \Exception('套餐不存在');
  1151. }
  1152. // 3. 检查卡类型是否为次卡或积分卡
  1153. if (!in_array($package['card_type'], [1, 2])) {
  1154. throw new \Exception('只有次卡和积分卡支持此使用方式');
  1155. }
  1156. // 4. 获取企业套餐席位信息,检查用户是否被授权
  1157. $seats = Db::table('ba_company_package_seats')
  1158. ->where('company_id', $companyInfo['id'])
  1159. ->where('package_price_id', $packagePriceId)
  1160. ->lock(true)
  1161. ->find();
  1162. if (!$seats) {
  1163. throw new \Exception('企业套餐不存在');
  1164. }
  1165. // 检查用户是否在授权列表中
  1166. $authorizedUsers = json_decode($seats['authorized_users'] ?? '[]', true);
  1167. if (!in_array($userId, $authorizedUsers)) {
  1168. throw new \Exception('您未被授权使用此套餐');
  1169. }
  1170. // 5. 检查共享资源池是否足够
  1171. if ($package['card_type'] == 1) { // 次卡
  1172. if ($seats['remaining_times'] < $useQuantity) {
  1173. throw new \Exception('剩余次数不足,剩余:' . $seats['remaining_times'] . '次');
  1174. }
  1175. } elseif ($package['card_type'] == 2) { // 积分卡
  1176. if ($seats['remaining_points'] < $useQuantity) {
  1177. throw new \Exception('剩余积分不足,剩余:' . $seats['remaining_points'] . '积分');
  1178. }
  1179. }
  1180. // 6. 获取用户的分配记录
  1181. $userAllocation = Db::table('ba_company_package_allocation')
  1182. ->where('company_id', $companyInfo['id'])
  1183. ->where('package_price_id', $packagePriceId)
  1184. ->where('assigned_to_user_id', $userId)
  1185. ->where('allocation_status', 1)
  1186. ->order('id', 'asc')
  1187. ->find();
  1188. if (!$userAllocation) {
  1189. throw new \Exception('未找到您的分配记录');
  1190. }
  1191. // 7. 扣减共享池资源
  1192. if ($package['card_type'] == 1) { // 次卡
  1193. Db::table('ba_company_package_seats')
  1194. ->where('id', $seats['id'])
  1195. ->dec('remaining_times', $useQuantity)
  1196. ->update();
  1197. $remainingTimesAfter = $seats['remaining_times'] - $useQuantity;
  1198. $remainingPointsAfter = $seats['remaining_points'];
  1199. } else { // 积分卡
  1200. Db::table('ba_company_package_seats')
  1201. ->where('id', $seats['id'])
  1202. ->dec('remaining_points', $useQuantity)
  1203. ->update();
  1204. $remainingTimesAfter = $seats['remaining_times'];
  1205. $remainingPointsAfter = $seats['remaining_points'] - $useQuantity;
  1206. }
  1207. // 8. 更新分配记录的使用状态
  1208. Db::table('ba_company_package_allocation')
  1209. ->where('id', $userAllocation['id'])
  1210. ->update([
  1211. 'is_used' => 1,
  1212. 'usage_count' => Db::raw('usage_count + ' . $useQuantity),
  1213. 'update_time' => date('Y-m-d H:i:s')
  1214. ]);
  1215. // 9. 记录使用明细
  1216. $usageData = [
  1217. 'allocation_id' => $userAllocation['id'],
  1218. 'company_id' => $companyInfo['id'],
  1219. 'package_price_id' => $packagePriceId,
  1220. 'user_id' => $userId,
  1221. 'card_type' => $package['card_type'],
  1222. 'used_quantity' => $useQuantity,
  1223. 'remaining_times_after' => $remainingTimesAfter,
  1224. 'remaining_points_after' => $remainingPointsAfter,
  1225. 'product_id' => $productId,
  1226. 'usage_time' => date('Y-m-d H:i:s'),
  1227. 'remark' => $remark,
  1228. 'create_time' => date('Y-m-d H:i:s')
  1229. ];
  1230. $usageId = Db::table('ba_company_package_usage')->insertGetId($usageData);
  1231. Db::commit();
  1232. return [
  1233. 'success' => true,
  1234. 'usage_id' => $usageId,
  1235. 'remaining_times' => $remainingTimesAfter,
  1236. 'remaining_points' => $remainingPointsAfter,
  1237. 'message' => '使用成功'
  1238. ];
  1239. } catch (\Throwable $e) {
  1240. Db::rollback();
  1241. Log::error('使用企业次卡/积分卡失败: ' . $e->getMessage());
  1242. throw $e;
  1243. }
  1244. }
  1245. }