OrderSuperLoss.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <?php
  2. namespace app\api\controller;
  3. use app\common\controller\Api;
  4. use app\service\AIGatewayService;
  5. use app\service\ImageService;
  6. use think\Cache;
  7. use \think\Request;
  8. use \think\Db;
  9. /**
  10. * 工单超节损核算接口
  11. */
  12. class OrderSuperLoss extends Api
  13. {
  14. protected $noNeedLogin = ['*'];
  15. protected $noNeedRight = ['*'];
  16. public function index()
  17. {
  18. $this->success('请求成功');
  19. }
  20. /**
  21. * 获取左侧菜单栏
  22. * @ApiMethod GET
  23. */
  24. public function getTab()
  25. {
  26. // 检查请求类型是否为 GET
  27. if (!Request::instance()->isGet()) {
  28. $this->error('非法请求');
  29. }
  30. // SQL 查询,连接 db3 数据库
  31. $sql = "SELECT
  32. DATE_FORMAT(a.jjcp_sj, '%Y-%m') AS date, -- 格式化为年月
  33. RTRIM(b.`编号`) AS 编号,
  34. RTRIM(b.`名称`) AS 客户名称,
  35. COUNT(DISTINCT a.jjcp_gdbh) AS count
  36. FROM
  37. `成品入仓` a
  38. LEFT JOIN
  39. `物料_存货结构` b
  40. ON (CASE
  41. WHEN LEFT(a.`jjcp_cpdh`, 1) REGEXP '^[A-Za-z]' THEN LEFT(a.`jjcp_cpdh`, 5)
  42. ELSE LEFT(a.`jjcp_cpdh`, 4)
  43. END) = RTRIM(b.`编号`) -- 根据字母/数字判断连接方式
  44. WHERE
  45. a.jjcp_sj >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH) -- 最近12个月
  46. AND a.jjcp_sj >= '2025-01-01 00:00:00' -- 限定从2025年开始
  47. AND a.`成品编码` != '' -- 编码不为空
  48. AND a.jjcp_smb IN ('末 板', '末板') -- 状态筛选
  49. GROUP BY
  50. DATE_FORMAT(a.jjcp_sj, '%Y-%m'),
  51. RTRIM(b.`编号`)
  52. ORDER BY
  53. date DESC,
  54. 编号 ASC";
  55. // 执行查询,确保连接的是 db3 数据库
  56. $db3 = \db()->connect(config('database.db3'));
  57. $data = $db3->query($sql);
  58. // 创建一个新数组存储整理后的数据
  59. $organizedData = [];
  60. // 遍历原始数据
  61. foreach ($data as $entry) {
  62. $date = date('Ym', strtotime($entry['date'])); // 格式化为年月,例如 202506
  63. // 如果当前月份已经有数据,追加数据
  64. if (isset($organizedData[$date])) {
  65. $organizedData[$date][] = [
  66. '客户名称' => $entry['客户名称'],
  67. '客户编号' => $entry['编号'],
  68. 'total' => $entry['count'],
  69. ];
  70. // 累加该月的总数
  71. $organizedData[$date]['total_count'] += $entry['count'];
  72. } else {
  73. // 否则初始化该月数据
  74. $organizedData[$date] = [
  75. [
  76. '客户名称' => $entry['客户名称'],
  77. '客户编号' => $entry['编号'],
  78. 'total' => $entry['count'],
  79. ]
  80. ];
  81. // 初始化总数
  82. $organizedData[$date]['total_count'] = $entry['count'];
  83. }
  84. }
  85. // 整理成最终格式,以 "年月-总数" 为键
  86. $finalData = [];
  87. foreach ($organizedData as $date => $entries) {
  88. $totalCount = $entries['total_count'];
  89. $index = $date . '-' . $totalCount;
  90. unset($entries['total_count']); // 删除临时统计字段
  91. $finalData[$index] = $entries;
  92. }
  93. // 返回成功结果
  94. $this->success('请求成功', $finalData);
  95. }
  96. /**
  97. * 超节损列表
  98. */
  99. public function getList()
  100. {
  101. if (!Request::instance()->isGet()) {
  102. $this->error('非法请求');
  103. }
  104. $params = Request::instance()->param();
  105. $where = [];
  106. // 设置默认分页和限制
  107. $limit = empty($params['limit']) ? 15 : intval($params['limit']);
  108. $pages = empty($params['page']) ? 1 : intval($params['page']);
  109. // 构建查询条件
  110. if (!empty($params['code'])) {
  111. $prefixLength = preg_match('/[A-Za-z]/', $params['code']) ? 5 : 4;
  112. $where['a.jjcp_cpdh'] = ['LIKE', substr($params['code'], 0, $prefixLength) . '%'];
  113. }
  114. if (!empty($params['date'])) {
  115. $year = substr($params['date'], 0, 4);
  116. $month = substr($params['date'], 4, 2);
  117. $formattedDate = $year . '-' . $month;
  118. $where['a.jjcp_sj'] = ['like', $formattedDate . '%'];
  119. }
  120. if (!empty($params['search'])) {
  121. if (!empty($params['date']) || !empty($params['code'])) {
  122. $this->error('参数错误');
  123. }
  124. $where['a.jjcp_gdbh|a.jjcp_cpdh|a.jjcp_cpmc'] = ['like', '%' . $params['search'] . '%'];
  125. }
  126. // 数据库连接
  127. $db3 = \db()->connect(config('database.db3'));
  128. // 定义查询字段
  129. $field = '
  130. b.sys_ny,
  131. a.jjcp_gdbh AS Gd_gdbh,
  132. a.jjcp_yjno AS jjcp_yjno,
  133. a.jjcp_yjno AS yj_Yjno,
  134. c.yj_Yjdh as yjdh,
  135. c.yj_ls AS yj_ls,
  136. c.yj_cpdh AS 成品编码,
  137. c.yj_yjmc AS 成品名称,
  138. b.`实际投料`,
  139. b.`计量单位`,
  140. DATE(a.jjcp_sj) AS warehousing_date,
  141. total_sl.warehousing_num,
  142. SUM(DISTINCT b.`计划损耗`) AS `工单计划损耗`,
  143. b.`废品合计`,
  144. SUM(b.`CjsJe`) AS reward_money,
  145. b.`印件无形损` AS 工单无形损,
  146. b.`材料废`,
  147. b.`零头处理`,
  148. b.`外发废`,
  149. SUM(b.`班组制程废品`) AS zcfp,
  150. `工单质检废` AS 工单质检废,
  151. c.质量考核
  152. ';
  153. // 子查询:计算入仓数量
  154. $subQuery = $db3->name('成品入仓')
  155. ->alias('a_sub')
  156. ->field('
  157. a_sub.jjcp_gdbh AS Gd_gdbh_sub,
  158. a_sub.jjcp_yjno AS jjcp_yjno_sub,
  159. SUM(a_sub.jjcp_sl) AS warehousing_num
  160. ')
  161. ->group('a_sub.jjcp_gdbh, a_sub.jjcp_yjno')
  162. ->buildSql();
  163. // 主查询
  164. $list = $db3->name('成品入仓')
  165. ->alias('a')
  166. ->join('工单_质量考核汇总 b', 'a.jjcp_gdbh = b.Gy0_gdbh AND a.jjcp_yjno = b.印件及工序', 'left')
  167. ->join('工单_印件资料 c', 'a.jjcp_gdbh = c.Yj_Gdbh AND a.jjcp_yjno = c.yj_Yjno', 'left')
  168. ->join([$subQuery => 'total_sl'], 'a.jjcp_gdbh = total_sl.Gd_gdbh_sub AND a.jjcp_yjno = total_sl.jjcp_yjno_sub', 'left')
  169. ->field($field)
  170. ->where($where)
  171. ->where('a.jjcp_smb', 'like', '末%')
  172. ->group('Gd_gdbh, jjcp_yjno')
  173. ->order('warehousing_date DESC')
  174. ->page($pages, $limit)
  175. ->select();
  176. // 后续处理结果
  177. foreach ($list as $key => $value) {
  178. $status = $db3->name('工单_印件资料')
  179. ->where('Yj_Gdbh', $value['Gd_gdbh'])
  180. ->where('yj_Yjno', $value['jjcp_yjno'])
  181. ->value('质量考核');
  182. $list[$key]['工单质检废'] = $value['废品合计'] - $value['zcfp'];
  183. $list[$key]['质量考核'] = $status;
  184. $list[$key]['考核'] = ($status == 1) ? '否' : '是';
  185. if ($value['实际投料'] !== null) {
  186. $actualMaterial = $value['实际投料'] * 10000;
  187. $list[$key]['target_rate'] = $this->calculateRate($actualMaterial, $value['工单计划损耗']);
  188. $realRate = $value['warehousing_num'] / $actualMaterial * 100;
  189. $list[$key]['real_rate'] = number_format($realRate, 2) . '%';
  190. } else {
  191. $list[$key]['target_rate'] = null;
  192. $list[$key]['real_rate'] = null;
  193. }
  194. $list[$key]['reward_rate'] = '1'; // 如有实际逻辑,可替换
  195. }
  196. //只保留考核为“是”的数据
  197. $list = array_filter($list, function ($item) {
  198. return $item['考核'] === '是';
  199. });
  200. // 重新整理数组索引(重0开始)
  201. $list = array_values($list);
  202. if (!empty($list)) {
  203. $this->success('成功', $list);
  204. } else {
  205. $this->error('未找到数据');
  206. }
  207. }
  208. // 计算合格率
  209. private function calculateRate($actual, $loss)
  210. {
  211. if ($actual == 0) {
  212. return '0.00%'; // 防止除以零
  213. }
  214. $rate = ($actual - $loss) / $actual * 100;
  215. return number_format($rate, 2) . '%';
  216. }
  217. /**
  218. * AI 超节损分析(月度)
  219. */
  220. // public function oversizedloss() {
  221. // $params = $this->request->param();
  222. // $service = new ImageService();
  223. // $service->chaojiesun_handleImage($params);
  224. // $this->success('任务成功提交至队列');
  225. // }
  226. // public function oversizedloss() {
  227. // // 读取原始 POST JSON body
  228. // $raw = $this->request->getInput();
  229. // $params = json_decode($raw, true);
  230. //
  231. // if (empty($params['content'])) {
  232. // return json(['code' => 1, 'msg' => 'content 不能为空']);
  233. // }
  234. //
  235. // // 取出 content
  236. //// $promptData = $params['content']['content'];
  237. // $promptData = $params['content'];
  238. //
  239. // // 统一处理为字符串
  240. // if (is_array($promptData)) {
  241. // // 如果是二维数组(多个工单)
  242. // if (isset($promptData[0]) && is_array($promptData[0])) {
  243. // $lines = array_map(function($row) {
  244. // return implode(', ', array_map(
  245. // fn($k, $v) => "$k: $v",
  246. // array_keys($row),
  247. // array_values($row)
  248. // ));
  249. // }, $promptData);
  250. // $promptContent = implode("\n", $lines);
  251. // }
  252. // // 如果是一维数组(单个工单)
  253. // else {
  254. // $promptContent = implode(', ', array_map(
  255. // fn($k, $v) => "$k: $v",
  256. // array_keys($promptData),
  257. // array_values($promptData)
  258. // ));
  259. // }
  260. // } else {
  261. // // 已经是字符串,无需处理
  262. // $promptContent = $promptData;
  263. // }
  264. //
  265. // // 查询模板内容
  266. // $db3 = \db()->connect(config('database.db3'));
  267. // $list = $db3->name('workorder_template')->where("id",1)->find();
  268. //
  269. // $prompt = $list['content'] . "\n" . $promptContent;
  270. //
  271. // // 调用 AI
  272. // $ai = new AIGatewayService();
  273. // $gptRes = $ai->chaojiesunGptApi($prompt);
  274. //
  275. // $gptText = trim($gptRes['choices'][0]['message']['content'] ?? '');
  276. //
  277. // return json(['code' => 0, 'data' => $gptText]);
  278. // }
  279. }