From e3aaa6c1612c5f01e975a204e81c2f3fdb114f81 Mon Sep 17 00:00:00 2001 From: zxf <1532322479@qq.com> Date: Sun, 10 May 2026 16:44:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=87=BA=E5=85=A5=E5=BA=93=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E5=AF=BC=E5=87=BA=E4=BF=AE=E6=94=B9=E3=80=81=E7=89=A9?= =?UTF-8?q?=E8=B5=84=E7=9B=98=E7=82=B9=E6=98=BE=E7=A4=BA=E5=92=8C=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E4=BF=AE=E6=94=B9=E3=80=81=E6=B7=BB=E5=8A=A0=E7=82=B9?= =?UTF-8?q?=E8=B8=A9=E7=90=86=E7=94=B1=E6=98=BE=E7=A4=BA=E3=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/package.json | 1 + admin/src/api/ckbillcargo.js | 14 +- admin/src/api/ckstockchange.js | 12 + admin/src/api/pmdailymenu.js | 39 + admin/src/api/pmdailymenudtllike.js | 8 +- admin/src/api/pmproductclass.js | 12 + admin/src/api/systemadmin.js | 8 +- admin/src/directive/dialog/drag.js | 101 +- .../components/StockChoose/index.vue | 4 +- .../ckoutbound/components/StockOut/index.vue | 63 +- admin/src/views/ck/ckoutbound/index.vue | 243 +++- .../src/views/ck/ckstock/index-inventory.vue | 663 +++++++++ admin/src/views/ck/ckstock/index.vue | 120 +- .../ckwarehouse/components/BillEdit/index.vue | 35 +- .../ckwarehouse/components/BillList/index.vue | 51 +- admin/src/views/ck/ckwarehouse/index.vue | 738 ++++++++-- admin/src/views/ck/instock/index.vue | 421 +++--- admin/src/views/ck/outstock/index.vue | 705 ++++++++++ .../views/cm/cust/cmcust-add-and-update.vue | 6 +- .../product/cmcustproduct-add-and-update.vue | 18 +- .../src/views/cm/cust/product/index-cust.vue | 4 +- admin/src/views/cm/cust/product/index.vue | 4 +- .../qualify/cmcustqualify-add-and-update.vue | 4 +- .../cmstoragelocation-add-and-update.vue | 4 +- .../warehouse/cmwarehouse-add-and-update.vue | 3 +- .../charge/fmbillcharge-add-and-update.vue | 4 +- admin/src/views/fm/bill/charge/index.vue | 4 +- .../fm/cash/writeoff/components/Charge.vue | 2 +- .../views/pm/canteen/demand/DispatchList.vue | 4 +- .../demand/pmcanteendemand-add-and-update.vue | 5 +- ...pmcanteenpurchasedetail-add-and-update.vue | 4 +- .../purchase/order/OrderDetailList.vue | 4 +- .../pmcanteenpurchaseorder-add-and-update.vue | 8 +- .../pm/canteen/purchase/plan/DispatchList.vue | 4 +- .../views/pm/canteen/purchase/plan/index.vue | 2 +- .../pmcanteenpurchaseplan-add-and-update.vue | 8 +- .../views/pm/daily/menu/dtl/index-like.vue | 126 +- .../dtl/pmdailymenudtl-add-and-update.vue | 4 +- admin/src/views/pm/daily/menu/index.vue | 141 +- .../pm/houser/pmhouse-add-and-update.vue | 4 +- .../pmmaintenancedispatch-add-and-update.vue | 2 +- .../src/views/pm/maintenance/order/index.vue | 26 +- .../pmmaintenanceorder-add-and-update.vue | 4 +- .../class/pmproductclass-add-and-update.vue | 3 +- app/pages/supply_chain/dispatch/index.vue | 2 +- .../controller/AdminLoginController.java | 6 + .../controller/SystemAdminController.java | 1 - .../zbkj/admin/service/AdminLoginService.java | 5 + .../service/impl/AdminLoginServiceImpl.java | 85 +- .../common/request/QuickOutBillRequest.java | 12 + .../common/response/InventoryResponse.java | 117 ++ .../controller/CkBillCargoController.java | 1044 +++++++++++++- .../controller/CkBillController.java | 1232 ++++++++++++++++- .../controller/CkCargoStockController.java | 73 +- .../controller/CkStockChangeController.java | 29 + .../controller/CmCustController.java | 2 + .../controller/CmCustProductController.java | 2 +- .../controller/PmDailyMenuController.java | 718 +++++++++- .../PmDailyMenuDtlLikeController.java | 28 +- .../PmMaintenanceDispatchController.java | 3 + .../PmMaintenanceOrderController.java | 101 +- .../autogencode/entity/CkBillCargo.java | 33 + .../autogencode/entity/CkCargoStock.java | 29 + .../autogencode/entity/CkStockChange.java | 13 + .../entity/PmDailyMenuDtlLike.java | 13 + .../autogencode/entity/vo/ImportItemVO.java | 23 + .../service/CkStockChangeService.java | 15 + .../service/PmDailyMenuService.java | 9 + .../service/SysDictDataService.java | 7 + .../service/impl/CkBillServiceImpl.java | 212 ++- .../impl/CkStockChangeServiceImpl.java | 620 ++++++++- .../service/impl/PmDailyMenuServiceImpl.java | 16 + .../service/impl/SysDictDataServiceImpl.java | 6 + .../common/constants/NotifyConstants.java | 5 + .../zbkj/common/model/wechat/WechatFans.java | 82 ++ .../zbkj/common/request/PageParamRequest.java | 10 + .../request/RegisterThirdUserRequest.java | 3 + .../request/SubscribeMessageRequest.java | 61 + .../response/SubscribeMessageResponse.java | 26 + .../common/token/FrontTokenComponent.java | 4 +- .../com/zbkj/front/CrmebFrontApplication.java | 2 + .../front/controller/WeChatController.java | 51 +- .../service/impl/UserCenterServiceImpl.java | 1 + .../com/zbkj/service/dao/SystemMenuDao.java | 2 + .../com/zbkj/service/dao/WechatFansDao.java | 21 + .../service/service/SystemMenuService.java | 7 + .../service/service/WechatFansService.java | 60 + .../service/impl/SystemAdminServiceImpl.java | 2 + .../service/impl/SystemMenuServiceImpl.java | 10 + .../service/service/impl/UserServiceImpl.java | 1 + .../service/impl/WechatFansServiceImpl.java | 270 ++++ .../com/zbkj/service/util/WeiXinUtil.java | 8 +- .../mapper/system/SystemMenuMapper.xml | 11 + 93 files changed, 8039 insertions(+), 694 deletions(-) create mode 100644 admin/src/views/ck/ckstock/index-inventory.vue create mode 100644 admin/src/views/ck/outstock/index.vue create mode 100644 crmeb/crmeb-admin/src/main/java/com/zbkj/common/response/InventoryResponse.java create mode 100644 crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/vo/ImportItemVO.java create mode 100644 crmeb/crmeb-common/src/main/java/com/zbkj/common/model/wechat/WechatFans.java create mode 100644 crmeb/crmeb-common/src/main/java/com/zbkj/common/request/SubscribeMessageRequest.java create mode 100644 crmeb/crmeb-common/src/main/java/com/zbkj/common/response/SubscribeMessageResponse.java create mode 100644 crmeb/crmeb-service/src/main/java/com/zbkj/service/dao/WechatFansDao.java create mode 100644 crmeb/crmeb-service/src/main/java/com/zbkj/service/service/WechatFansService.java create mode 100644 crmeb/crmeb-service/src/main/java/com/zbkj/service/service/impl/WechatFansServiceImpl.java diff --git a/admin/package.json b/admin/package.json index 1842ea9..c6736a3 100644 --- a/admin/package.json +++ b/admin/package.json @@ -38,6 +38,7 @@ }, "dependencies": { "@babel/parser": "^7.9.6", + "@element-plus/icons-vue": "^2.3.2", "@riophae/vue-treeselect": "0.4.0", "async-validator": "^1.11.2", "axios": "^0.24.0", diff --git a/admin/src/api/ckbillcargo.js b/admin/src/api/ckbillcargo.js index ccf833f..b994593 100644 --- a/admin/src/api/ckbillcargo.js +++ b/admin/src/api/ckbillcargo.js @@ -42,9 +42,8 @@ export function ckbillcargoDetailApi(id) { */ export function ckbillcargoDeleteApi(ids) { return request({ - url: `autogencode/ckbillcargo/delete`, - method: 'POST', - data: ids + url: `autogencode/ckbillcargo/` + ids.join(','), + method: 'DELETE' }) } @@ -99,6 +98,15 @@ export function selectTotalWt(query) { }) } +// 查询出库合计(数量和金额) +export function selectTotal(query) { + return request({ + url: 'autogencode/ckbillcargo/selectTotal', + method: 'get', + params: query + }) +} + export function recallOutBill(id) { return request({ url: 'autogencode/ckbillcargo/recallOutBill/' + id, diff --git a/admin/src/api/ckstockchange.js b/admin/src/api/ckstockchange.js index 08a808b..15adabb 100644 --- a/admin/src/api/ckstockchange.js +++ b/admin/src/api/ckstockchange.js @@ -61,3 +61,15 @@ export function ckstockchangeListApi(params) { }) } +/** + * 物资盘点表查询 + * @param data 查询条件 + */ +export function inventoryListApi(data) { + return request({ + url: `autogencode/ckstockchange/inventory`, + method: 'POST', + data + }) +} + diff --git a/admin/src/api/pmdailymenu.js b/admin/src/api/pmdailymenu.js index 21d9b3f..852cac4 100644 --- a/admin/src/api/pmdailymenu.js +++ b/admin/src/api/pmdailymenu.js @@ -144,3 +144,42 @@ export function pmdailymenuListByAll(params) { }) } +/** + * pmdailymenu导出 + * @param params + */ +export function pmdailymenuExportApi(params) { + return request({ + url: `autogencode/pmdailymenu/export`, + method: 'GET', + params, + responseType: 'blob' + }) +} + +/** + * pmdailymenu导入 + * @param formData + */ +export function pmdailymenuImportApi(formData) { + return request({ + url: `autogencode/pmdailymenu/import`, + method: 'POST', + data: formData, + headers: { + 'Content-Type': 'multipart/form-data' + } + }) +} + +/** + * pmdailymenu导入模板下载 + */ +export function pmdailymenuImportTemplateApi() { + return request({ + url: `autogencode/pmdailymenu/import/template`, + method: 'GET', + responseType: 'blob' + }) +} + diff --git a/admin/src/api/pmdailymenudtllike.js b/admin/src/api/pmdailymenudtllike.js index b573d04..5bc8bdf 100644 --- a/admin/src/api/pmdailymenudtllike.js +++ b/admin/src/api/pmdailymenudtllike.js @@ -65,14 +65,18 @@ export function pmdailymenudtllikeListApi(params) { * 对菜单明细进行点赞/点踩 * @param menuDtlId 菜单明细ID * @param likeType 评价类型:1-点赞,2-点踩 + * @param remark 备注(可选) + * @param uid 手机端用户ID(可选) */ -export function pmdailymenudtllikeLikeApi(menuDtlId, likeType) { +export function pmdailymenudtllikeLikeApi(menuDtlId, likeType, remark, uid) { return request({ url: `autogencode/pmdailymenudtllike/like`, method: 'POST', params: { menuDtlId, - likeType + likeType, + remark, + uid } }) } diff --git a/admin/src/api/pmproductclass.js b/admin/src/api/pmproductclass.js index 7852f22..5544b5e 100644 --- a/admin/src/api/pmproductclass.js +++ b/admin/src/api/pmproductclass.js @@ -73,3 +73,15 @@ export function pmproductclassTreeSelect(params) { }) } +/** + * 获取商品类别列表 + * @param params + */ +export function listProductClass(params) { + return request({ + url: `autogencode/pmproductclass/list`, + method: 'GET', + params + }) +} + diff --git a/admin/src/api/systemadmin.js b/admin/src/api/systemadmin.js index c4023fe..b569bdd 100644 --- a/admin/src/api/systemadmin.js +++ b/admin/src/api/systemadmin.js @@ -63,7 +63,8 @@ export function adminAdd(pram) { status: pram.status, phone: pram.phone, depts: pram.depts, - posts: pram.posts + posts: pram.posts, + isDefaultReceiver: pram.isDefaultReceiver } return request({ url: '/admin/system/admin/save', @@ -84,7 +85,8 @@ export function adminUpdate(pram) { isDel: pram.isDel, phone: pram.phone, depts: pram.depts, - posts: pram.posts + posts: pram.posts, + isDefaultReceiver: pram.isDefaultReceiver } return request({ url: '/admin/system/admin/update', @@ -204,4 +206,4 @@ export function updateIsSmsApi(params) { method: 'get', params }) -} \ No newline at end of file +} diff --git a/admin/src/directive/dialog/drag.js b/admin/src/directive/dialog/drag.js index 2e82346..16fe59d 100644 --- a/admin/src/directive/dialog/drag.js +++ b/admin/src/directive/dialog/drag.js @@ -1,64 +1,57 @@ /** -* v-dialogDrag 弹窗拖拽 -* Copyright (c) 2019 ruoyi -*/ - + * v-dialogDrag 弹窗拖拽(终极稳定版) + * 解决:初始居中 + 按下闪烁、偏移错误、飞到右下角 + */ export default { - bind(el, binding, vnode, oldVnode) { + bind(el, binding) { const value = binding.value - if (value == false) return - // 获取拖拽内容头部 - const dialogHeaderEl = el.querySelector('.el-dialog__header'); - const dragDom = el.querySelector('.el-dialog'); - dialogHeaderEl.style.cursor = 'move'; - // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null); - const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null); - dragDom.style.position = 'absolute'; - dragDom.style.marginTop = 0; - let width = dragDom.style.width; - if (width.includes('%')) { - width = +document.body.clientWidth * (+width.replace(/\%/g, '') / 100); - } else { - width = +width.replace(/\px/g, ''); - } - dragDom.style.left = `${(document.body.clientWidth - width) / 2}px`; - // 鼠标按下事件 - dialogHeaderEl.onmousedown = (e) => { - // 鼠标按下,计算当前元素距离可视区的距离 (鼠标点击位置距离可视窗口的距离) - const disX = e.clientX - dialogHeaderEl.offsetLeft; - const disY = e.clientY - dialogHeaderEl.offsetTop; + if (value === false) return - // 获取到的值带px 正则匹配替换 - let styL, styT; + const dialogHeaderEl = el.querySelector('.el-dialog__header') + const dragDom = el.querySelector('.el-dialog') - // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px - if (sty.left.includes('%')) { - styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100); - styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100); - } else { - styL = +sty.left.replace(/\px/g, ''); - styT = +sty.top.replace(/\px/g, ''); - }; + if (!dialogHeaderEl || !dragDom) return - // 鼠标拖拽事件 - document.onmousemove = function (e) { - // 通过事件委托,计算移动的距离 (开始拖拽至结束拖拽的距离) - const l = e.clientX - disX; - const t = e.clientY - disY; + dialogHeaderEl.style.cursor = 'move' - let finallyL = l + styL - let finallyT = t + styT + // 初始化:绝对居中 + dragDom.style.position = 'fixed' + dragDom.style.margin = '0' + dragDom.style.left = '50%' + dragDom.style.top = '50%' + dragDom.style.transform = 'translate(-50%, -50%)' - // 移动当前元素 - dragDom.style.left = `${finallyL}px`; - dragDom.style.top = `${finallyT}px`; - - }; - - document.onmouseup = function (e) { - document.onmousemove = null; - document.onmouseup = null; - }; + // 鼠标按下 + dialogHeaderEl.onmousedown = (e) => { + e.preventDefault() + + // 先拿到当前弹窗真实的 left / top(去掉 translate 影响) + const currLeft = dragDom.getBoundingClientRect().left + const currTop = dragDom.getBoundingClientRect().top + + // 按下瞬间立刻清除居中,重新设置真实 left top + dragDom.style.transform = 'none' + dragDom.style.left = currLeft + 'px' + dragDom.style.top = currTop + 'px' + + // 计算正确偏移(这是唯一不飞的算法) + const disX = e.clientX - currLeft + const disY = e.clientY - currTop + + // 移动 + document.onmousemove = (e) => { + let left = e.clientX - disX + let top = e.clientY - disY + + dragDom.style.left = left + 'px' + dragDom.style.top = top + 'px' + } + + // 抬起 + document.onmouseup = () => { + document.onmousemove = null + document.onmouseup = null + } } } -}; \ No newline at end of file +} diff --git a/admin/src/views/ck/ckoutbound/components/StockChoose/index.vue b/admin/src/views/ck/ckoutbound/components/StockChoose/index.vue index c900d8a..ff54302 100644 --- a/admin/src/views/ck/ckoutbound/components/StockChoose/index.vue +++ b/admin/src/views/ck/ckoutbound/components/StockChoose/index.vue @@ -170,11 +170,11 @@ export default { }, getList() { this.list = []; - if (!this.custId) return; this.loading = true; listCkstock({ - custId: this.custId, + custId: this.custId || '', gdsSeqno: this.searchGdsSeqno, + isShow: false, // 不显示库存为0的商品 }).then(res => { this.loading = false; this.list = res || []; diff --git a/admin/src/views/ck/ckoutbound/components/StockOut/index.vue b/admin/src/views/ck/ckoutbound/components/StockOut/index.vue index a3712a3..2446fb7 100644 --- a/admin/src/views/ck/ckoutbound/components/StockOut/index.vue +++ b/admin/src/views/ck/ckoutbound/components/StockOut/index.vue @@ -206,17 +206,31 @@ + + + + + + + + @@ -323,9 +337,7 @@ export default { billNumber: [ { required: true, message: '单据编号不能为空', trigger: 'blur' } ], - custId: [ - { required: true, message: '供应商不能为空', trigger: 'blur' } - ], + custId: [], cargoName: [ { required: true, message: '商品名称不能为空', trigger: 'blur' } ], @@ -407,11 +419,34 @@ export default { // this.getVesselList(); }, methods: { - // getCargoValue(row){ - // const value = row.unitPrice * row.cargoWt; - // row.cargoValue = isNaN(value) ? 0 : value.toFixed(4); - // return row.cargoValue; - // }, + getCargoValue(row){ + if (!row) return 0; + const value = (row.unitPrice || 0) * (row.cargoWt || 0); + row.cargoValue = isNaN(value) ? 0 : value.toFixed(4); + return row.cargoValue; + }, + // 领用数量变化时更新金额 + onCargoWtChange(row) { + if (row) { + const value = (row.unitPrice || 0) * (row.cargoWt || 0); + row.cargoValue = isNaN(value) ? 0 : value.toFixed(4); + } + }, + // 获取最大金额(库存数量 × 单价) + getMaxCargoValue(row) { + if (!row) return Infinity; + const maxWt = this.getMaxData(row).cargoWt; + const value = (row.unitPrice || 0) * maxWt; + return isNaN(value) ? Infinity : value; + }, + // 金额输入处理,限制不超过最大值 + onCargoValueInput(row, event) { + const value = parseFloat(event); + const maxValue = this.getMaxCargoValue(row); + if (!isNaN(value) && value > maxValue) { + row.cargoValue = maxValue.toFixed(4); + } + }, init() { this.getCustList(); this.getMenuList(); @@ -838,10 +873,6 @@ export default { }); }, addStockOut() { - if (!this.form.custId) { - this.$message('请先选择供应商'); - return; - } this.open = true; }, // 获取领用明细列表 @@ -908,11 +939,9 @@ export default { }, // 获取仓库库存列表 getStockList() { - if (!this.form.custId) { - return; - } listCkstock({ - custId: this.form.custId, + custId: this.form.custId || '', + isShow: false, // 不显示库存为0的商品 }).then(res => { this.stockList = res || []; }).catch(e => { diff --git a/admin/src/views/ck/ckoutbound/index.vue b/admin/src/views/ck/ckoutbound/index.vue index 78a1403..b49f614 100644 --- a/admin/src/views/ck/ckoutbound/index.vue +++ b/admin/src/views/ck/ckoutbound/index.vue @@ -3,13 +3,40 @@ + + 导入 + + + + 导出 + + - -

单据列表

- + +
+ + +
+

+ 单据列表

+
@@ -120,6 +147,8 @@ + + @@ -128,7 +157,7 @@ - + @@ -148,6 +180,38 @@ + + + + + +
将文件拖到此处,或点击上传
+
+
+ + 是否更新已经存在的数据 +
+ 仅允许导入xls、xlsx格式文件。 + 下载模板 +
+
+ +
@@ -172,6 +236,7 @@ import { ckbillDeleteApi as delCkbill, auditBill, cancelAuditBill, rejectBill} f import SearchBlock from '@/components/SearchBlock'; // import { listVessel } from "@/api/jxc/vessel"; import { pmdailymenuListByAll } from "@/api/pmdailymenu.js"; +import request from '@/utils/request'; export default { name: 'Ckoutbound', @@ -341,6 +406,25 @@ export default { readonly: false, // 表单参数 form: {}, + // 导入参数 + upload: { + // 是否显示弹出层 + open: false, + // 弹出层标题 + title: "出库信息导入", + // 是否禁用上传 + isUploading: false, + // 是否更新已存在数据 + updateSupport: 0, + // 请求头 + headers: { + Authorization: "" + }, + // 上传的文件列表 + fileList: [] + }, + // 左侧列是否折叠 + isCollapse: false, } }, created() { @@ -358,20 +442,37 @@ export default { // }); // }, // 点击单据 - billClick(row) { - const isChange = this.selectedBill.id !== row.id; - this.selectedBill = row || {}; - this.queryParams.billNumber = this.selectedBill.billNumber; - // 切换的时候重新搜索,否则刷新当前 - if (isChange) { + billClick(row, isAutoRestore = false) { + if (row && Object.keys(row).length > 0) { + if (isAutoRestore) { + // 如果是自动恢复选中状态,直接更新selectedBill和查询参数 + this.selectedBill = row || {}; + this.queryParams.pageNum = 1; + this.queryParams.billNumber = this.selectedBill.billNumber; + // 触发右侧的查询 + this.getList(); + } else { + const isChange = this.selectedBill.id !== row.id; + this.selectedBill = row || {}; + this.queryParams.billNumber = this.selectedBill.billNumber; + // 切换的时候重新搜索,否则刷新当前 + if (isChange) { + this.queryParams.pageNum = 1; + } + if (this.queryParams.billNumber) { + this.getList(); + } else { + this.ckcargoList = []; + } + } + } else if (!row || Object.keys(row).length === 0) { + // 清空选中状态 + this.selectedBill = {}; this.queryParams.pageNum = 1; - } - if (this.queryParams.billNumber) { + this.queryParams.billNumber = null; + // 触发右侧的查询 this.getList(); - } else { - this.ckcargoList = []; } - }, handleClose(show) { if (!show) { @@ -455,6 +556,9 @@ export default { this.handleQuery() }, backBill() { + // 保存当前选中的单据ID和单据编号 + const selectedBillId = this.selectedBill.id; + const billNumber = this.selectedBill.billNumber; this.$confirm(`确定撤销当前单据吗?`, '温馨提示', { confirmButtonText: '确定', cancelButtonText: '取消', @@ -467,14 +571,27 @@ export default { message: '撤销成功!', type: 'success' }); + // 更新selectedBill的状态,使按钮状态立即变化 + this.selectedBill.billStatus = '0'; this.loading = false; + // 刷新列表并保持选中状态 this.refreshList(); + // 刷新右侧商品列表 + this.$nextTick(() => { + if (billNumber) { + this.queryParams.billNumber = billNumber; + this.getList(); + } + }); }).catch(e => { this.loading = false; }); }); }, confirm() { + // 保存当前选中的单据ID和单据编号 + const selectedBillId = this.selectedBill.id; + const billNumber = this.selectedBill.billNumber; // 检查是否自动出库 if (this.selectedBill.autoOut === '1') { this.$confirm(`当前单据设置为自动出库,确认后将会自动生成出库单据。\n确定提交当前单据吗?`, '温馨提示', { @@ -489,8 +606,18 @@ export default { message: '提交成功!', type: 'success' }); + // 更新selectedBill的状态,使按钮状态立即变化 + this.selectedBill.billStatus = '1'; this.loading = false; + // 刷新列表并保持选中状态 this.refreshList(); + // 刷新右侧商品列表 + this.$nextTick(() => { + if (billNumber) { + this.queryParams.billNumber = billNumber; + this.getList(); + } + }); }).catch(e => { this.loading = false; }); @@ -508,8 +635,18 @@ export default { message: '提交成功!', type: 'success' }); + // 更新selectedBill的状态,使按钮状态立即变化 + this.selectedBill.billStatus = '1'; this.loading = false; + // 刷新列表并保持选中状态 this.refreshList(); + // 刷新右侧商品列表 + this.$nextTick(() => { + if (billNumber) { + this.queryParams.billNumber = billNumber; + this.getList(); + } + }); }).catch(e => { this.loading = false; }); @@ -616,7 +753,13 @@ export default { this.$modal.confirm('是否确认删除"' + this.selectedBill.billNumber+ '"的领用单据?').then(() => { return delCkbill(ids); }).then(() => { + // 清空选中状态和查询参数 + this.selectedBill = {}; + this.queryParams.billNumber = null; + // 刷新左侧单据列表 this.refreshList(); + // 刷新右侧商品列表 + this.getList(); this.$modal.msgSuccess("删除成功"); }).catch(() => {}); } @@ -700,6 +843,76 @@ export default { this.download('jxc/ckcargo/export', { ...this.queryParams }, `仓库领用管理_${new Date().getTime()}.xlsx`) + }, + /** 导入按钮操作 */ + handleImport() { + this.upload.title = "出库信息导入"; + this.upload.open = true; + }, + /** 下载模板操作 */ + importTemplate() { + // 调用后端API下载出库模板 + request({ + url: '/autogencode/ckbill/downloadOutTemplate', + method: 'get', + responseType: 'blob' + }).then(blob => { + // 直接使用blob数据 + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', `出库导入模板_${new Date().getTime()}.xlsx`); + document.body.appendChild(link); + link.click(); + // 清理 + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + }).catch(error => { + console.error('下载模板失败:', error); + this.$modal.msgError('下载模板失败,请重试'); + }); + }, + // 文件上传中处理 + handleFileUploadProgress(event, file, fileList) { + this.upload.isUploading = true; + }, + // 文件上传成功处理 + handleFileSuccess(response, file, fileList) { + this.upload.open = false; + this.upload.isUploading = false; + this.$refs.upload.clearFiles(); + this.$alert("
{ + this.$modal.msgSuccess('导入成功'); + this.upload.open = false; + this.upload.fileList = []; + // 导入成功后刷新列表 + this.refreshList(); + }).catch(error => { + this.$modal.msgError('导入失败:' + (error.response?.data?.message || '未知错误')); + }).finally(() => { + this.upload.isUploading = false; + }); } } } diff --git a/admin/src/views/ck/ckstock/index-inventory.vue b/admin/src/views/ck/ckstock/index-inventory.vue new file mode 100644 index 0000000..81e9e85 --- /dev/null +++ b/admin/src/views/ck/ckstock/index-inventory.vue @@ -0,0 +1,663 @@ + + + + + diff --git a/admin/src/views/ck/ckstock/index.vue b/admin/src/views/ck/ckstock/index.vue index 4bc06cf..673c64b 100644 --- a/admin/src/views/ck/ckstock/index.vue +++ b/admin/src/views/ck/ckstock/index.vue @@ -55,29 +55,37 @@ - - + + + - - - + + + + + - - - + + + + + + + + + + + + + + + this.warehouses, + optionsProp: 'id', + optionsLabel: 'stockName', + }, + { + label: '商品类别', + prop: 'classId', + type: 'select', + getOptions: () => this.productClassList, + optionsProp: 'id', + optionsLabel: 'className', + }, { label: '商品名称', prop: 'cargoName', @@ -209,6 +232,8 @@ export default { // 仓库名称数据 warehouses: [], + // 商品类别数据 + productClassList: [], // 遮罩层 loading: true, // 选中数组 @@ -329,16 +354,13 @@ export default { }, created() { this.getList(); - this.getStockList(); - this.getCustList(); - this.getCargoList(); - this.getVesselList(); - this.getBmvoyage(); this.getNewListCkstock(); listWarehouse({}).then(response => { - this.warehouses = response.data; + this.warehouses = response; }); + this.getProductClassList(); + }, methods: { getNewListCkstock() { @@ -365,34 +387,6 @@ export default { // } // }) // }, - getBmvoyage() { - let param = {}; - listBmvoyage(param).then(response => { - this.bmvoyageList = response.data; - }).catch(e => { - }); - }, - - //船舶 - setVesselDetail(vesselId) { - console.log(vesselId); - this.vesselList.forEach(ele => { - if (ele.id == vesselId) { - console.log("aa:", ele.vesselName); - this.form.vesselName = ele.vesselName; - - } - }) - }, - - getVesselList() { - let param = {}; - listVessel(param).then(response => { - this.vesselList = response.data; - }).catch(e => { - }); - }, - //商品 setCargoDetail(cargoId) { @@ -407,14 +401,6 @@ export default { }) }, - getCargoList() { - let param = {}; - listCargo(param).then(response => { - this.cargoList = response.data; - }).catch(e => { - }); - }, - //客户 setCustDetail(custId) { this.custList.forEach(ele => { @@ -443,11 +429,13 @@ export default { }) }, - getStockList() { - let param = {}; - listStock(param).then(response => { - this.stockList = response.data; + getProductClassList() { + pmproductclassTreeSelect({}).then(response => { + // 只取最顶级的分类 + this.productClassList = response || []; }).catch(e => { + console.error('获取物品类别列表失败:', e); + this.productClassList = []; }); }, diff --git a/admin/src/views/ck/ckwarehouse/components/BillEdit/index.vue b/admin/src/views/ck/ckwarehouse/components/BillEdit/index.vue index 51af97e..0f8e038 100644 --- a/admin/src/views/ck/ckwarehouse/components/BillEdit/index.vue +++ b/admin/src/views/ck/ckwarehouse/components/BillEdit/index.vue @@ -86,7 +86,11 @@ @@ -94,7 +98,16 @@
{{ scope.row.cargoWt }}
+ :min="0" controls-position="right" @change="updateCargoValue(scope.row)"/> + + +
+ + @@ -216,7 +229,7 @@ label="单位" > { this.list = response.list || []; @@ -129,18 +146,32 @@ export default { this.loading = false; if (this.list.length) { - let currentItem = this.list[0]; - if (this.selectItem.id) { + if (currentSelectedId) { // 如果当前有选中的则选中上次保存的单据 - const findIndex = this.list.findIndex(val => val.id === this.selectItem.id); + const findIndex = this.list.findIndex(val => val.id === currentSelectedId); if (findIndex > -1) { - currentItem = this.list[findIndex]; + const currentItem = this.list[findIndex]; + // 直接更新selectItem,不调用rowClick,避免触发父组件的billClick方法 + this.selectItem = currentItem; + // 设置表格选中状态 + this.$refs.table.setCurrentRow(currentItem); + // 通知父组件选中状态变化,但传递一个参数表示这是自动恢复选中状态 + this.$emit('rowClick', currentItem, true); + return; } } - this.rowClick(currentItem); - this.$refs.table.setCurrentRow(currentItem); - } else { - this.rowClick({}); + + if (this.autoSelectFirst) { + // 只有在没有之前选中的单据时,才自动选择第一个 + const currentItem = this.list[0]; + this.selectItem = currentItem; + this.$refs.table.setCurrentRow(currentItem); + this.$emit('rowClick', currentItem, true); + } + } else if (!this.list.length) { + this.selectItem = {}; + this.$refs.table.setCurrentRow(null); + this.$emit('rowClick', {}, true); } }).catch(e => { this.loading = false; diff --git a/admin/src/views/ck/ckwarehouse/index.vue b/admin/src/views/ck/ckwarehouse/index.vue index bc2cdf8..e399081 100644 --- a/admin/src/views/ck/ckwarehouse/index.vue +++ b/admin/src/views/ck/ckwarehouse/index.vue @@ -2,7 +2,13 @@ - + + + 新增单据 + + 导入 + >导入 + 导出 + >导出 + - -

单据列表

- + +
+ + +
+

+ 单据列表

+
+ + +
+ +
+ - 新增单据 - - - + 新增明细 + v-if="(!isCollapse && (this.selectedBill.sourceType == '1' && this.selectedBill.billStatus == '0'))" plain + icon="el-icon-plus" + @click="handleAdd('out')" v-hasPermi="['jxc:ckcargo:add']">新增明细 + - + 删除单据 + v-if="(!isCollapse && this.selectedBill.sourceType == '1' && this.selectedBill.billStatus == '0') || (isCollapse && this.modifyData.id)" plain + icon="el-icon-delete" + @click="handleDel" v-hasPermi="['jxc:ckcargo:remove']">{{ !isCollapse ? '删除单据' : '删除明细' }} + - - - 单据撤销 + + 单据撤销 + - - - 单据确认 + + 单据确认 + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + - - - - + + @pagination="getList"/>
+ :visible.sync="open" fullscreen append-to-body> @@ -128,6 +190,7 @@ :limit="1" accept=".xlsx, .xls" :headers="upload.headers" + :action="''" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" @@ -139,10 +202,13 @@
将文件拖到此处,或点击上传
- 是否更新已经存在的数据 + + 是否更新已经存在的数据
仅允许导入xls、xlsx格式文件。 - 下载模板 + 下载模板 +
@@ -156,11 +192,26 @@ pageSize: 10, totalPage: 0, dataListLoading: false, - dataListSelections: [], + dataListSelections: [], addOrUpdateVisible: false, menuDtlAddOrUpdateVisible: false, refreshKey: 0, - expandedRowKeys: [] // 存储展开的行的ID + expandedRowKeys: [], // 存储展开的行的ID + // 导入参数 + upload: { + // 是否显示弹出层 + open: false, + // 弹出层标题 + title: "", + // 是否禁用上传 + isUploading: false, + // 是否更新已经存在的数据 + updateSupport: 0, + // 设置上传的请求头部 + headers: {Authorization: "Bearer " + sessionStorage.getItem('token')}, + // 上传的地址 + url: process.env.VUE_APP_BASE_API + "/api/autogencode/pmdailymenu/import" + } } }, components: { @@ -326,6 +377,92 @@ }) }) }, + // 导出菜单 + exportHandle () { + // 构建导出参数 + const params = {} + + // 将查询条件添加到参数中 + if (this.dataForm.menuDate) params.menuDate = this.dataForm.menuDate; + if (this.dataForm.canteenName) params.canteenName = this.dataForm.canteenName; + if (this.dataForm.mealType) params.mealType = this.dataForm.mealType; + if (this.dataForm.status) params.status = this.dataForm.status; + if (this.dataForm.remark) params.remark = this.dataForm.remark; + + // 调用导出API + api.pmdailymenuExportApi(params).then(res => { + // 创建下载链接 + const url = window.URL.createObjectURL(new Blob([res])) + const link = document.createElement('a') + link.style.display = 'none' + link.href = url + link.setAttribute('download', `菜单导出_${new Date().getTime()}.xlsx`) + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + this.$message.success('导出成功') + }).catch(e => { + this.$message.error('导出失败') + }) + }, + // 导入按钮操作 + handleImport() { + this.upload.title = "菜单信息导入" + this.upload.open = true + }, + // 下载模板操作 + importTemplate() { + // 调用后端API下载模板 + api.pmdailymenuImportTemplateApi().then(blob => { + // 直接使用blob数据 + const url = window.URL.createObjectURL(blob) + const link = document.createElement('a') + link.href = url + link.setAttribute('download', `菜单导入模板_${new Date().getTime()}.xlsx`) + document.body.appendChild(link) + link.click() + // 清理 + document.body.removeChild(link) + window.URL.revokeObjectURL(url) + }).catch(error => { + console.error('下载模板失败:', error) + this.$message.error('下载模板失败,请重试') + }) + }, + // 文件上传中处理 + handleFileUploadProgress(event, file, fileList) { + this.upload.isUploading = true + }, + // 文件上传成功处理 + handleFileSuccess(response, file, fileList) { + this.upload.open = false + this.upload.isUploading = false + this.$refs.upload.clearFiles() + this.$alert("
" + response.msg + "
", "导入结果", {dangerouslyUseHTMLString: true}) + this.getDataList() + }, + // 提交上传文件 + submitFileForm() { + this.$refs.upload.submit() + }, + // 自定义上传方法 + uploadFile(file) { + const formData = new FormData() + formData.append('file', file.file) + formData.append('updateSupport', this.upload.updateSupport) + + api.pmdailymenuImportApi(formData).then(response => { + this.$message.success('导入成功') + this.upload.open = false + this.upload.fileList = [] + // 导入成功后刷新列表 + this.getDataList() + }).catch(error => { + this.$message.error('导入失败:' + (error.response?.data?.message || '未知错误')) + }).finally(() => { + this.upload.isUploading = false + }) + }, } } diff --git a/admin/src/views/pm/houser/pmhouse-add-and-update.vue b/admin/src/views/pm/houser/pmhouse-add-and-update.vue index b3ff3cf..bfa4673 100644 --- a/admin/src/views/pm/houser/pmhouse-add-and-update.vue +++ b/admin/src/views/pm/houser/pmhouse-add-and-update.vue @@ -4,7 +4,9 @@ :title="!dataForm.id ? '添加' : '修改'" :close-on-click-modal="false" :append-to-body="true" - :visible.sync="visible"> + :visible.sync="visible" + v-dialogDrag + > diff --git a/admin/src/views/pm/maintenance/dispatch/pmmaintenancedispatch-add-and-update.vue b/admin/src/views/pm/maintenance/dispatch/pmmaintenancedispatch-add-and-update.vue index 52b2446..4904981 100644 --- a/admin/src/views/pm/maintenance/dispatch/pmmaintenancedispatch-add-and-update.vue +++ b/admin/src/views/pm/maintenance/dispatch/pmmaintenancedispatch-add-and-update.vue @@ -186,7 +186,7 @@ export default { costAmount: '' , completePhoto: '' , ownerConfirm: '否' , - status: '0' , + status: '1' , files: [] }, diff --git a/admin/src/views/pm/maintenance/order/index.vue b/admin/src/views/pm/maintenance/order/index.vue index 8723038..ce4ce60 100644 --- a/admin/src/views/pm/maintenance/order/index.vue +++ b/admin/src/views/pm/maintenance/order/index.vue @@ -144,11 +144,12 @@ @@ -397,6 +398,27 @@ import DictTag from '@/components/DictTag' }).catch(() => { }) }, + // 提交订单(从草稿到待处理) + submitOrder (id) { + this.$confirm('确定要提交订单吗?提交后状态将变为待处理', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + // 调用更新状态的API + api.pmmaintenanceorderUpdateStatusAndRemarkApi({ + id: id, + status: '0', // 0是待处理状态 + remark: '' + }).then(res => { + this.$message.success('提交成功') + this.getDataList() + }).catch(() => { + this.$message.error('提交失败') + }) + }).catch(() => { + }) + }, } } diff --git a/admin/src/views/pm/maintenance/order/pmmaintenanceorder-add-and-update.vue b/admin/src/views/pm/maintenance/order/pmmaintenanceorder-add-and-update.vue index 57d4bfc..12969f8 100644 --- a/admin/src/views/pm/maintenance/order/pmmaintenanceorder-add-and-update.vue +++ b/admin/src/views/pm/maintenance/order/pmmaintenanceorder-add-and-update.vue @@ -256,7 +256,7 @@ export default { reportChannel: '', reportTime: '', urgencyLevel: '', - status: '0', + status: '1', remark: '', files: [], // 故障照片文件 afterProcessFiles: [] // 修复凭证文件 @@ -632,7 +632,7 @@ export default { }.bind(this)) } else { // 新增时设置默认文件上传权限 - this.setFileUploadPermissions('0'); // 默认为待处理状态 + this.setFileUploadPermissions('1'); // 默认为草稿状态 // 新增时清空故障照片和修复照片文件列表 this.faultPhotoFiles = [] this.afterProcessFiles = [] diff --git a/admin/src/views/pm/product/class/pmproductclass-add-and-update.vue b/admin/src/views/pm/product/class/pmproductclass-add-and-update.vue index 4d9b7f2..7bf3c90 100644 --- a/admin/src/views/pm/product/class/pmproductclass-add-and-update.vue +++ b/admin/src/views/pm/product/class/pmproductclass-add-and-update.vue @@ -5,7 +5,8 @@ :close-on-click-modal="false" :append-to-body="true" :visible.sync="visible" - width="600px"> + width="600px" + v-dialogDrag> diff --git a/app/pages/supply_chain/dispatch/index.vue b/app/pages/supply_chain/dispatch/index.vue index e78183c..ba92c26 100644 --- a/app/pages/supply_chain/dispatch/index.vue +++ b/app/pages/supply_chain/dispatch/index.vue @@ -454,7 +454,7 @@ export default { assigneContext: this.form.handleContent, dispatchNote: this.form.dispatchNote, expectedCompleteTime: this.form.expectedCompleteTime, - status: '0', + status: '1', assignTime: this.getCurrentTime(), files: [] }; diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/controller/AdminLoginController.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/controller/AdminLoginController.java index 110bc28..922fe2e 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/controller/AdminLoginController.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/controller/AdminLoginController.java @@ -62,6 +62,12 @@ public class AdminLoginController { public CommonResult getAdminInfo() { return CommonResult.success(adminLoginService.getInfoByToken()); } + + @ApiOperation(value="根据uid获取用户详情") + @GetMapping(value = "/getAdminInfoByUid") + public CommonResult getAdminInfoByUid(@RequestParam Long uid) { + return CommonResult.success(adminLoginService.getInfoByUid(uid)); + } /** * 获取登录页图片 diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/controller/SystemAdminController.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/controller/SystemAdminController.java index 81b2605..ed3eecd 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/controller/SystemAdminController.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/controller/SystemAdminController.java @@ -46,7 +46,6 @@ public class SystemAdminController { * @param systemAdminRequest 搜索条件 * @param pageParamRequest 分页参数 */ - @PreAuthorize("hasAuthority('admin:system:admin:list')") @ApiOperation(value = "分页列表") @RequestMapping(value = "/list", method = RequestMethod.GET) @ResponseBody diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/service/AdminLoginService.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/service/AdminLoginService.java index c01ce38..4db3135 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/service/AdminLoginService.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/service/AdminLoginService.java @@ -48,4 +48,9 @@ public interface AdminLoginService { * 根据Token获取对应用户信息 */ SystemAdminResponse getInfoByToken(); + + /** + * 根据uid获取管理员信息 + */ + SystemAdminResponse getInfoByUid(Long uid); } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/service/impl/AdminLoginServiceImpl.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/service/impl/AdminLoginServiceImpl.java index 58ba227..6f49be9 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/service/impl/AdminLoginServiceImpl.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/admin/service/impl/AdminLoginServiceImpl.java @@ -2,6 +2,7 @@ package com.zbkj.admin.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import com.zbkj.admin.filter.TokenComponent; import com.zbkj.admin.service.AdminLoginService; import com.zbkj.admin.service.ValidateCodeService; @@ -11,6 +12,8 @@ import com.zbkj.common.exception.CrmebException; import com.zbkj.common.model.system.SystemAdmin; import com.zbkj.common.model.system.SystemMenu; import com.zbkj.common.model.system.SystemPermissions; +import com.zbkj.common.model.system.SystemRole; +import com.zbkj.common.model.user.User; import com.zbkj.common.request.SystemAdminLoginRequest; import com.zbkj.common.response.MenusResponse; import com.zbkj.common.response.SystemAdminResponse; @@ -19,10 +22,8 @@ import com.zbkj.common.response.SystemLoginResponse; import com.zbkj.common.utils.SecurityUtil; import com.zbkj.common.vo.LoginUserVo; import com.zbkj.common.vo.MenuTree; -import com.zbkj.service.service.SystemAdminService; -import com.zbkj.service.service.SystemConfigService; -import com.zbkj.service.service.SystemGroupDataService; -import com.zbkj.service.service.SystemMenuService; +import com.zbkj.service.dao.SystemMenuDao; +import com.zbkj.service.service.*; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; @@ -33,6 +34,7 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -75,6 +77,16 @@ public class AdminLoginServiceImpl implements AdminLoginService { @Autowired private SystemMenuService systemMenuService; + + @Autowired + private SystemMenuDao systemMenuDao; + + @Autowired + private SystemRoleService systemRoleService; + + @Autowired + private UserService userService; + /** * PC登录 */ @@ -184,4 +196,69 @@ public class AdminLoginServiceImpl implements AdminLoginService { systemAdminResponse.setPermissionsList(permList); return systemAdminResponse; } + + /** + * 根据uid获取管理员信息 + */ + @Override + public SystemAdminResponse getInfoByUid(Long uid) { + // 先查询sysUser,然后用其中的adminId查询对应的管理员 + User user = userService.getById(uid); + if (user == null) { + throw new CrmebException("用户不存在"); + } + + Integer adminId = user.getAdminid(); + if (adminId == null) { + throw new CrmebException("用户不是管理员"); + } + + SystemAdmin systemAdmin = systemAdminService.getById(adminId); + if (systemAdmin == null) { + throw new CrmebException("管理员不存在"); + } + + SystemAdminResponse systemAdminResponse = new SystemAdminResponse(); + BeanUtils.copyProperties(systemAdmin, systemAdminResponse); + List roleList = Stream.of(systemAdmin.getRoles().split(",")).collect(Collectors.toList()); + List permList = CollUtil.newArrayList(); + if (roleList.contains("1")) { + permList.add("*:*:*"); + } else { + // 获取用户权限 + permList = getPermissionsByAdminId(systemAdmin.getId()); + } + systemAdminResponse.setPermissionsList(permList); + return systemAdminResponse; + } + + /** + * 根据管理员ID获取权限列表 + * @param adminId 管理员ID + * @return 权限列表 + */ + private List getPermissionsByAdminId(Integer adminId) { + SystemAdmin systemAdmin = systemAdminService.getById(adminId); + if (systemAdmin == null || systemAdmin.getIsDel()) { + throw new CrmebException("管理员不存在"); + } + + // 获取角色列表 + List roleList = new ArrayList<>(); + if (StrUtil.isNotBlank(systemAdmin.getRoles())) { + roleList = Stream.of(systemAdmin.getRoles().split(",")).collect(Collectors.toList()); + } + + // 如果是超管,返回所有权限 + if (roleList.contains("1")) { + return CollUtil.newArrayList("*:*:*"); + } + + // 获取用户权限 + List menuList =systemMenuDao.getMenusByUserIdAndPhoto(adminId); + menuList = menuList.stream().filter(e -> StrUtil.isNotEmpty(e.getPerms())).collect(Collectors.toList()); + List permissions = menuList.stream().map(SystemMenu::getPerms).collect(Collectors.toList()); + + return permissions; + } } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/common/request/QuickOutBillRequest.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/common/request/QuickOutBillRequest.java index bb19872..b106dab 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/common/request/QuickOutBillRequest.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/common/request/QuickOutBillRequest.java @@ -24,6 +24,12 @@ public class QuickOutBillRequest { @ApiModelProperty(value = "商品列表") private List items; + /** + * 用户id + */ + @ApiModelProperty(value = "用户id") + private Long uid; + /** * 快速出库商品项 */ @@ -42,6 +48,12 @@ public class QuickOutBillRequest { @ApiModelProperty(value = "数量") private BigDecimal quantity; + /** + * 入库单据id + */ + @ApiModelProperty(value = "入库单据id") + private Long ckCargoStockId; + } } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/common/response/InventoryResponse.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/common/response/InventoryResponse.java new file mode 100644 index 0000000..e72e1d3 --- /dev/null +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/common/response/InventoryResponse.java @@ -0,0 +1,117 @@ +package com.zbkj.common.response; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; + +import java.math.BigDecimal; + +/** + * 物资盘点表响应对象 + */ +@Data +public class InventoryResponse { + + /** + * 序号 + */ + @ApiModelProperty(value = "序号") + @ExcelProperty(value = "序号", index = 0) + @ColumnWidth(10) + private Integer index; + + /** + * 商品名称 + */ + @ApiModelProperty(value = "商品名称") + @ExcelIgnore + private String cargoName; + + /** + * 规格型号 + */ + @ApiModelProperty(value = "规格型号") + @ExcelIgnore + private String cargoSpec; + + /** + * 品名及规格 + */ + @ApiModelProperty(value = "品名及规格") + @ExcelProperty(value = "品名及规格", index = 1) + @ColumnWidth(30) + private String cargoNameWithSpec; + + /** + * 上期结存数量 + */ + @ApiModelProperty(value = "上期结存数量") + @ExcelProperty(value = {"上期结存", "数量"}, index = 2) + @ColumnWidth(15) + private BigDecimal lastCargoNum; + + /** + * 上期结存金额 + */ + @ApiModelProperty(value = "上期结存金额") + @ExcelProperty(value = {"上期结存", "金额"}, index = 3) + @ColumnWidth(15) + private BigDecimal lastCargoValue; + + /** + * 本期入库数量 + */ + @ApiModelProperty(value = "本期入库数量") + @ExcelProperty(value = {"本期入库", "数量"}, index = 4) + @ColumnWidth(15) + private BigDecimal inCargoNum; + + /** + * 本期入库金额 + */ + @ApiModelProperty(value = "本期入库金额") + @ExcelProperty(value = {"本期入库", "金额"}, index = 5) + @ColumnWidth(15) + private BigDecimal inCargoValue; + + /** + * 本期出库数量 + */ + @ApiModelProperty(value = "本期出库数量") + @ExcelProperty(value = {"本期出库", "数量"}, index = 6) + @ColumnWidth(15) + private BigDecimal outCargoNum; + + /** + * 本期出库金额 + */ + @ApiModelProperty(value = "本期出库金额") + @ExcelProperty(value = {"本期出库", "金额"}, index = 7) + @ColumnWidth(15) + private BigDecimal outCargoValue; + + /** + * 累计结存数量 + */ + @ApiModelProperty(value = "累计结存数量") + @ExcelProperty(value = {"累计结存", "数量"}, index = 8) + @ColumnWidth(15) + private BigDecimal cargoNum; + + /** + * 累计结存金额 + */ + @ApiModelProperty(value = "累计结存金额") + @ExcelProperty(value = {"累计结存", "金额"}, index = 9) + @ColumnWidth(15) + private BigDecimal cargoValue; + + /** + * 单位 + */ + @ApiModelProperty(value = "单位") + @ExcelIgnore + private String unit; +} \ No newline at end of file diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkBillCargoController.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkBillCargoController.java index 02db833..e6096b7 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkBillCargoController.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkBillCargoController.java @@ -8,24 +8,28 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URLEncoder; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import javax.servlet.http.HttpServletResponse; import cn.hutool.core.lang.Validator; import cn.hutool.core.util.StrUtil; import com.zbkj.common.model.system.SysDept; import com.zbkj.common.model.system.SystemAdmin; import com.zbkj.modules.autogencode.entity.*; -import com.zbkj.modules.autogencode.service.CkStockChangeService; +import com.zbkj.modules.autogencode.service.*; import com.zbkj.service.service.SysDeptService; import com.zbkj.service.service.SystemAdminService; +import com.zbkj.modules.autogencode.service.CmCustService; import org.apache.commons.lang3.StringUtils; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.zbkj.common.request.PageParamRequest; import com.zbkj.common.response.CommonResult; import com.zbkj.common.page.CommonPage; -import com.zbkj.modules.autogencode.service.CkBillService; -import com.zbkj.modules.autogencode.service.CkBillCargoService; -import com.zbkj.modules.autogencode.service.CkBillStockService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -58,6 +62,18 @@ public class CkBillCargoController { @Autowired private SysDeptService systemDeptService; + + @Autowired + private CmCustProductService cmCustProductService; + + @Autowired + private PmProductClassService pmProductClassService; + + @Autowired + private SysDictDataService sysDictDataService; + + @Autowired + private CmCustService cmCustService; /** * 条件设置 * 根据实体类字段自动生成查询条件 @@ -201,7 +217,36 @@ public class CkBillCargoController { // 货主名称 if (StrUtil.isNotBlank(request.getCustName())) { - queryWrapper.eq(CkBillCargo::getCustName, request.getCustName()); + queryWrapper.like(CkBillCargo::getCustName, request.getCustName()); + } + + // 商品分类ID(通过关联CmCustProduct表查询) + if (request.getClassId() != null) { + queryWrapper.inSql(CkBillCargo::getCargoId, + "select id from cm_cust_product where class_id = " + request.getClassId()); + } + + // 商品分类ID列表(支持多选,逗号分隔字符串或数组格式) + if (StrUtil.isNotBlank(request.getClassIds())) { + String classIdsStr = request.getClassIds(); + // 处理数组格式 [1,2,3] 或 ["1","2","3"] + if (classIdsStr.startsWith("[")) { + classIdsStr = classIdsStr.replace("[", "").replace("]", "").replace("\"", ""); + } + // 处理数组参数格式 classIds[]=1&classIds[]=2 转换后的字符串 + if (classIdsStr.contains("classIds[]=")) { + String[] parts = classIdsStr.split("&"); + StringBuilder sb = new StringBuilder(); + for (String part : parts) { + if (part.startsWith("classIds[]=")) { + if (sb.length() > 0) sb.append(","); + sb.append(part.replace("classIds[]=", "")); + } + } + classIdsStr = sb.toString(); + } + queryWrapper.inSql(CkBillCargo::getCargoId, + "select id from cm_cust_product where class_id in (" + classIdsStr + ")"); } // 业务性质 @@ -433,6 +478,51 @@ public class CkBillCargoController { } } } + + // 根据cargoId查询CmCustProduct获取分类信息 + if (cargo.getCargoId() != null) { + CmCustProduct product = cmCustProductService.getById(cargo.getCargoId()); + if (product != null) { + cargo.setClassId(product.getClassId()); + // 根据classId查询物资类别代码 + if (product.getClassId() != null) { + PmProductClass productClass = pmProductClassService.getById(product.getClassId()); + if (productClass != null) { + cargo.setClassCode(productClass.getClassCode()); + cargo.setClassName(productClass.getClassName()); + } + } + } + } + + // 添加单位名称(翻译过的单位) + if (StrUtil.isNotBlank(cargo.getUnit())) { + // 尝试不同的字典类型 + String[] dictTypes = {"bm_measuring_unit"}; + String unitName = cargo.getUnit(); + + for (String dictType : dictTypes) { + SysDictData dictData = sysDictDataService.getOne(new LambdaQueryWrapper() + .eq(SysDictData::getDictType, dictType) + .and(wrapper -> wrapper.eq(SysDictData::getDictValue, cargo.getUnit()) + .or().eq(SysDictData::getDictLabel, cargo.getUnit()))); + + if (dictData != null) { + unitName = dictData.getDictLabel(); + break; + } + } + + // 如果仍然是0/斤格式,提取斤部分 + if (unitName != null && unitName.contains("/")) { + String[] parts = unitName.split("/"); + if (parts.length > 1) { + unitName = parts[1]; + } + } + + cargo.setUnitName(unitName); + } } return CommonResult.success(page); @@ -468,9 +558,17 @@ public class CkBillCargoController { if (request.getBeginDateTwo() != null) { queryWrapper.between(CkBillCargo::getExpiryDate, request.getBeginDateTwo(), adjustEndDate(request.getEndDateTwo())); } + + // 出库时间范围查询(通过billDate字段处理) + if (request.getBillDate() != null && request.getBeginDate() == null) { + // 使用billDate作为起始日期查询 + queryWrapper.ge(CkBillCargo::getBillDate, request.getBillDate()); + } // 排序 queryWrapper.orderByDesc(CkBillCargo::getBillDate); + queryWrapper.orderByDesc(CkBillCargo::getBillNumber); + } /** @@ -604,6 +702,35 @@ public class CkBillCargoController { return CommonResult.success(totalWeight); } + /** + * 查询出库合计(数量和金额) + */ + @ApiOperation("查询出库合计") + @GetMapping("/selectTotal") + public CommonResult> selectTotal(CkBillCargo ckBillCargo) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + + // 处理stockCode参数 + handleStockCodeParam(queryWrapper, ckBillCargo); + + buildQueryWrapper(queryWrapper, ckBillCargo); + List list = ckBillCargoService.list(queryWrapper); + + BigDecimal totalWt = list.stream() + .map(CkBillCargo::getCargoWt) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + BigDecimal totalValue = list.stream() + .map(CkBillCargo::getCargoValue) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + Map result = new HashMap<>(); + result.put("totalWt", totalWt); + result.put("totalValue", totalValue); + + return CommonResult.success(result); + } + /** * 撤回出库单据 @@ -627,6 +754,15 @@ public class CkBillCargoController { @ApiOperation("删除出入库单据明细") @DeleteMapping("/{ids}") public CommonResult remove(@PathVariable List ids) { + for (Long id : ids) { + CkBillCargo ckBillCargo = ckBillCargoService.getById(id); + if (ckBillCargo != null && ckBillCargo.getBillId() != null) { + CkBill ckBill = ckBillService.getById(ckBillCargo.getBillId()); + if (ckBill != null && ckBill.getBillStatus().equals("1")) { + return CommonResult.failed("此单据已经提交,不允许删除"); + } + } + } boolean removeByIds = ckBillCargoService.removeByIds(ids); if (removeByIds) { @@ -638,6 +774,904 @@ public class CkBillCargoController { } + /** + * 导出入库数据 + */ + @ApiOperation("导出入库数据") + @RequestMapping(value = "/export-instock", method = RequestMethod.GET) + public void exportInstock(CkBillCargo request, HttpServletResponse response) throws IOException { + // 设置入库类型 + request.setInoutType("1"); + + // 构建查询条件 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + handleStockCodeParam(queryWrapper, request); + buildQueryWrapper(queryWrapper, request); + + // 查询所有数据(不分页) + List list = ckBillCargoService.list(queryWrapper); + + // 查询所有的CkBillStock信息 + List ckBillStockList = ckBillStockService.list(); + + // 构建映射,key为billCargoId,value为对应的CkBillStock + Map ckBillStockMap = new HashMap<>(); + for (CkBillStock ckBillStock : ckBillStockList) { + ckBillStockMap.put(ckBillStock.getBillCargoId(), ckBillStock); + } + + // 获取所有商品对应的单据 ID 列表 + List billIds = new ArrayList<>(); + for (CkBillCargo cargo : list) { + if (cargo.getBillId() != null) { + billIds.add(cargo.getBillId()); + } + } + + // 查询所有对应的 CkBill 信息 + Map ckBillMap = new HashMap<>(); + if (!billIds.isEmpty()) { + List ckBillList = ckBillService.listByIds(billIds); + for (CkBill ckBill : ckBillList) { + ckBillMap.put(ckBill.getId(), ckBill); + } + } + + // 为数据添加关联信息 + for (CkBillCargo cargo : list) { + // 添加仓库信息 + CkBillStock ckBillStock = ckBillStockMap.get(cargo.getId()); + if (ckBillStock != null) { + cargo.setStockName(ckBillStock.getStockName()); + cargo.setOutBillNumber(ckBillStock.getBillNumber()); + } + + // 根据cargoId查询CmCustProduct获取分类信息 + if (cargo.getCargoId() != null) { + CmCustProduct product = cmCustProductService.getById(cargo.getCargoId()); + if (product != null) { + cargo.setClassId(product.getClassId()); + // 根据classId查询物资类别代码 + if (product.getClassId() != null) { + PmProductClass productClass = pmProductClassService.getById(product.getClassId()); + if (productClass != null) { + cargo.setClassCode(productClass.getClassCode()); + cargo.setClassName(productClass.getClassName()); + } + } + } + } + } + + // 查询单位字典数据进行翻译 + Map unitTranslationMap = new HashMap<>(); + List unitDictList = sysDictDataService.list(new LambdaQueryWrapper() + .eq(SysDictData::getDictType, "bm_measuring_unit")); + for (SysDictData dictData : unitDictList) { + unitTranslationMap.put(dictData.getDictValue(), dictData.getDictLabel()); + } + + // 翻译单位 + for (CkBillCargo cargo : list) { + if (StrUtil.isNotBlank(cargo.getUnit())) { + String unitName = unitTranslationMap.get(cargo.getUnit()); + if (unitName != null) { + if (unitName.contains("/")) { + String[] parts = unitName.split("/"); + if (parts.length > 1) { + unitName = parts[1]; + } + } + cargo.setUnit(unitName); + } + } + } + + // 计算合计 + BigDecimal totalWt = list.stream() + .map(c -> c.getCargoWt() != null ? c.getCargoWt() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + BigDecimal totalValue = list.stream() + .map(c -> c.getCargoValue() != null ? c.getCargoValue() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + // 创建工作簿 + Workbook workbook = new XSSFWorkbook(); + Sheet sheet = workbook.createSheet("入库单证"); + + // 创建标题样式(主标题:11号字,宋体,加粗) + CellStyle titleStyle = workbook.createCellStyle(); + titleStyle.setAlignment(HorizontalAlignment.CENTER); + titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = workbook.createFont(); + titleFont.setBold(true); + titleFont.setFontHeightInPoints((short) 11); + titleFont.setFontName("SimSun"); + titleStyle.setFont(titleFont); + + // 创建信息行样式 + CellStyle infoStyle = workbook.createCellStyle(); + infoStyle.setAlignment(HorizontalAlignment.LEFT); + infoStyle.setVerticalAlignment(VerticalAlignment.CENTER); + + // 创建表头样式(表头标题:10号字,宋体,加粗) + CellStyle headerStyle = workbook.createCellStyle(); + headerStyle.setAlignment(HorizontalAlignment.CENTER); + headerStyle.setVerticalAlignment(VerticalAlignment.CENTER); + Font headerFont = workbook.createFont(); + headerFont.setBold(true); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setFontName("SimSun"); + headerStyle.setFont(headerFont); + headerStyle.setBorderBottom(BorderStyle.THIN); + headerStyle.setBorderTop(BorderStyle.THIN); + headerStyle.setBorderLeft(BorderStyle.THIN); + headerStyle.setBorderRight(BorderStyle.THIN); + headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + + // 创建数据样式(数据内容:9号字,宋体,常规) + CellStyle dataStyle = workbook.createCellStyle(); + dataStyle.setAlignment(HorizontalAlignment.CENTER); + dataStyle.setVerticalAlignment(VerticalAlignment.CENTER); + dataStyle.setBorderBottom(BorderStyle.THIN); + dataStyle.setBorderTop(BorderStyle.THIN); + dataStyle.setBorderLeft(BorderStyle.THIN); + dataStyle.setBorderRight(BorderStyle.THIN); + Font dataFont = workbook.createFont(); + dataFont.setFontHeightInPoints((short) 9); + dataFont.setFontName("SimSun"); + dataStyle.setFont(dataFont); + + // 创建合计样式(合计行:9号字,宋体,加粗) + CellStyle totalStyle = workbook.createCellStyle(); + totalStyle.setAlignment(HorizontalAlignment.CENTER); + totalStyle.setVerticalAlignment(VerticalAlignment.CENTER); + Font totalFont = workbook.createFont(); + totalFont.setBold(true); + totalFont.setFontHeightInPoints((short) 9); + totalFont.setFontName("SimSun"); + totalStyle.setFont(totalFont); + totalStyle.setBorderBottom(BorderStyle.THIN); + totalStyle.setBorderTop(BorderStyle.THIN); + totalStyle.setBorderLeft(BorderStyle.THIN); + totalStyle.setBorderRight(BorderStyle.THIN); + + // 创建签字样式(签字栏:20号字,宋体,加粗) + Font signFont = workbook.createFont(); + signFont.setFontName("SimSun"); // 字体类型:宋体 + signFont.setFontHeightInPoints((short) 20); // 字体大小:20磅 + signFont.setBold(true); // 样式:加粗 + signFont.setItalic(false); // 无倾斜 + signFont.setUnderline(Font.U_NONE); // 无下划线 + + CellStyle signStyle = workbook.createCellStyle(); + signStyle.setAlignment(HorizontalAlignment.CENTER); + signStyle.setVerticalAlignment(VerticalAlignment.CENTER); + signStyle.setFont(signFont); + + // 设置列宽(按表头顺序,对应14列) + int[] columnWidths = { + 6 * 256, // 序号 + 12 * 256, // 单据编号 + 12 * 256, // 仓库 + 14 * 256, // 出/入库时间 + 12 * 256, // 物资分类 + 12 * 256, // 物资编号 + 18 * 256, // 物资名称 + 16 * 256, // 规格型号 + 6 * 256, // 单位 + 10 * 256, // 数量 + 10 * 256, // 单价 + 12 * 256, // 金额 + 12 * 256, // 发票号 + 18 * 256 // 供应商 + }; + for (int i = 0; i < columnWidths.length; i++) { + sheet.setColumnWidth(i, columnWidths[i]); + } + + // 第0行:标题"入库单证" + Row titleRow = sheet.createRow(0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellValue("入库单证"); + titleCell.setCellStyle(titleStyle); + sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(0, 0, 0, 13)); + + // 第1行:表头 + Row headerRow = sheet.createRow(1); + headerRow.setHeightInPoints(25); + String[] headers = {"序号", "单据编号", "仓库", "出/入库时间", "物资分类", "物资编号", "物资名称", "规格型号", "单位", "数量", "单价", "金额", "发票号", "供应商"}; + for (int i = 0; i < headers.length; i++) { + Cell cell = headerRow.createCell(i); + cell.setCellValue(headers[i]); + cell.setCellStyle(headerStyle); + } + + // 填充数据(从第2行开始) + int rowNum = 2; + for (CkBillCargo cargo : list) { + Row row = sheet.createRow(rowNum); + row.setHeightInPoints(20); + + // 序号(累计) + Cell cell0 = row.createCell(0); + cell0.setCellValue(rowNum - 1); + cell0.setCellStyle(dataStyle); + + // 单据编号 + Cell cell1 = row.createCell(1); + cell1.setCellValue(cargo.getBillNumber() != null ? cargo.getBillNumber() : ""); + cell1.setCellStyle(dataStyle); + + // 仓库 + Cell cell2 = row.createCell(2); + cell2.setCellValue(cargo.getStockName() != null ? cargo.getStockName() : ""); + cell2.setCellStyle(dataStyle); + + // 出/入库时间 + Cell cell3 = row.createCell(3); + if (cargo.getBillDate() != null) { + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); + cell3.setCellValue(sdf.format(cargo.getBillDate())); + } else { + cell3.setCellValue(""); + } + cell3.setCellStyle(dataStyle); + + // 物资分类(商品类别) + Cell cell4 = row.createCell(4); + cell4.setCellValue(cargo.getClassName() != null ? cargo.getClassName() : ""); + cell4.setCellStyle(dataStyle); + + // 物资编号(商品编码) + Cell cell5 = row.createCell(5); + cell5.setCellValue(cargo.getHsCode() != null ? cargo.getHsCode() : ""); + cell5.setCellStyle(dataStyle); + + // 物资名称 + Cell cell6 = row.createCell(6); + cell6.setCellValue(cargo.getCargoName() != null ? cargo.getCargoName() : ""); + cell6.setCellStyle(dataStyle); + + // 规格型号(去掉批次号,索引提前) + Cell cell7 = row.createCell(7); + cell7.setCellValue(cargo.getCargoSpec() != null ? cargo.getCargoSpec() : ""); + cell7.setCellStyle(dataStyle); + + // 单位(索引提前) + Cell cell8 = row.createCell(8); + cell8.setCellValue(cargo.getUnit() != null ? cargo.getUnit() : ""); + cell8.setCellStyle(dataStyle); + + // 数量(索引提前) + Cell cell9 = row.createCell(9); + cell9.setCellValue(cargo.getCargoWt() != null ? cargo.getCargoWt().doubleValue() : 0); + cell9.setCellStyle(dataStyle); + + // 单价(索引提前) + Cell cell10 = row.createCell(10); + cell10.setCellValue(cargo.getUnitPrice() != null ? cargo.getUnitPrice().doubleValue() : 0); + cell10.setCellStyle(dataStyle); + + // 金额(索引提前) + Cell cell11 = row.createCell(11); + cell11.setCellValue(cargo.getCargoValue() != null ? cargo.getCargoValue().doubleValue() : 0); + cell11.setCellStyle(dataStyle); + + // 发票号(索引提前) + Cell cell12 = row.createCell(12); + cell12.setCellValue(cargo.getBillNo() != null ? cargo.getBillNo() : ""); + cell12.setCellStyle(dataStyle); + + // 供应商(索引提前) + Cell cell13 = row.createCell(13); + cell13.setCellValue(cargo.getCustName() != null ? cargo.getCustName() : ""); + cell13.setCellStyle(dataStyle); + + rowNum++; + } + + // 添加合计行 + Row totalRow = sheet.createRow(rowNum); + totalRow.setHeightInPoints(25); + + // 序号列空 + Cell tCell0 = totalRow.createCell(0); + tCell0.setCellStyle(totalStyle); + + // 单据编号列显示"合计" + Cell tCell1 = totalRow.createCell(1); + tCell1.setCellValue("合计"); + tCell1.setCellStyle(totalStyle); + + // 仓库列空 + Cell tCell2 = totalRow.createCell(2); + tCell2.setCellStyle(totalStyle); + + // 出/入库时间列空 + Cell tCell3 = totalRow.createCell(3); + tCell3.setCellStyle(totalStyle); + + // 物资分类列空 + Cell tCell4 = totalRow.createCell(4); + tCell4.setCellStyle(totalStyle); + + // 物资编号列空 + Cell tCell5 = totalRow.createCell(5); + tCell5.setCellStyle(totalStyle); + + // 物资名称列空 + Cell tCell6 = totalRow.createCell(6); + tCell6.setCellStyle(totalStyle); + + // 规格型号列空(去掉批次号,索引提前) + Cell tCell7 = totalRow.createCell(7); + tCell7.setCellStyle(totalStyle); + + // 单位列空(索引提前) + Cell tCell8 = totalRow.createCell(8); + tCell8.setCellStyle(totalStyle); + + // 数量合计(索引提前) + Cell tCell9 = totalRow.createCell(9); + tCell9.setCellValue(totalWt.doubleValue()); + tCell9.setCellStyle(totalStyle); + + // 单价列空(索引提前) + Cell tCell10 = totalRow.createCell(10); + tCell10.setCellStyle(totalStyle); + + // 金额合计(索引提前) + Cell tCell11 = totalRow.createCell(11); + tCell11.setCellValue(totalValue.doubleValue()); + tCell11.setCellStyle(totalStyle); + + // 发票号列空(索引提前) + Cell tCell12 = totalRow.createCell(12); + tCell12.setCellStyle(totalStyle); + + // 供应商列空(索引提前) + Cell tCell13 = totalRow.createCell(13); + tCell13.setCellStyle(totalStyle); + + // 移动到下一行 + rowNum++; + + // 添加签字行(部门主管、采购员、仓管员) + Row signRow = sheet.createRow(rowNum); + signRow.setHeightInPoints(30); + + Cell signCell1 = signRow.createCell(1); // B列 + signCell1.setCellValue("部门主管:"); + signCell1.setCellStyle(signStyle); + + Cell signCell2 = signRow.createCell(6); // G列 + signCell2.setCellValue("采购员:"); + signCell2.setCellStyle(signStyle); + + Cell signCell3 = signRow.createCell(12); // M列 + signCell3.setCellValue("仓管员:"); + signCell3.setCellStyle(signStyle); + + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("UTF-8"); + String fileName = URLEncoder.encode("仓库入库管理_" + System.currentTimeMillis(), "UTF-8"); + response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx"); + + // 输出Excel + OutputStream outputStream = response.getOutputStream(); + workbook.write(outputStream); + outputStream.flush(); + outputStream.close(); + workbook.close(); + } + + /** + * 导出出库数据 + */ + @ApiOperation("导出出库数据") + @RequestMapping(value = "/export", method = RequestMethod.GET) + public void export(CkBillCargo request, HttpServletResponse response) throws IOException { + // 构建查询条件 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + handleStockCodeParam(queryWrapper, request); + buildQueryWrapper(queryWrapper, request); + + // 查询所有数据(不分页) + List list = ckBillCargoService.list(queryWrapper); + + // 查询所有的CkBillStock信息 + List ckBillStockList = ckBillStockService.list(); + + // 构建映射,key为billCargoId,value为对应的CkBillStock + Map ckBillStockMap = new HashMap<>(); + for (CkBillStock ckBillStock : ckBillStockList) { + ckBillStockMap.put(ckBillStock.getBillCargoId(), ckBillStock); + } + + // 获取所有商品对应的单据 ID 列表 + List billIds = new ArrayList<>(); + for (CkBillCargo cargo : list) { + if (cargo.getBillId() != null) { + billIds.add(cargo.getBillId()); + } + } + + // 查询所有对应的 CkBill 信息 + Map ckBillMap = new HashMap<>(); + List deptIds = new ArrayList<>(); + List adminIds = new ArrayList<>(); + if (!billIds.isEmpty()) { + List ckBillList = ckBillService.listByIds(billIds); + for (CkBill ckBill : ckBillList) { + ckBillMap.put(ckBill.getId(), ckBill); + // 收集部门ID和用户ID + if (ckBill.getReceiverDeptId() != null) { + deptIds.add(ckBill.getReceiverDeptId()); + } + if (ckBill.getReceiverId() != null) { + adminIds.add(ckBill.getReceiverId()); + } + } + } + + // 查询部门信息 + Map deptNameMap = new HashMap<>(); + if (!deptIds.isEmpty()) { + List deptList = systemDeptService.listByIds(deptIds); + for (SysDept dept : deptList) { + deptNameMap.put(dept.getDeptId(), dept.getDeptName()); + } + } + + // 查询用户信息 + Map adminNameMap = new HashMap<>(); + if (!adminIds.isEmpty()) { + List adminList = systemAdminService.listByIds(adminIds); + for (SystemAdmin admin : adminList) { + adminNameMap.put(Long.valueOf(admin.getId()), admin.getRealName()); + } + } + + // 为数据添加关联信息 + for (CkBillCargo cargo : list) { + // 添加仓库信息 + CkBillStock ckBillStock = ckBillStockMap.get(cargo.getId()); + if (ckBillStock != null) { + cargo.setStockName(ckBillStock.getStockName()); + cargo.setOutBillNumber(ckBillStock.getBillNumber()); + } + + // 添加领用部门和领用人信息 + if (cargo.getBillId() != null) { + CkBill ckBill = ckBillMap.get(cargo.getBillId()); + if (ckBill != null) { + if (ckBill.getReceiverDeptId() != null) { + cargo.setReceiveDeptName(deptNameMap.getOrDefault(ckBill.getReceiverDeptId(), ckBill.getReceiverDeptId().toString())); + } + if (ckBill.getReceiverId() != null) { + cargo.setReceiverName(adminNameMap.getOrDefault(ckBill.getReceiverId(), ckBill.getReceiverId().toString())); + } + } + } + + // 根据cargoId查询CmCustProduct获取分类信息 + if (cargo.getCargoId() != null) { + CmCustProduct product = cmCustProductService.getById(cargo.getCargoId()); + if (product != null) { + cargo.setClassId(product.getClassId()); + // 根据classId查询物资类别代码 + if (product.getClassId() != null) { + PmProductClass productClass = pmProductClassService.getById(product.getClassId()); + if (productClass != null) { + cargo.setClassCode(productClass.getClassCode()); + cargo.setClassName(productClass.getClassName()); + } + } + } + } + } + + // 查询单位字典数据进行翻译 + Map unitTranslationMap = new HashMap<>(); + List unitDictList = sysDictDataService.list(new LambdaQueryWrapper() + .eq(SysDictData::getDictType, "bm_measuring_unit")); + for (SysDictData dictData : unitDictList) { + unitTranslationMap.put(dictData.getDictValue(), dictData.getDictLabel()); + } + + // 翻译单位 + for (CkBillCargo cargo : list) { + if (StrUtil.isNotBlank(cargo.getUnit())) { + String unitName = unitTranslationMap.get(cargo.getUnit()); + if (unitName != null) { + // 如果是 0/斤 格式,提取斤部分 + if (unitName.contains("/")) { + String[] parts = unitName.split("/"); + if (parts.length > 1) { + unitName = parts[1]; + } + } + cargo.setUnit(unitName); + } + } + } + + // 获取类别名称 + String className = "所有类别"; + if (StrUtil.isNotBlank(request.getClassIds())) { + String[] classIdArray = request.getClassIds().split(","); + List classNames = new ArrayList<>(); + for (String classIdStr : classIdArray) { + try { + Long classId = Long.parseLong(classIdStr.trim()); + PmProductClass productClass = pmProductClassService.getById(classId); + if (productClass != null && StrUtil.isNotBlank(productClass.getClassName())) { + classNames.add(productClass.getClassName()); + } + } catch (NumberFormatException e) { + // 忽略格式错误的ID + } + } + if (!classNames.isEmpty()) { + className = String.join(",", classNames); + } + } + + // 获取供应商名称 + String custName = "所有"; + if (request.getCustId() != null) { + CmCust cust = cmCustService.getById(request.getCustId()); + if (cust != null && StrUtil.isNotBlank(cust.getCustName())) { + custName = cust.getCustName(); + } + } + + // 获取日期范围 + String dateRange = ""; + if (request.getBeginDate() != null && request.getEndDate() != null) { + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); + dateRange = sdf.format(request.getBeginDate()) + " 至 " + sdf.format(request.getEndDate()); + } else if (request.getBeginDate() != null) { + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); + dateRange = sdf.format(request.getBeginDate()) + " 起"; + } else if (request.getEndDate() != null) { + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); + dateRange = " 至 " + sdf.format(request.getEndDate()); + } + + // 计算合计 + BigDecimal totalWt = list.stream() + .map(c -> c.getCargoWt() != null ? c.getCargoWt() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + BigDecimal totalValue = list.stream() + .map(c -> c.getCargoValue() != null ? c.getCargoValue() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + // 创建工作簿 + Workbook workbook = new XSSFWorkbook(); + Sheet sheet = workbook.createSheet("出库单证"); + + // 创建标题样式(主标题:11号字,宋体,加粗) + CellStyle titleStyle = workbook.createCellStyle(); + titleStyle.setAlignment(HorizontalAlignment.CENTER); + titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = workbook.createFont(); + titleFont.setBold(true); + titleFont.setFontHeightInPoints((short) 11); + titleFont.setFontName("SimSun"); + titleStyle.setFont(titleFont); + + // 创建信息行样式 + CellStyle infoStyle = workbook.createCellStyle(); + infoStyle.setAlignment(HorizontalAlignment.LEFT); + infoStyle.setVerticalAlignment(VerticalAlignment.CENTER); + + // 创建表头样式(表头标题:10号字,宋体,加粗) + CellStyle headerStyle = workbook.createCellStyle(); + headerStyle.setAlignment(HorizontalAlignment.CENTER); + headerStyle.setVerticalAlignment(VerticalAlignment.CENTER); + Font headerFont = workbook.createFont(); + headerFont.setBold(true); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setFontName("SimSun"); + headerStyle.setFont(headerFont); + headerStyle.setBorderBottom(BorderStyle.THIN); + headerStyle.setBorderTop(BorderStyle.THIN); + headerStyle.setBorderLeft(BorderStyle.THIN); + headerStyle.setBorderRight(BorderStyle.THIN); + headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + + // 创建数据样式(数据内容:9号字,宋体,常规) + CellStyle dataStyle = workbook.createCellStyle(); + dataStyle.setAlignment(HorizontalAlignment.CENTER); + dataStyle.setVerticalAlignment(VerticalAlignment.CENTER); + dataStyle.setBorderBottom(BorderStyle.THIN); + dataStyle.setBorderTop(BorderStyle.THIN); + dataStyle.setBorderLeft(BorderStyle.THIN); + dataStyle.setBorderRight(BorderStyle.THIN); + Font dataFont = workbook.createFont(); + dataFont.setFontHeightInPoints((short) 9); + dataFont.setFontName("SimSun"); + dataStyle.setFont(dataFont); + + // 创建合计样式(合计行:9号字,宋体,加粗) + CellStyle totalStyle = workbook.createCellStyle(); + totalStyle.setAlignment(HorizontalAlignment.CENTER); + totalStyle.setVerticalAlignment(VerticalAlignment.CENTER); + Font totalFont = workbook.createFont(); + totalFont.setBold(true); + totalFont.setFontHeightInPoints((short) 9); + totalFont.setFontName("SimSun"); + totalStyle.setFont(totalFont); + totalStyle.setBorderBottom(BorderStyle.THIN); + totalStyle.setBorderTop(BorderStyle.THIN); + totalStyle.setBorderLeft(BorderStyle.THIN); + totalStyle.setBorderRight(BorderStyle.THIN); + + // 创建签字样式(签字栏:20号字,宋体,加粗) + Font signFont = workbook.createFont(); + signFont.setFontName("SimSun"); // 字体类型:宋体 + signFont.setFontHeightInPoints((short) 20); // 字体大小:20磅 + signFont.setBold(true); // 样式:加粗 + signFont.setItalic(false); // 无倾斜 + signFont.setUnderline(Font.U_NONE); // 无下划线 + + CellStyle signStyle = workbook.createCellStyle(); + signStyle.setAlignment(HorizontalAlignment.CENTER); + signStyle.setVerticalAlignment(VerticalAlignment.CENTER); + signStyle.setFont(signFont); + + // 设置列宽(按表头顺序,对应15列) + int[] columnWidths = { + 6 * 256, // 序号 + 12 * 256, // 单据编号 + 12 * 256, // 仓库 + 14 * 256, // 出/入库时间 + 12 * 256, // 物资分类 + 12 * 256, // 物资编号 + 18 * 256, // 物资名称 + 12 * 256, // 批次号 + 16 * 256, // 规格型号 + 6 * 256, // 单位 + 10 * 256, // 数量 + 10 * 256, // 单价 + 12 * 256, // 金额 + 14 * 256, // 领用部门 + 10 * 256 // 领用人 + }; + for (int i = 0; i < columnWidths.length; i++) { + sheet.setColumnWidth(i, columnWidths[i]); + } + + // 第0行:标题"出库单证" + Row titleRow = sheet.createRow(0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellValue("出库单证"); + titleCell.setCellStyle(titleStyle); + sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(0, 0, 0, 14)); + + // 第1行:信息行(类别、供应商、日期) + Row infoRow1 = sheet.createRow(1); + infoRow1.setHeightInPoints(25); + Cell infoCell1 = infoRow1.createCell(0); + infoCell1.setCellValue("类别:" + className); + infoCell1.setCellStyle(infoStyle); + + Cell infoCell2 = infoRow1.createCell(5); + infoCell2.setCellValue("供应商:" + custName); + infoCell2.setCellStyle(infoStyle); + + Cell infoCell3 = infoRow1.createCell(12); + infoCell3.setCellValue("日期:" + dateRange); + infoCell3.setCellStyle(infoStyle); + + // 第2行:表头 + Row headerRow = sheet.createRow(2); + headerRow.setHeightInPoints(25); + String[] headers = {"序号", "单据编号", "仓库", "出/入库时间", "物资分类", "物资编号", "物资名称", "批次号", "规格型号", "单位", "数量", "单价", "金额", "领用部门", "领用人"}; + for (int i = 0; i < headers.length; i++) { + Cell cell = headerRow.createCell(i); + cell.setCellValue(headers[i]); + cell.setCellStyle(headerStyle); + } + + // 填充数据(从第3行开始) + int rowNum = 3; + for (CkBillCargo cargo : list) { + Row row = sheet.createRow(rowNum); + row.setHeightInPoints(20); + + // 序号(累计) + Cell cell0 = row.createCell(0); + cell0.setCellValue(rowNum - 2); + cell0.setCellStyle(dataStyle); + + // 单据编号 + Cell cell1 = row.createCell(1); + cell1.setCellValue(cargo.getBillNumber() != null ? cargo.getBillNumber() : ""); + cell1.setCellStyle(dataStyle); + + // 仓库 + Cell cell2 = row.createCell(2); + cell2.setCellValue(cargo.getStockName() != null ? cargo.getStockName() : ""); + cell2.setCellStyle(dataStyle); + + // 出/入库时间 + Cell cell3 = row.createCell(3); + if (cargo.getBillDate() != null) { + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); + cell3.setCellValue(sdf.format(cargo.getBillDate())); + } else { + cell3.setCellValue(""); + } + cell3.setCellStyle(dataStyle); + + // 物资分类(商品类别) + Cell cell4 = row.createCell(4); + cell4.setCellValue(cargo.getClassName() != null ? cargo.getClassName() : ""); + cell4.setCellStyle(dataStyle); + + // 物资编号(商品编码) + Cell cell5 = row.createCell(5); + cell5.setCellValue(cargo.getHsCode() != null ? cargo.getHsCode() : ""); + cell5.setCellStyle(dataStyle); + + // 物资名称 + Cell cell6 = row.createCell(6); + cell6.setCellValue(cargo.getCargoName() != null ? cargo.getCargoName() : ""); + cell6.setCellStyle(dataStyle); + + // 批次号 + Cell cell7 = row.createCell(7); + cell7.setCellValue(cargo.getOutBillNumber() != null ? cargo.getOutBillNumber() : ""); + cell7.setCellStyle(dataStyle); + + // 规格型号 + Cell cell8 = row.createCell(8); + cell8.setCellValue(cargo.getCargoSpec() != null ? cargo.getCargoSpec() : ""); + cell8.setCellStyle(dataStyle); + + // 单位 + Cell cell9 = row.createCell(9); + cell9.setCellValue(cargo.getUnit() != null ? cargo.getUnit() : ""); + cell9.setCellStyle(dataStyle); + + // 数量 + Cell cell10 = row.createCell(10); + cell10.setCellValue(cargo.getCargoWt() != null ? cargo.getCargoWt().doubleValue() : 0); + cell10.setCellStyle(dataStyle); + + // 单价 + Cell cell11 = row.createCell(11); + cell11.setCellValue(cargo.getUnitPrice() != null ? cargo.getUnitPrice().doubleValue() : 0); + cell11.setCellStyle(dataStyle); + + // 金额 + Cell cell12 = row.createCell(12); + cell12.setCellValue(cargo.getCargoValue() != null ? cargo.getCargoValue().doubleValue() : 0); + cell12.setCellStyle(dataStyle); + + // 领用部门 + Cell cell13 = row.createCell(13); + cell13.setCellValue(cargo.getReceiveDeptName() != null ? cargo.getReceiveDeptName() : ""); + cell13.setCellStyle(dataStyle); + + // 领用人 + Cell cell14 = row.createCell(14); + cell14.setCellValue(cargo.getReceiverName() != null ? cargo.getReceiverName() : ""); + cell14.setCellStyle(dataStyle); + + rowNum++; + } + + // 添加合计行 + Row totalRow = sheet.createRow(rowNum); + totalRow.setHeightInPoints(25); + + // 序号列空 + Cell tCell0 = totalRow.createCell(0); + tCell0.setCellStyle(totalStyle); + + // 单据编号列显示"合计" + Cell tCell1 = totalRow.createCell(1); + tCell1.setCellValue("合计"); + tCell1.setCellStyle(totalStyle); + + // 仓库列空 + Cell tCell2 = totalRow.createCell(2); + tCell2.setCellStyle(totalStyle); + + // 出/入库时间列空 + Cell tCell3 = totalRow.createCell(3); + tCell3.setCellStyle(totalStyle); + + // 物资分类列空 + Cell tCell4 = totalRow.createCell(4); + tCell4.setCellStyle(totalStyle); + + // 物资编号列空 + Cell tCell5 = totalRow.createCell(5); + tCell5.setCellStyle(totalStyle); + + // 物资名称列空 + Cell tCell6 = totalRow.createCell(6); + tCell6.setCellStyle(totalStyle); + + // 批次号列空 + Cell tCell7 = totalRow.createCell(7); + tCell7.setCellStyle(totalStyle); + + // 规格型号列空 + Cell tCell8 = totalRow.createCell(8); + tCell8.setCellStyle(totalStyle); + + // 单位列空 + Cell tCell9 = totalRow.createCell(9); + tCell9.setCellStyle(totalStyle); + + // 数量合计 + Cell tCell10 = totalRow.createCell(10); + tCell10.setCellValue(totalWt.doubleValue()); + tCell10.setCellStyle(totalStyle); + + // 单价列空 + Cell tCell11 = totalRow.createCell(11); + tCell11.setCellStyle(totalStyle); + + // 金额合计 + Cell tCell12 = totalRow.createCell(12); + tCell12.setCellValue(totalValue.doubleValue()); + tCell12.setCellStyle(totalStyle); + + // 领用部门列空 + Cell tCell13 = totalRow.createCell(13); + tCell13.setCellStyle(totalStyle); + + // 领用人列空 + Cell tCell14 = totalRow.createCell(14); + tCell14.setCellStyle(totalStyle); + + // 移动到下一行 + rowNum++; + + // 添加签字行(部门主管、采购员、仓管员) + Row signRow = sheet.createRow(rowNum); + signRow.setHeightInPoints(30); + + Cell signCell1 = signRow.createCell(1); // B列 + signCell1.setCellValue("部门主管:"); + signCell1.setCellStyle(signStyle); + + Cell signCell2 = signRow.createCell(6); // G列 + signCell2.setCellValue("采购员:"); + signCell2.setCellStyle(signStyle); + + Cell signCell3 = signRow.createCell(12); // M列 + signCell3.setCellValue("仓管员:"); + signCell3.setCellStyle(signStyle); + + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("UTF-8"); + String fileName = URLEncoder.encode("仓库出库管理_" + System.currentTimeMillis(), "UTF-8"); + response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx"); + + // 输出Excel + OutputStream outputStream = response.getOutputStream(); + workbook.write(outputStream); + outputStream.flush(); + outputStream.close(); + workbook.close(); + } + } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkBillController.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkBillController.java index 1c8daa5..2151b20 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkBillController.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkBillController.java @@ -1,9 +1,19 @@ package com.zbkj.modules.autogencode.controller; +import java.io.InputStream; import java.math.BigDecimal; import java.util.*; import java.io.IOException; import javax.servlet.http.HttpServletResponse; + +import com.zbkj.common.model.system.SysDept; +import com.zbkj.common.model.system.SystemAdmin; +import com.zbkj.common.model.user.User; +import com.zbkj.modules.autogencode.entity.vo.ImportItemVO; +import com.zbkj.modules.autogencode.service.*; +import com.zbkj.service.service.SysDeptService; +import com.zbkj.service.service.SystemAdminService; +import com.zbkj.service.service.UserService; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; @@ -22,18 +32,6 @@ import com.zbkj.modules.autogencode.entity.CkStockChange; import com.zbkj.modules.autogencode.entity.CmCustProduct; import com.zbkj.modules.autogencode.entity.CmWarehouse; import com.zbkj.modules.autogencode.entity.CmCust; -import com.zbkj.modules.autogencode.service.CkBillCargoService; -import com.zbkj.modules.autogencode.service.CkBillStockService; -import com.zbkj.modules.autogencode.service.CkCargoStockService; -import com.zbkj.modules.autogencode.service.CkBillCargoMenuRelService; -import com.zbkj.modules.autogencode.service.CkStockChangeService; -import com.zbkj.modules.autogencode.service.CmCustProductService; -import com.zbkj.modules.autogencode.service.CkBillNumberService; -import com.zbkj.modules.autogencode.service.CmWarehouseService; -import com.zbkj.modules.autogencode.service.CmCustService; -import com.zbkj.modules.autogencode.service.PmDailyMenuDtlService; -import com.zbkj.modules.autogencode.service.PmDailyMenuService; -import com.zbkj.modules.autogencode.service.PmProductClassService; import com.zbkj.service.service.SystemAttachmentService; import com.zbkj.common.vo.CkBillExcelVo; import com.zbkj.common.utils.ExportUtil; @@ -56,7 +54,6 @@ import org.slf4j.LoggerFactory; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.zbkj.modules.autogencode.service.CkBillService; /** @@ -109,6 +106,18 @@ public class CkBillController { @Autowired private CkBillNumberService ckBillNumberService; + @Autowired + private SysDeptService sysDeptService; + + @Autowired + private SystemAdminService sysAdminService; + + @Autowired + private UserService userService; + + @Autowired + private SysDictDataService sysDictDataService; + /** * 条件设置 */ @@ -253,6 +262,7 @@ public class CkBillController { public CommonResult> stockPageList(@Validated CkBill ckBill, @Validated PageParamRequest pageParamRequest) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); condition(queryWrapper, ckBill); + queryWrapper.orderByDesc(CkBill::getBillDate); //单据类型 if(Validator.isNotEmpty(ckBill.getBillType())){ if (ckBill.getBillType().equals("1")) { @@ -654,6 +664,32 @@ public class CkBillController { // 设置备注为用途 ckBill.setRemark(request.getPurpose()); + + + + + // 修改一下,直接获取部门和人员中被设置成领用的状态的人,与部门 + if (request.getUid() != null) { + // 先查询user 其中有对应的adminId + User user = userService.getOne(new LambdaQueryWrapper() + .eq(User::getUid, request.getUid())); + if (user != null && user.getAdminid() != null) { + // 根据adminId查询对应的SystemAdmin + SystemAdmin admin = sysAdminService.getOne(new LambdaQueryWrapper() + .eq(SystemAdmin::getId, user.getAdminid())); + if (admin != null) { + // 设置领用人ID + ckBill.setReceiverId(Long.valueOf(admin.getId())); + // 设置领用部门ID + ckBill.setReceiverDeptId(user.getDeptId()); + } + } + } + + // 查询默认仓库 + CmWarehouse defaultWarehouse = cmWarehouseService.getOne(new LambdaQueryWrapper() + .eq(CmWarehouse::getDelFlag, "0") + .last("LIMIT 1")); // 构建商品列表 List ckBillCargos = new ArrayList<>(); @@ -665,6 +701,26 @@ public class CkBillController { cargo.setBillStatus("0"); cargo.setInoutType("2"); // 2表示出库 + // 根据入库单据ID查询CkCargoStock + if (item.getCkCargoStockId() != null) { + CkCargoStock ckCargoStock = ckCargoStockService.getById(item.getCkCargoStockId()); + if (ckCargoStock != null) { + // 填写对应的内容 + cargo.setOutStockId(ckCargoStock.getStockId()); + cargo.setOutBillNumber(ckCargoStock.getBillNumber()); + cargo.setHsCode(ckCargoStock.getHsCode()); + cargo.setCargoSpec(ckCargoStock.getCargoSpec()); + cargo.setOpType(ckCargoStock.getOpType()); + cargo.setTradType(ckCargoStock.getTradType()); + cargo.setOriginCountry(ckCargoStock.getOriginCountry()); + } + } else { + // 设置默认出库库场 + if (defaultWarehouse != null) { + cargo.setOutStockId(defaultWarehouse.getId()); + } + } + // 根据商品ID查询对应的供应商ID CmCustProduct cmCustProduct = cmCustProductService.getOne(new LambdaQueryWrapper() .eq(CmCustProduct::getId, item.getCargoId()) @@ -679,6 +735,8 @@ public class CkBillController { } cargo.setCargoNum(item.getQuantity()); // 商品件数设为与数量相同 + cargo.setCargoWt(item.getQuantity()); // 商品重量设为与数量相同 + cargo.setCargoVol(BigDecimal.ZERO); // 设置默认体积为0 ckBillCargos.add(cargo); } @@ -821,9 +879,6 @@ public class CkBillController { List ckBillCargos = ckBill.getCkBillCargos(); for (int i = 0; i < ckBillCargos.size(); i++) { CkBillCargo ckBillCargo = ckBillCargos.get(i); - if (ckBillCargo.getCustId() == null) { - return CommonResult.failed("货主不能为空"); - } if (ckBillCargo.getCargoNum() == null) { return CommonResult.failed("商品件数不能为空"); } @@ -833,7 +888,8 @@ public class CkBillController { if (ckBillCargo.getCargoVol() == null) { return CommonResult.failed("商品体积不能为空"); } - if (ckBillCargo.getOutStockId() == null) { + // 直接领用类型的出库单不需要验证出库库场 + if (!"1".equals(ckBill.getSourceType()) && ckBillCargo.getOutStockId() == null) { return CommonResult.failed("商品的出库库场不能为空"); } } @@ -844,8 +900,8 @@ public class CkBillController { * 下载入库模板 */ @ApiOperation("下载入库模板") - @GetMapping("/downloadTemplate") - public void downloadTemplate(HttpServletResponse response) throws IOException { + @GetMapping("/downloadInTemplate") + public void downloadInTemplate(HttpServletResponse response) throws IOException { // 创建工作簿 Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("入库模板"); @@ -893,14 +949,78 @@ public class CkBillController { workbook.write(response.getOutputStream()); workbook.close(); } + + /** + * 下载出库模板 + */ + @ApiOperation("下载出库模板") + @GetMapping("/downloadOutTemplate") + public void downloadOutTemplate(HttpServletResponse response) throws IOException { + // 创建工作簿 + Workbook workbook = new XSSFWorkbook(); + Sheet sheet = workbook.createSheet("出库模板"); + + // 创建表头样式 + CellStyle headerStyle = workbook.createCellStyle(); + // 水平居中 + headerStyle.setAlignment(HorizontalAlignment.CENTER); + // 垂直居中 + headerStyle.setVerticalAlignment(VerticalAlignment.CENTER); + // 设置边框 + headerStyle.setBorderTop(BorderStyle.THIN); + headerStyle.setBorderBottom(BorderStyle.THIN); + headerStyle.setBorderLeft(BorderStyle.THIN); + headerStyle.setBorderRight(BorderStyle.THIN); + // 设置字体 + Font headerFont = workbook.createFont(); + headerFont.setFontName("微软雅黑"); + headerFont.setFontHeightInPoints((short) 11); + headerFont.setBold(true); + headerStyle.setFont(headerFont); + + // 创建表头 + String[] headers = {"单据编号", "仓库", "出库时间", "商品分类", "商品编号", "商品名称", "批次号", "规格型号", "单位", "数量", "单价", "金额", "领用部门", "领用人"}; + Row headerRow = sheet.createRow(0); + headerRow.setHeightInPoints(20); // 设置行高 + for (int i = 0; i < headers.length; i++) { + Cell cell = headerRow.createCell(i); + cell.setCellValue(headers[i]); + cell.setCellStyle(headerStyle); + } + + // 设置列宽 + for (int i = 0; i < headers.length; i++) { + sheet.setColumnWidth(i, 20 * 256); + } + + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + String encodedFileName = java.net.URLEncoder.encode("出库导入模板.xlsx", "UTF-8").replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + encodedFileName); + + // 写入响应流 + workbook.write(response.getOutputStream()); + workbook.close(); + } + + /** + * 下载模板(兼容旧接口) + */ + @ApiOperation("下载模板") + @GetMapping("/downloadTemplate") + public void downloadTemplate(HttpServletResponse response) throws IOException { + // 默认下载入库模板 + downloadInTemplate(response); + } /** * 导入入库数据 */ @ApiOperation("导入入库数据") - @PostMapping("/importData") + @PostMapping("/importInData") @Transactional(rollbackFor = Exception.class) - public CommonResult importData(@RequestParam("file") MultipartFile file, @RequestParam(value = "updateSupport", defaultValue = "0") Integer updateSupport) throws Exception { + public CommonResult importInData(@RequestParam("file") MultipartFile file, @RequestParam(value = "updateSupport", defaultValue = "0") Integer updateSupport) throws Exception { if (file.isEmpty()) { return CommonResult.failed("文件不能为空"); } @@ -918,6 +1038,10 @@ public class CkBillController { int successCount = 0; int failureCount = 0; StringBuilder failureMsg = new StringBuilder(); + // 用于存储自动生成的单据编号,确保一次导入中所有无编号的行使用同一个编号 + String autoBillNumber = null; + // 用于存储导入过程中创建的单据ID,以便前端确认 + Set billIds = new HashSet<>(); for (int i = 1; i < rowCount; i++) { Row row = sheet.getRow(i); @@ -935,6 +1059,19 @@ public class CkBillController { String cargoName = getCellValue(row.getCell(5)); String cargoSpec = getCellValue(row.getCell(6)); String unit = getCellValue(row.getCell(7)); + + // 通过bm_measuring_unit字典翻译单位名称为编码 + if (StringUtils.isNotBlank(unit)) { + SysDictData dictData = sysDictDataService.getOne(new LambdaQueryWrapper() + .eq(SysDictData::getDictType, "bm_measuring_unit") + .eq(SysDictData::getDictLabel, unit)); + if (dictData != null) { + unit = dictData.getDictValue(); + } else { + log.warn("未找到单位[{}]对应的字典翻译", unit); + } + } + String cargoWtStr = getCellValue(row.getCell(8)); String unitPriceStr = getCellValue(row.getCell(9)); String cargoValueStr = getCellValue(row.getCell(10)); @@ -953,9 +1090,12 @@ public class CkBillController { } } - // 处理单据编号,如果没有,就自动生成 + // 处理单据编号,如果没有,就自动生成,确保一次导入中所有无编号的行使用同一个编号 if (StringUtils.isBlank(billNumber)) { - billNumber = ckBillNumberService.getBillNumber("RK"); + if (autoBillNumber == null) { + autoBillNumber = ckBillNumberService.getBillNumber("RK"); + } + billNumber = autoBillNumber; } // 验证必填字段 @@ -981,9 +1121,21 @@ public class CkBillController { // 查找供应商 CmCust cmCust = cmCustService.getOne(new LambdaQueryWrapper().eq(CmCust::getCustName, custName)); if (cmCust == null) { - failureCount++; - failureMsg.append("第").append(i + 1).append("行:供应商").append(custName).append("不存在\n"); - continue; + // 创建新供应商 + cmCust = new CmCust(); + cmCust.setCustName(custName); + // 生成custCode:名称的首字母大写 + StringBuilder custCodeBuilder = new StringBuilder(); + String[] nameParts = custName.split("\\s+"); + for (String part : nameParts) { + if (!part.isEmpty()) { + custCodeBuilder.append(Character.toUpperCase(part.charAt(0))); + } + } + cmCust.setCustCode(custCodeBuilder.toString()); + cmCust.setStatus("1"); + cmCust.setDelFlag("0"); + cmCustService.save(cmCust); } // 查找商品 @@ -1001,12 +1153,18 @@ public class CkBillController { product.setUnit(unit); product.setCustId(cmCust.getId()); product.setStatus("1"); + + // 处理商品分类 + if (StringUtils.isNotBlank(cargoCategory)) { + // 查找商品分类 + List pmProductClasses = pmProductClassService.list(new LambdaQueryWrapper() + .eq(PmProductClass::getClassName, cargoCategory)); + if (null != pmProductClasses && pmProductClasses.size() > 0) { + product.setClassId(pmProductClasses.get(0).getId()); + } + } + cmCustProductService.save(product); - } else if (updateSupport == 1) { - // 更新商品信息 - product.setUnit(unit); - product.setCustId(cmCust.getId()); - cmCustProductService.updateById(product); } // 判断是否自动出库 @@ -1036,7 +1194,33 @@ public class CkBillController { bill.setAutoOut(autoOut); bill.setCustId(cmCust.getId()); bill.setCustName(cmCust.getCustName()); + + // 获取被设置为领用状态的部门 + SysDept receiveDept = sysDeptService.getOne(new LambdaQueryWrapper() + .eq(SysDept::getIsReceiveDept, "1") + .eq(SysDept::getDelFlag, "0") + .eq(SysDept::getStatus, "0") + .last("LIMIT 1")); + if (receiveDept != null) { + bill.setReceiverDeptId(receiveDept.getDeptId()); + } + + // 获取被设置为领用状态的人员 + SystemAdmin receiver = sysAdminService.getOne(new LambdaQueryWrapper() + .eq(SystemAdmin::getIsDefaultReceiver, "1") + .eq(SystemAdmin::getIsDel, false) + .eq(SystemAdmin::getStatus, true) + .last("LIMIT 1")); + if (receiver != null) { + bill.setReceiverId(Long.valueOf(receiver.getId())); + } + ckBillService.save(bill); + // 将创建的单据ID添加到集合中,以便后续自动确认 + billIds.add(bill.getId()); + } else { + // 如果是已存在的单据,也添加到集合中,以便后续自动确认 + billIds.add(bill.getId()); } // 检查是否已存在相同商品的明细 @@ -1123,38 +1307,982 @@ public class CkBillController { String msg = "导入成功" + successCount + "条,失败" + failureCount + "条:" + failureMsg.toString(); return CommonResult.failed(msg); } else { - return CommonResult.success("导入成功" + successCount + "条"); + // 返回导入成功的消息和单据ID列表,以便前端显示确认弹窗 + Map result = new HashMap<>(); + result.put("message", "导入成功" + successCount + "条"); + result.put("billIds", billIds); + return CommonResult.success(result); + } + } + + + +// @ApiOperation("导入入库数据") +// @PostMapping("/importInData") +// @Transactional(rollbackFor = Exception.class) +// public CommonResult importInData( +// @RequestParam("file") MultipartFile file, +// @RequestParam(value = "updateSupport", defaultValue = "0") Integer updateSupport +// ) throws Exception { +// +// if (file.isEmpty()) { +// return CommonResult.failed("文件不能为空"); +// } +// +// // ====================== 1. 读取并解析整个Excel(完全适配当前模板) ====================== +// List dataList = new ArrayList<>(); +// try (InputStream in = file.getInputStream(); +// Workbook workbook = WorkbookFactory.create(in)) { +// +// Sheet sheet = workbook.getSheetAt(0); +// if (sheet == null) { +// return CommonResult.failed("Excel无数据"); +// } +// +// // 全局变量(整表共用) +// String orderNo = null; +// String orderDate = null; +// String customer = null; +// String address = null; +// +// // 列索引映射,通过表头内容动态确定 +// Map columnMap = new HashMap<>(); +// boolean isProductRowStart = false; // 标记是否进入商品行区域 +// +// // 逐行读取 +// for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) { +// Row row = sheet.getRow(rowNum); +// if (row == null) { +// continue; // 跳过空行 +// } +// +// // 尝试获取A列的值,用于判断是否是表头行 +// String cellA = ""; +// try { +// cellA = getCellValue(row.getCell(0)).trim(); +// } catch (Exception e) { +// // 忽略异常,继续处理 +// } +// +// // ====================== 提取表头信息 ====================== +// // 单据编号 +// if (cellA.contains("单据编号")) { +// try { +// // 尝试从B列获取值 +// orderNo = getCellValue(row.getCell(1)).trim(); +// } catch (Exception e) { +// // 尝试从其他列获取 +// for (int i = 1; i < row.getLastCellNum(); i++) { +// try { +// String value = getCellValue(row.getCell(i)).trim(); +// if (!StringUtils.isBlank(value)) { +// orderNo = value; +// break; +// } +// } catch (Exception ex) { +// // 忽略异常,继续尝试 +// } +// } +// } +// continue; +// } +// +// // 单据日期 +// if (cellA.contains("单据日期")) { +// try { +// // 尝试从B列获取值 +// orderDate = getCellValue(row.getCell(1)).trim(); +// } catch (Exception e) { +// // 尝试从其他列获取 +// for (int i = 1; i < row.getLastCellNum(); i++) { +// try { +// String value = getCellValue(row.getCell(i)).trim(); +// if (!StringUtils.isBlank(value)) { +// orderDate = value; +// break; +// } +// } catch (Exception ex) { +// // 忽略异常,继续尝试 +// } +// } +// } +// continue; +// } +// +// // 客户收货地 +// if (cellA.contains("客户收货地")) { +// try { +// // 尝试从B列获取值 +// address = getCellValue(row.getCell(1)).trim(); +// } catch (Exception e) { +// // 尝试从其他列获取 +// for (int i = 1; i < row.getLastCellNum(); i++) { +// try { +// String value = getCellValue(row.getCell(i)).trim(); +// if (!StringUtils.isBlank(value)) { +// address = value; +// break; +// } +// } catch (Exception ex) { +// // 忽略异常,继续尝试 +// } +// } +// } +// continue; +// } +// +// // 客户 +// if (cellA.contains("客户:")) { +// try { +// // 尝试从B列获取值 +// customer = getCellValue(row.getCell(1)).trim(); +// } catch (Exception e) { +// // 尝试从其他列获取 +// for (int i = 1; i < row.getLastCellNum(); i++) { +// try { +// String value = getCellValue(row.getCell(i)).trim(); +// if (!StringUtils.isBlank(value)) { +// customer = value; +// break; +// } +// } catch (Exception ex) { +// // 忽略异常,继续尝试 +// } +// } +// } +// continue; +// } +// +// // ====================== 识别商品表头 ====================== +// // 检查是否是商品表头行 +// boolean isHeaderRow = false; +// try { +// // 遍历所有列,查找表头关键字 +// for (int i = 0; i < row.getLastCellNum(); i++) { +// String cellValue = getCellValue(row.getCell(i)).trim(); +// if (cellValue.equals("商品名称") || cellValue.equals("规格型号") || +// cellValue.equals("单位") || cellValue.equals("数量") || +// cellValue.equals("单价") || cellValue.equals("金额") || +// cellValue.equals("仓库") || cellValue.equals("备注") || +// cellValue.equals("序") || cellValue.equals("序号")) { +// isHeaderRow = true; +// // 记录列索引 +// columnMap.put(cellValue, i); +// } +// } +// } catch (Exception e) { +// // 忽略异常,继续处理 +// } +// +// if (isHeaderRow) { +// isProductRowStart = true; +// continue; // 跳过表头行 +// } +// +// // ====================== 只读取商品行区域的数据 ====================== +// if (!isProductRowStart) { +// continue; // 未进入商品行区域,跳过 +// } +// +// // 商品行判断:尝试从不同列获取序号 +// String seqStr = ""; +// try { +// // 尝试从序号列获取 +// if (columnMap.containsKey("序")) { +// seqStr = getCellValue(row.getCell(columnMap.get("序"))).trim(); +// } else if (columnMap.containsKey("序号")) { +// seqStr = getCellValue(row.getCell(columnMap.get("序号"))).trim(); +// } else { +// // 尝试从A列获取 +// seqStr = getCellValue(row.getCell(0)).trim(); +// } +// } catch (Exception e) { +// // 忽略异常,继续处理 +// } +// +// if (StringUtils.isBlank(seqStr) || !seqStr.matches("\\d+")) { +// continue; // 不是商品行,跳过 +// } +// +// // ====================== 按动态列索引读取商品字段 ====================== +// ImportItemVO item = new ImportItemVO(); +// item.setOrderNo(orderNo); // 全局表头信息 +// item.setOrderDate(orderDate); +// item.setCustomer(customer); +// item.setAddress(address); +// +// item.setSeq(seqStr); // 序号 +// +// // 商品名称 +// try { +// if (columnMap.containsKey("商品名称")) { +// item.setGoodsName(getCellValue(row.getCell(columnMap.get("商品名称"))).trim()); +// } else { +// // 尝试从其他列获取 +// for (int i = 0; i < row.getLastCellNum(); i++) { +// try { +// String value = getCellValue(row.getCell(i)).trim(); +// if (!StringUtils.isBlank(value) && !value.matches("\\d+\\.?\\d*") && +// !value.equals("序") && !value.equals("序号")) { +// item.setGoodsName(value); +// break; +// } +// } catch (Exception ex) { +// // 忽略异常,继续尝试 +// } +// } +// } +// } catch (Exception e) { +// // 忽略异常,继续处理 +// } +// +// // 规格型号 +// try { +// if (columnMap.containsKey("规格型号")) { +// item.setSpec(getCellValue(row.getCell(columnMap.get("规格型号"))).trim()); +// } +// } catch (Exception e) { +// // 忽略异常,继续处理 +// } +// +// // 单位 +// try { +// if (columnMap.containsKey("单位")) { +// String unitName = getCellValue(row.getCell(columnMap.get("单位"))).trim(); +// // 通过bm_measuring_unit字典翻译单位名称为编码 +// SysDictData dictData = sysDictDataService.getOne(new LambdaQueryWrapper() +// .eq(SysDictData::getDictType, "bm_measuring_unit") +// .eq(SysDictData::getDictLabel, unitName)); +// if (dictData != null) { +// item.setUnit(dictData.getDictValue()); +// } else { +// // 未找到对应字典,保留原始值并记录警告 +// item.setUnit(unitName); +// log.warn("未找到单位[{}]对应的字典翻译", unitName); +// } +// } +// } catch (Exception e) { +// // 忽略异常,继续处理 +// } +// +// // 数量 +// try { +// if (columnMap.containsKey("数量")) { +// item.setQty(getBigDecimal(row.getCell(columnMap.get("数量")))); +// } +// } catch (Exception e) { +// // 忽略异常,继续处理 +// } +// +// // 单价 +// try { +// if (columnMap.containsKey("单价")) { +// item.setPrice(getBigDecimal(row.getCell(columnMap.get("单价")))); +// } +// } catch (Exception e) { +// // 忽略异常,继续处理 +// } +// +// // 金额 +// try { +// if (columnMap.containsKey("金额")) { +// item.setAmount(getBigDecimal(row.getCell(columnMap.get("金额")))); +// } +// } catch (Exception e) { +// // 忽略异常,继续处理 +// } +// +// // 仓库 +// try { +// if (columnMap.containsKey("仓库")) { +// item.setRemark(getCellValue(row.getCell(columnMap.get("仓库"))).trim()); +// } +// } catch (Exception e) { +// // 忽略异常,继续处理 +// } +// +// // 备注 +// try { +// if (columnMap.containsKey("备注")) { +// item.setRemark(getCellValue(row.getCell(columnMap.get("备注"))).trim()); +// } +// } catch (Exception e) { +// // 忽略异常,继续处理 +// } +// +// dataList.add(item); +// } +// } +// +// if (dataList.isEmpty()) { +// return CommonResult.failed("未读取到有效商品数据,请检查Excel模板格式是否正确"); +// } +// +// // ====================== 2. 执行业务保存(你的原有逻辑,完全不变) ====================== +// int successCount = 0; +// int failureCount = 0; +// StringBuilder failureMsg = new StringBuilder(); +// // 用于存储导入过程中创建的单据ID,以便前端确认 +// Set billIds = new HashSet<>(); +// +// for (ImportItemVO item : dataList) { +// try { +// // 处理日期 +// Date billDate = null; +// if (StringUtils.isNotBlank(item.getOrderDate())) { +// try { +// java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); +// billDate = sdf.parse(item.getOrderDate()); +// } catch (Exception e) { +// failureCount++; +// failureMsg.append("商品:").append(item.getGoodsName()).append(":日期格式错误\n"); +// continue; +// } +// } +// +// // 处理单据编号,如果没有,就自动生成 +// String billNumber = item.getOrderNo(); +// if (StringUtils.isBlank(billNumber)) { +// billNumber = ckBillNumberService.getBillNumber("RK"); +// } +// +// // 验证必填字段 +// if (StringUtils.isBlank(item.getGoodsName())) { +// failureCount++; +// failureMsg.append("第").append(item.getSeq()).append("行:商品名称不能为空\n"); +// continue; +// } +// +// // 查找或创建仓库 +// CmWarehouse cmWarehouse = cmWarehouseService.getOne(new LambdaQueryWrapper() +// .eq(CmWarehouse::getDelFlag, "0") +// .last("LIMIT 1")); +// if (cmWarehouse == null) { +// // 创建默认仓库 +// cmWarehouse = new CmWarehouse(); +// cmWarehouse.setStockName("默认仓库"); +// cmWarehouse.setStockCode("DEFAULT"); +// cmWarehouse.setDelFlag("0"); +// cmWarehouseService.save(cmWarehouse); +// } +// +// // 查找或创建供应商 +// CmCust cmCust = null; +// if (StringUtils.isNotBlank(item.getCustomer())) { +// cmCust = cmCustService.getOne(new LambdaQueryWrapper() +// .eq(CmCust::getCustName, item.getCustomer())); +// if (cmCust == null) { +// // 创建新供应商 +// cmCust = new CmCust(); +// cmCust.setCustName(item.getCustomer()); +// // 生成custCode:名称的首字母大写 +// StringBuilder custCodeBuilder = new StringBuilder(); +// String[] nameParts = item.getCustomer().split("\\s+"); +// for (String part : nameParts) { +// if (!part.isEmpty()) { +// custCodeBuilder.append(Character.toUpperCase(part.charAt(0))); +// } +// } +// cmCust.setCustCode(custCodeBuilder.toString()); +// cmCust.setStatus("1"); +// cmCust.setDelFlag("0"); +// cmCustService.save(cmCust); +// } +// } else { +// // 如果没有客户信息,查找默认供应商 +// cmCust = cmCustService.getOne(new LambdaQueryWrapper() +// .eq(CmCust::getDelFlag, "0") +// .last("LIMIT 1")); +// if (cmCust == null) { +// // 创建默认供应商 +// cmCust = new CmCust(); +// cmCust.setCustName("默认供应商"); +// cmCust.setCustCode("DEFAULT"); +// cmCust.setStatus("1"); +// cmCust.setDelFlag("0"); +// cmCustService.save(cmCust); +// } +// } +// +// // 查找或创建商品 +// CmCustProduct product = cmCustProductService.getOne(new LambdaQueryWrapper() +// .eq(CmCustProduct::getGoodsName, item.getGoodsName()) +// .eq(CmCustProduct::getSpec, item.getSpec())); +// +// if (product == null) { +// // 创建新商品 +// product = new CmCustProduct(); +// product.setGoodsCode("PROD" + System.currentTimeMillis()); +// product.setGoodsName(item.getGoodsName()); +// product.setSpec(item.getSpec()); +// product.setUnit(item.getUnit()); +// product.setCustId(cmCust.getId()); +// product.setStatus("1"); +// product.setDelFlag("0"); +// cmCustProductService.save(product); +// } else if (updateSupport == 1) { +// // 更新商品信息 +// product.setUnit(item.getUnit()); +// product.setCustId(cmCust.getId()); +// cmCustProductService.updateById(product); +// } +// +// // 判断是否自动出库 +// String autoOut = "0"; +// // 检查仓库是否设置了自动出库 +// if (cmWarehouse != null && "1".equals(cmWarehouse.getAutoOut())) { +// autoOut = "1"; +// } +// // 检查商品分类是否设置了自动出库 +// if (product.getClassId() != null) { +// PmProductClass productClass = pmProductClassService.getById(product.getClassId()); +// if (productClass != null && "1".equals(productClass.getAutoOut())) { +// autoOut = "1"; +// } +// } +// +// // 查找或创建单据 +// CkBill bill = ckBillService.getOne(new LambdaQueryWrapper() +// .eq(CkBill::getBillNumber, billNumber)); +// if (bill == null) { +// bill = new CkBill(); +// bill.setBillNumber(billNumber); +// bill.setBillDate(billDate); +// bill.setBillType("1"); // 入库 +// bill.setStatus("0"); // 未确认 +// bill.setBillStatus("0"); +// bill.setSourceType("1"); +// bill.setAutoOut(autoOut); +// bill.setCustId(cmCust.getId()); +// bill.setCustName(cmCust.getCustName()); +// ckBillService.save(bill); +// // 将创建的单据ID添加到集合中,以便前端确认 +// billIds.add(bill.getId()); +// } else { +// // 如果是已存在的单据,也添加到集合中,以便前端确认 +// billIds.add(bill.getId()); +// } +// +// // 检查是否已存在相同商品的明细 +// CkBillCargo billCargo = ckBillCargoService.getOne(new LambdaQueryWrapper() +// .eq(CkBillCargo::getBillId, bill.getId()) +// .eq(CkBillCargo::getCargoId, product.getId())); +// +// if (billCargo == null) { +// // 创建新明细 +// billCargo = new CkBillCargo(); +// billCargo.setBillId(bill.getId()); +// billCargo.setCargoId(product.getId()); +// billCargo.setCargoName(product.getGoodsName()); +// billCargo.setHsCode(product.getGoodsCode()); +// billCargo.setCargoSpec(product.getSpec()); +// billCargo.setUnit(product.getUnit()); +// billCargo.setCargoWt(item.getQty()); +// billCargo.setUnitPrice(item.getPrice()); +// billCargo.setCargoValue(item.getAmount()); +// billCargo.setBillDate(billDate); +// billCargo.setBillNumber(billNumber); +// billCargo.setInoutType("1"); // 入库 +// billCargo.setSourceType("1"); +// billCargo.setCustId(cmCust.getId()); +// billCargo.setCustName(cmCust.getCustName()); +// billCargo.setInStockId(cmWarehouse.getId()); +// billCargo.setOpType("1"); +// billCargo.setTradType("1"); +// billCargo.setBillStatus("0"); +// ckBillCargoService.save(billCargo); +// +// // 创建单据库存信息 +// CkBillStock ckBillStock = new CkBillStock(); +// ckBillStock.setInoutType("1"); // 入库 +// ckBillStock.setStockId(cmWarehouse.getId()); +// ckBillStock.setStockName(cmWarehouse.getStockName()); +// ckBillStock.setStockCode(cmWarehouse.getStockCode()); +// ckBillStock.setBillNumber(billNumber); +// ckBillStock.setCargoNum(item.getQty()); +// ckBillStock.setCargoWt(item.getQty()); +// ckBillStock.setCargoVol(BigDecimal.ZERO); // 默认为0 +// ckBillStock.setCargoValue(item.getAmount()); +// ckBillStock.setBillCargoId(billCargo.getId()); +// ckBillStockService.save(ckBillStock); +// } else if (updateSupport == 1) { +// // 更新明细 +// billCargo.setCargoName(product.getGoodsName()); +// billCargo.setHsCode(product.getGoodsCode()); +// billCargo.setCargoSpec(product.getSpec()); +// billCargo.setUnit(product.getUnit()); +// billCargo.setCargoWt(item.getQty()); +// billCargo.setUnitPrice(item.getPrice()); +// billCargo.setCargoValue(item.getAmount()); +// billCargo.setBillDate(billDate); +// billCargo.setCustId(cmCust.getId()); +// billCargo.setCustName(cmCust.getCustName()); +// billCargo.setInStockId(cmWarehouse.getId()); +// ckBillCargoService.updateById(billCargo); +// +// // 更新单据库存信息 +// CkBillStock ckBillStock = ckBillStockService.getOne(new LambdaQueryWrapper() +// .eq(CkBillStock::getBillCargoId, billCargo.getId())); +// if (ckBillStock != null) { +// ckBillStock.setStockId(cmWarehouse.getId()); +// ckBillStock.setStockName(cmWarehouse.getStockName()); +// ckBillStock.setStockCode(cmWarehouse.getStockCode()); +// ckBillStock.setCargoNum(item.getQty()); +// ckBillStock.setCargoWt(item.getQty()); +// ckBillStock.setCargoValue(item.getAmount()); +// ckBillStockService.updateById(ckBillStock); +// } +// } +// +// successCount++; +// } catch (Exception e) { +// failureCount++; +// String msg = "商品:" + item.getGoodsName() + " 导入失败:" + e.getMessage(); +// failureMsg.append(msg).append("\n"); +// log.error(msg, e); +// } +// } +// +// if (failureCount > 0) { +// String msg = "导入成功" + successCount + "条,失败" + failureCount + "条:" + failureMsg.toString(); +// return CommonResult.failed(msg); +// } else { +// return CommonResult.success("导入成功" + successCount + "条"); +// } +// } + + // 读取单元格(通用) + private String getCellValue(Cell cell) { + if (cell == null) return ""; + cell.setCellType(CellType.STRING); + return cell.getStringCellValue().trim(); + } + + // 转数字 + private BigDecimal getBigDecimal(Cell cell) { + try { + return new BigDecimal(getCellValue(cell)); + } catch (Exception e) { + return BigDecimal.ZERO; } } /** * 获取单元格值 */ - private String getCellValue(Cell cell) { - if (cell == null) { - return ""; +// private String getCellValue(Cell cell) { +// if (cell == null) { +// return ""; +// } +// try { +// int cellType = cell.getCellType(); +// if (cellType == Cell.CELL_TYPE_STRING) { +// return cell.getStringCellValue(); +// } else if (cellType == Cell.CELL_TYPE_NUMERIC) { +// if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) { +// java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); +// return sdf.format(cell.getDateCellValue()); +// } else { +// return String.valueOf(cell.getNumericCellValue()); +// } +// } else if (cellType == Cell.CELL_TYPE_BOOLEAN) { +// return String.valueOf(cell.getBooleanCellValue()); +// } else if (cellType == Cell.CELL_TYPE_FORMULA) { +// return cell.getCellFormula(); +// } else { +// return ""; +// } +// } catch (Exception e) { +// return ""; +// } +// } + + /** + * 导入出库数据 + */ + @ApiOperation("导入出库数据") + @PostMapping("/importOutData") + @Transactional(rollbackFor = Exception.class) + public CommonResult importOutData(@RequestParam("file") MultipartFile file, @RequestParam(value = "updateSupport", defaultValue = "0") Integer updateSupport) throws Exception { + if (file.isEmpty()) { + return CommonResult.failed("文件不能为空"); } - try { - int cellType = cell.getCellType(); - if (cellType == Cell.CELL_TYPE_STRING) { - return cell.getStringCellValue(); - } else if (cellType == Cell.CELL_TYPE_NUMERIC) { - if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) { - java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); - return sdf.format(cell.getDateCellValue()); + + // 解析Excel文件 + Workbook workbook = WorkbookFactory.create(file.getInputStream()); + Sheet sheet = workbook.getSheetAt(0); + int rowCount = sheet.getPhysicalNumberOfRows(); + if (rowCount < 2) { + workbook.close(); + return CommonResult.failed("Excel文件至少包含表头和一条数据"); + } + + // 开始导入 + int successCount = 0; + int failureCount = 0; + StringBuilder failureMsg = new StringBuilder(); + // 用于存储导入过程中创建的单据ID,以便前端确认 + Set billIds = new HashSet<>(); + + for (int i = 1; i < rowCount; i++) { + Row row = sheet.getRow(i); + if (row == null) { + continue; + } + + try { + // 读取数据 + String billNumber = getCellValue(row.getCell(0)); + String stockName = getCellValue(row.getCell(1)); + String billDateStr = getCellValue(row.getCell(2)); + String cargoCategory = getCellValue(row.getCell(3)); + String hsCode = getCellValue(row.getCell(4)); + String cargoName = getCellValue(row.getCell(5)); + String batchNo = getCellValue(row.getCell(6)); + String cargoSpec = getCellValue(row.getCell(7)); + String unit = getCellValue(row.getCell(8)); + String cargoWtStr = getCellValue(row.getCell(9)); + String unitPriceStr = getCellValue(row.getCell(10)); + String cargoValueStr = getCellValue(row.getCell(11)); + String deptName = getCellValue(row.getCell(12)); + String receiverName = getCellValue(row.getCell(13)); + + // 处理日期 + Date billDate = null; + if (StringUtils.isNotBlank(billDateStr)) { + try { + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); + billDate = sdf.parse(billDateStr); + } catch (Exception e) { + failureCount++; + failureMsg.append("第").append(i + 1).append("行:日期格式错误\n"); + continue; + } + } + + // 处理单据编号,如果没有,就自动生成 + if (StringUtils.isBlank(billNumber)) { + billNumber = ckBillNumberService.getBillNumber("CK"); + } + + // 验证必填字段 + if (StringUtils.isBlank(stockName) || StringUtils.isBlank(cargoName)) { + failureCount++; + failureMsg.append("第").append(i + 1).append("行:必填字段不能为空\n"); + continue; + } + + // 处理数值 + BigDecimal cargoWt = StringUtils.isBlank(cargoWtStr) ? BigDecimal.ZERO : new BigDecimal(cargoWtStr); + BigDecimal unitPrice = StringUtils.isBlank(unitPriceStr) ? BigDecimal.ZERO : new BigDecimal(unitPriceStr); + BigDecimal cargoValue = StringUtils.isBlank(cargoValueStr) ? BigDecimal.ZERO : new BigDecimal(cargoValueStr); + + // 查找仓库 + CmWarehouse cmWarehouse = cmWarehouseService.getOne(new LambdaQueryWrapper().eq(CmWarehouse::getStockName, stockName)); + if (cmWarehouse == null) { + failureCount++; + failureMsg.append("第").append(i + 1).append("行:仓库").append(stockName).append("不存在\n"); + continue; + } + + // 查找领用部门 + SysDept dept = sysDeptService.getOne(new LambdaQueryWrapper().eq(SysDept::getDeptName, deptName)); + Long deptId = null; + if (dept != null) { + deptId = dept.getDeptId(); + } + + // 查找领用人 + SystemAdmin receiver = sysAdminService.getOne(new LambdaQueryWrapper().eq(SystemAdmin::getRealName, receiverName)); + Long receiverId = null; + if (receiver != null) { + receiverId = Long.valueOf(receiver.getId()); + } + + // 处理批次号(入库单据号) + CkCargoStock ckCargoStock = null; + CmCust cmCust = null; + if (StringUtils.isNotBlank(batchNo)) { + // 通过批次号(入库单据号)查询CkCargoStock + ckCargoStock = ckCargoStockService.getOne(new LambdaQueryWrapper() + .eq(CkCargoStock::getBillNumber, batchNo)); + if (ckCargoStock != null) { + // 从CkCargoStock中获取供应商信息 + if (ckCargoStock.getCustId() != null) { + cmCust = cmCustService.getById(ckCargoStock.getCustId()); + } + // 检查库存是否充足 + Boolean flag = false; + if (ckCargoStock.getCargoNum().compareTo(BigDecimal.ONE) >= 0) { + flag = true; + } + if (ckCargoStock.getCargoWt().compareTo(cargoWt) >= 0) { + flag = true; + } + if (ckCargoStock.getCargoVol().compareTo(BigDecimal.ZERO) >= 0) { + flag = true; + } + if (ckCargoStock.getCargoValue().compareTo(cargoValue) >= 0) { + flag = true; + } + if (!flag) { + failureCount++; + failureMsg.append("第").append(i + 1).append("行:库存不足\n"); + continue; + } + } else { + failureCount++; + failureMsg.append("第").append(i + 1).append("行:批次号对应的库存不存在\n"); + continue; + } } else { - return String.valueOf(cell.getNumericCellValue()); + failureCount++; + failureMsg.append("第").append(i + 1).append("行:批次号不能为空\n"); + continue; } - } else if (cellType == Cell.CELL_TYPE_BOOLEAN) { - return String.valueOf(cell.getBooleanCellValue()); - } else if (cellType == Cell.CELL_TYPE_FORMULA) { - return cell.getCellFormula(); - } else { - return ""; + + // 查找商品 + CmCustProduct product = cmCustProductService.getOne(new LambdaQueryWrapper() + .eq(CmCustProduct::getGoodsCode, hsCode) + .eq(CmCustProduct::getGoodsName, cargoName) + .eq(CmCustProduct::getSpec, cargoSpec)); + + if (product == null) { + // 商品不存在,创建商品 + product = new CmCustProduct(); + product.setGoodsCode(hsCode); + product.setGoodsName(cargoName); + product.setSpec(cargoSpec); + product.setUnit(unit); + + // 处理商品分类 + if (StringUtils.isNotBlank(cargoCategory)) { + // 查找商品分类 + List pmProductClasses = pmProductClassService.list(new LambdaQueryWrapper() + .eq(PmProductClass::getClassName, cargoCategory)); + + if (null != pmProductClasses && pmProductClasses.size() > 0) { + product.setClassId(pmProductClasses.get(0).getId()); + } + } + + // 保存商品 + cmCustProductService.save(product); + } + + // 判断是否自动出库 + String autoOut = "0"; + // 检查仓库是否设置了自动出库 + if (cmWarehouse != null && "1".equals(cmWarehouse.getAutoOut())) { + autoOut = "1"; + } + // 检查商品分类是否设置了自动出库 + if (product.getClassId() != null) { + PmProductClass productClass = pmProductClassService.getById(product.getClassId()); + if (productClass != null && "1".equals(productClass.getAutoOut())) { + autoOut = "1"; + } + } + + // 查找或创建单据 + CkBill bill = ckBillService.getOne(new LambdaQueryWrapper().eq(CkBill::getBillNumber, billNumber)); + if (bill == null) { + // 创建新单据 + bill = new CkBill(); + bill.setBillNumber(billNumber); + bill.setBillDate(billDate); + bill.setBillType("2"); // 出库 + bill.setStatus("0"); // 未确认 + bill.setBillStatus("0"); + bill.setSourceType("1"); // 直接领用 + bill.setAutoOut(autoOut); + bill.setReceiverDeptId(deptId); + bill.setReceiverId(receiverId); + if (cmCust != null) { + bill.setCustId(cmCust.getId()); + bill.setCustName(cmCust.getCustName()); + } + ckBillService.save(bill); + } + + // 检查是否已存在相同商品的明细 + CkBillCargo billCargo = ckBillCargoService.getOne(new LambdaQueryWrapper() + .eq(CkBillCargo::getBillId, bill.getId()) + .eq(CkBillCargo::getCargoId, product.getId())); + + if (billCargo == null) { + // 创建新明细 + billCargo = new CkBillCargo(); + billCargo.setBillId(bill.getId()); + billCargo.setCargoId(product.getId()); + billCargo.setCargoName(product.getGoodsName()); + billCargo.setHsCode(product.getGoodsCode()); + billCargo.setCargoSpec(product.getSpec()); + billCargo.setUnit(product.getUnit()); + billCargo.setCargoWt(cargoWt); + billCargo.setUnitPrice(unitPrice); + billCargo.setCargoValue(cargoValue); + billCargo.setBillDate(billDate); + billCargo.setBillNumber(billNumber); + billCargo.setInoutType("2"); // 出库 + billCargo.setSourceType("1"); + billCargo.setOutStockId(cmWarehouse.getId()); + billCargo.setOpType("1"); + billCargo.setTradType("1"); + billCargo.setBillStatus("0"); + // 设置供应商信息(从批次号对应的库存中获取) + if (cmCust != null) { + billCargo.setCustId(cmCust.getId()); + billCargo.setCustName(cmCust.getCustName()); + } + // 如果有CkCargoStock,使用其中的信息 + if (ckCargoStock != null) { + billCargo.setCargoId(ckCargoStock.getCargoId()); + billCargo.setHsCode(ckCargoStock.getHsCode()); + billCargo.setCargoName(ckCargoStock.getCargoName()); + billCargo.setCargoSpec(ckCargoStock.getCargoSpec()); + billCargo.setOpType(ckCargoStock.getOpType()); + billCargo.setTradType(ckCargoStock.getTradType()); + } + ckBillCargoService.save(billCargo); + + // 创建单据库存信息 + CkBillStock ckBillStock = new CkBillStock(); + ckBillStock.setInoutType("2"); // 出库 + if (ckCargoStock != null) { + ckBillStock.setStockId(ckCargoStock.getStockId()); + ckBillStock.setStockName(ckCargoStock.getStockName()); + ckBillStock.setStockCode(ckCargoStock.getStockCode()); + } else { + ckBillStock.setStockId(cmWarehouse.getId()); + ckBillStock.setStockName(cmWarehouse.getStockName()); + ckBillStock.setStockCode(cmWarehouse.getStockCode()); + } + // 使用批次号作为billNumber + ckBillStock.setBillNumber(batchNo); + ckBillStock.setCargoNum(BigDecimal.ONE); // 默认为1 + ckBillStock.setCargoWt(cargoWt); + ckBillStock.setCargoVol(BigDecimal.ZERO); // 默认为0 + ckBillStock.setCargoValue(cargoValue); + ckBillStock.setBillCargoId(billCargo.getId()); + ckBillStockService.save(ckBillStock); + + // 更新库存 + if (ckCargoStock != null) { + ckCargoStock.setCargoNum(ckCargoStock.getCargoNum().subtract(BigDecimal.ONE)); + ckCargoStock.setCargoWt(ckCargoStock.getCargoWt().subtract(cargoWt)); + ckCargoStock.setCargoVol(ckCargoStock.getCargoVol().subtract(BigDecimal.ZERO)); + ckCargoStock.setCargoValue(ckCargoStock.getCargoValue().subtract(cargoValue)); + ckCargoStockService.updateById(ckCargoStock); + } + } else if (updateSupport == 1) { + // 检查库存是否充足 + Boolean flag = false; + if (ckCargoStock.getCargoNum().compareTo(BigDecimal.ONE) >= 0) { + flag = true; + } + if (ckCargoStock.getCargoWt().compareTo(cargoWt) >= 0) { + flag = true; + } + if (ckCargoStock.getCargoVol().compareTo(BigDecimal.ZERO) >= 0) { + flag = true; + } + if (ckCargoStock.getCargoValue().compareTo(cargoValue) >= 0) { + flag = true; + } + if (!flag) { + failureCount++; + failureMsg.append("第").append(i + 1).append("行:库存不足\n"); + continue; + } + + // 更新明细 + billCargo.setCargoName(product.getGoodsName()); + billCargo.setHsCode(product.getGoodsCode()); + billCargo.setCargoSpec(product.getSpec()); + billCargo.setUnit(product.getUnit()); + billCargo.setCargoWt(cargoWt); + billCargo.setUnitPrice(unitPrice); + billCargo.setCargoValue(cargoValue); + billCargo.setBillDate(billDate); + billCargo.setOutStockId(cmWarehouse.getId()); + // 设置批次号 + // 设置供应商信息(从批次号对应的库存中获取) + if (cmCust != null) { + billCargo.setCustId(cmCust.getId()); + billCargo.setCustName(cmCust.getCustName()); + } + // 如果有CkCargoStock,使用其中的信息 + if (ckCargoStock != null) { + billCargo.setCargoId(ckCargoStock.getCargoId()); + billCargo.setHsCode(ckCargoStock.getHsCode()); + billCargo.setCargoName(ckCargoStock.getCargoName()); + billCargo.setCargoSpec(ckCargoStock.getCargoSpec()); + billCargo.setOpType(ckCargoStock.getOpType()); + billCargo.setTradType(ckCargoStock.getTradType()); + } + ckBillCargoService.updateById(billCargo); + + // 更新单据库存信息 + CkBillStock ckBillStock = ckBillStockService.getOne(new LambdaQueryWrapper().eq(CkBillStock::getBillCargoId, billCargo.getId())); + if (ckBillStock != null) { + if (ckCargoStock != null) { + ckBillStock.setStockId(ckCargoStock.getStockId()); + ckBillStock.setStockName(ckCargoStock.getStockName()); + ckBillStock.setStockCode(ckCargoStock.getStockCode()); + } else { + ckBillStock.setStockId(cmWarehouse.getId()); + ckBillStock.setStockName(cmWarehouse.getStockName()); + ckBillStock.setStockCode(cmWarehouse.getStockCode()); + } + // 使用批次号作为billNumber + ckBillStock.setBillNumber(batchNo); + ckBillStock.setCargoWt(cargoWt); + ckBillStock.setCargoValue(cargoValue); + ckBillStockService.updateById(ckBillStock); + + // 更新库存 + if (ckCargoStock != null) { + ckCargoStock.setCargoNum(ckCargoStock.getCargoNum().subtract(BigDecimal.ONE)); + ckCargoStock.setCargoWt(ckCargoStock.getCargoWt().subtract(cargoWt)); + ckCargoStock.setCargoVol(ckCargoStock.getCargoVol().subtract(BigDecimal.ZERO)); + ckCargoStock.setCargoValue(ckCargoStock.getCargoValue().subtract(cargoValue)); + ckCargoStockService.updateById(ckCargoStock); + } + } + } + + successCount++; + } catch (Exception e) { + failureCount++; + String msg = "第" + (i + 1) + "行导入失败:" + e.getMessage(); + failureMsg.append(msg).append("\n"); + log.error(msg, e); } - } catch (Exception e) { - return ""; } + + workbook.close(); + + if (failureCount > 0) { + String msg = "导入成功" + successCount + "条,失败" + failureCount + "条:" + failureMsg.toString(); + return CommonResult.failed(msg); + } else { + // 返回导入成功的消息和单据ID列表,以便前端显示确认弹窗 + Map result = new HashMap<>(); + result.put("message", "导入成功" + successCount + "条"); + result.put("billIds", billIds); + return CommonResult.success(result); + } + } + + /** + * 导入数据(兼容旧接口) + */ + @ApiOperation("导入数据") + @PostMapping("/importData") + public CommonResult importData(@RequestParam("file") MultipartFile file, @RequestParam(value = "updateSupport", defaultValue = "0") Integer updateSupport) throws Exception { + // 默认导入入库数据 + return importInData(file, updateSupport); } } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkCargoStockController.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkCargoStockController.java index 73a38e1..fa505ce 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkCargoStockController.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkCargoStockController.java @@ -4,6 +4,7 @@ import java.math.BigDecimal; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -31,6 +32,12 @@ import org.springframework.security.access.prepost.PreAuthorize; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.zbkj.modules.autogencode.entity.CkCargoStock; import com.zbkj.modules.autogencode.service.CkCargoStockService; +import com.zbkj.modules.autogencode.entity.CmWarehouse; +import com.zbkj.modules.autogencode.entity.CmCustProduct; +import com.zbkj.modules.autogencode.entity.PmProductClass; +import com.zbkj.modules.autogencode.service.CmWarehouseService; +import com.zbkj.modules.autogencode.service.CmCustProductService; +import com.zbkj.modules.autogencode.service.PmProductClassService; @@ -53,6 +60,15 @@ public class CkCargoStockController { @Autowired private CkBillStockService ckBillStockService; + + @Autowired + private CmWarehouseService cmWarehouseService; + + @Autowired + private CmCustProductService cmCustProductService; + + @Autowired + private PmProductClassService pmProductClassService; /** * 条件设置 * 根据实体类字段自动生成查询条件 @@ -397,6 +413,7 @@ public class CkCargoStockController { "hs_code", "cargo_number", "stock_date", + "stock_id", "sum(cargo_num) as cargo_num", "sum(cargo_wt) as cargo_wt", "sum(cargo_vol) as cargo_vol" @@ -441,7 +458,8 @@ public class CkCargoStockController { "origin_country", "hs_code", "cargo_number", - "stock_date" + "stock_date", + "stock_id" ); // 排序处理 @@ -473,6 +491,34 @@ public class CkCargoStockController { Page page = new Page<>(pageParamRequest.getPage(), pageParamRequest.getLimit()); Page resultPage = ckCargoStockService.page(page, queryWrapper); + // 补充仓库、分类、单位信息 + for (CkCargoStock stock : resultPage.getRecords()) { + // 仓库信息 + if (stock.getStockId() != null) { + CmWarehouse warehouse = cmWarehouseService.getById(stock.getStockId()); + if (warehouse != null) { + stock.setStockCode(warehouse.getStockCode()); + stock.setStockName(warehouse.getStockName()); + } + } + // 根据cargoId查询CmCustProduct获取分类、单位等信息 + if (stock.getCargoId() != null) { + CmCustProduct product = cmCustProductService.getById(stock.getCargoId()); + if (product != null) { + stock.setClassId(product.getClassId()); + stock.setUnit(product.getUnit()); + // 根据classId查询物资类别代码 + if (product.getClassId() != null) { + PmProductClass productClass = pmProductClassService.getById(product.getClassId()); + if (productClass != null) { + stock.setClassCode(productClass.getClassCode()); + stock.setClassName(productClass.getClassName()); + } + } + } + } + } + CommonPage commonPage = new CommonPage<>(); commonPage.setPage((int) resultPage.getCurrent()); commonPage.setLimit((int) resultPage.getSize()); @@ -502,9 +548,10 @@ public class CkCargoStockController { CkCargoStock::getRemark, CkCargoStock::getCreateDept, CkCargoStock::getCreateBy, CkCargoStock::getCreateTime, CkCargoStock::getUpdateBy, CkCargoStock::getUpdateTime, CkCargoStock::getDelFlag); condition(queryWrapper, ckCargoStock); - if(ckCargoStock.getCustId()!=null){ + // 过滤掉重量为0的商品,除非明确指定要显示 + if (null == ckCargoStock.getIsShow() || !ckCargoStock.getIsShow()) { queryWrapper.and(qw -> { - qw.gt(CkCargoStock::getCargoWt, "0") + qw.gt(CkCargoStock::getCargoWt, 0) ; }); } @@ -574,6 +621,11 @@ public class CkCargoStockController { return; } + // 仓库ID + if (request.getStockId() != null) { + queryWrapper.eq("stock_id", request.getStockId()); + } + // 商品ID if (request.getCargoId() != null) { queryWrapper.eq("cargo_id", request.getCargoId()); @@ -614,6 +666,21 @@ public class CkCargoStockController { queryWrapper.eq("cargo_number", request.getCargoNumber()); } + // 商品类别ID + if (request.getClassId() != null) { + // 通过子查询获取对应类别的商品ID + LambdaQueryWrapper productQueryWrapper = new LambdaQueryWrapper<>(); + productQueryWrapper.eq(CmCustProduct::getClassId, request.getClassId()); + List products = cmCustProductService.list(productQueryWrapper); + if (!products.isEmpty()) { + List cargoIds = products.stream().map(CmCustProduct::getId).collect(java.util.stream.Collectors.toList()); + queryWrapper.in("cargo_id", cargoIds); + } else { + // 如果没有找到对应类别的商品,返回空结果 + queryWrapper.eq("cargo_id", -1); + } + } + // 入库日期 if (request.getStockDate() != null) { queryWrapper.eq("stock_date", request.getStockDate()); diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkStockChangeController.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkStockChangeController.java index ba92d2d..13cd62d 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkStockChangeController.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CkStockChangeController.java @@ -1,12 +1,15 @@ package com.zbkj.modules.autogencode.controller; import java.util.Arrays; +import java.util.List; import java.util.Map; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.zbkj.common.request.PageParamRequest; +import com.zbkj.common.request.InventoryRequest; import com.zbkj.common.response.CommonResult; +import com.zbkj.common.response.InventoryResponse; import com.zbkj.common.page.CommonPage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; @@ -270,4 +273,30 @@ public class CkStockChangeController { return CommonResult.failed(); } + /** + * 查询物资盘点表数据 + */ + @ApiOperation(value = "查询物资盘点表数据") + @RequestMapping(value = "/inventory", method = RequestMethod.POST) + public CommonResult> getInventoryList(@RequestBody InventoryRequest request) { + List list = ckStockChangeService.getInventoryList(request); + CommonPage page = new CommonPage<>(); + page.setList(list); + page.setTotal(Long.valueOf(list.size())); + return CommonResult.success(page); + } + + /** + * 导出物资盘点表数据 + */ + @ApiOperation(value = "导出物资盘点表数据") + @RequestMapping(value = "/export-inventory", method = RequestMethod.GET) + public void exportInventory(InventoryRequest request) { + ckStockChangeService.exportInventory(request); + } + + + } + + diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CmCustController.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CmCustController.java index 1034c81..9346a91 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CmCustController.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CmCustController.java @@ -218,6 +218,8 @@ public class CmCustController { return CommonResult.success(cmCustList); } + + /** * 详情数据 */ diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CmCustProductController.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CmCustProductController.java index e39f0ed..0e4a51a 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CmCustProductController.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/CmCustProductController.java @@ -356,7 +356,7 @@ public class CmCustProductController { String unit = cmCustProduct.getUnit(); String translatedUnit = unit; List dictDataList = sysDictDataService.list(new LambdaQueryWrapper() - .eq(SysDictData::getDictType, "sys_measurement_unit") + .eq(SysDictData::getDictType, "bm_measuring_unit") .eq(SysDictData::getDictValue, unit) .eq(SysDictData::getStatus, "0")); if (!dictDataList.isEmpty()) { diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmDailyMenuController.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmDailyMenuController.java index 3ca6822..0f039f2 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmDailyMenuController.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmDailyMenuController.java @@ -1,26 +1,39 @@ package com.zbkj.modules.autogencode.controller; import java.util.*; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigDecimal; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.zbkj.common.request.PageParamRequest; import com.zbkj.common.response.CommonResult; import com.zbkj.common.page.CommonPage; +import org.apache.poi.hssf.util.HSSFColor; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.*; +import org.apache.poi.hssf.usermodel.*; +import org.apache.poi.ss.util.*; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; - - +import javax.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.ss.usermodel.DateUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.zbkj.modules.autogencode.entity.PmDailyMenu; import com.zbkj.modules.autogencode.entity.PmDailyMenuDtl; +import com.zbkj.modules.autogencode.entity.SysDictData; import com.zbkj.modules.autogencode.service.PmDailyMenuService; import com.zbkj.modules.autogencode.service.PmDailyMenuDtlService; +import com.zbkj.modules.autogencode.service.SysDictDataService; @@ -36,6 +49,9 @@ public class PmDailyMenuController { @Autowired private PmDailyMenuDtlService pmDailyMenuDtlService; + + @Autowired + private SysDictDataService sysDictDataService; @@ -200,6 +216,15 @@ public class PmDailyMenuController { */ @RequestMapping(value = "/delete", method = RequestMethod.POST) public CommonResult delete(@RequestBody Long[] ids){ + // 检查是否有关联的PmDailyMenuDtl + for (Long id : ids) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(PmDailyMenuDtl::getMenuId, id); + if (pmDailyMenuDtlService.count(queryWrapper) > 0) { + return CommonResult.failed("菜单下存在关联的菜单明细,无法删除"); + } + } + if (pmDailyMenuService.removeByIds(Arrays.asList(ids))) { return CommonResult.success(); } @@ -400,4 +425,693 @@ public class PmDailyMenuController { } } + /** + * 导出菜单 + */ + @ApiOperation(value = "导出菜单") + @RequestMapping(value = "/export", method = RequestMethod.GET) + public void export(PmDailyMenu request, HttpServletResponse response) throws IOException { + // 构建查询条件 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + condition(queryWrapper, request); + queryWrapper.orderByDesc(PmDailyMenu::getMenuDate); + queryWrapper.orderByDesc(PmDailyMenu::getCanteenName); + queryWrapper.orderByDesc(PmDailyMenu::getMealType); + + // 查询菜单数据 + List menus = pmDailyMenuService.list(queryWrapper); + + // 为每个菜单查询明细 + for (PmDailyMenu menu : menus) { + LambdaQueryWrapper dtlQueryWrapper = new LambdaQueryWrapper<>(); + dtlQueryWrapper.eq(PmDailyMenuDtl::getMenuId, menu.getId()); + List menuDtls = pmDailyMenuDtlService.list(dtlQueryWrapper); + menu.setPmDailyMenuDtls(menuDtls); + } + + // 创建 Excel 工作簿 + Workbook workbook = new XSSFWorkbook(); + + // 为每个菜单创建一个 sheet + for (PmDailyMenu menu : menus) { + // 创建 sheet,名称为 日期-食堂-餐别 + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); + String dateStr = sdf.format(menu.getMenuDate()); + String sheetName = dateStr + "-" + menu.getCanteenName() + "-" + getMealTypeLabel(menu.getMealType()); + // 移除无效字符 + sheetName = sheetName.replaceAll("[:\\/\\?\\*\\[\\]]", ""); + // 限制 sheet 名称长度 + if (sheetName.length() > 31) { + sheetName = sheetName.substring(0, 31); + } + Sheet sheet = workbook.createSheet(sheetName); + + // 设置列宽 + sheet.setColumnWidth(0, 5000); + sheet.setColumnWidth(1, 3000); + sheet.setColumnWidth(2, 5000); + sheet.setColumnWidth(3, 3000); + sheet.setColumnWidth(4, 5000); + sheet.setColumnWidth(5, 3000); + sheet.setColumnWidth(6, 5000); + sheet.setColumnWidth(7, 3000); + + // 创建标题行 + Row titleRow = sheet.createRow(0); + titleRow.setHeightInPoints(40); + // 为所有列创建单元格并应用背景色 + for (int i = 0; i <= 7; i++) { + Cell cell = titleRow.createCell(i); + if (i == 0) { + cell.setCellValue(getWeekday(menu.getMenuDate()) + "(" + getMealTypeLabel(menu.getMealType()) + ")"); + } + cell.setCellStyle(getTitleCellStyle(workbook)); + } + sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(0, 0, 0, 7)); + + // 创建表头 + Row headerRow = sheet.createRow(1); + headerRow.setHeightInPoints(30); + + // 为所有列创建单元格并应用背景色 + for (int i = 0; i <= 7; i++) { + Cell cell = headerRow.createCell(i); + cell.setCellStyle(getHeaderCellStyle(workbook)); + } + + // 设置表头内容并合并单元格 + Cell cell0 = headerRow.getCell(0); + cell0.setCellValue("主荤菜"); + sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(1, 1, 0, 1)); + + Cell cell2 = headerRow.getCell(2); + cell2.setCellValue("半荤菜"); + sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(1, 1, 2, 3)); + + Cell cell4 = headerRow.getCell(4); + cell4.setCellValue("素菜"); + sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(1, 1, 4, 5)); + + Cell cell6 = headerRow.getCell(6); + cell6.setCellValue("炖罐"); + sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(1, 1, 6, 7)); + + // 分类菜品 + Map> dishMap = new HashMap<>(); + dishMap.put("主荤菜", new ArrayList<>()); + dishMap.put("半荤菜", new ArrayList<>()); + dishMap.put("素菜", new ArrayList<>()); + dishMap.put("炖罐", new ArrayList<>()); + dishMap.put("免费主食", new ArrayList<>()); + dishMap.put("付费主食", new ArrayList<>()); + dishMap.put("打包/西点", new ArrayList<>()); + dishMap.put("打包/中点", new ArrayList<>()); + + if (menu.getPmDailyMenuDtls() != null) { + for (PmDailyMenuDtl dtl : menu.getPmDailyMenuDtls()) { + String itemType = dtl.getItemType(); + if ("ZHC".equals(itemType)) { + dishMap.get("主荤菜").add(dtl); + } else if ("BHC".equals(itemType)) { + dishMap.get("半荤菜").add(dtl); + } else if ("SC".equals(itemType)) { + dishMap.get("素菜").add(dtl); + } else if ("DG".equals(itemType)) { + dishMap.get("炖罐").add(dtl); + } else if ("MFZS".equals(itemType)) { + dishMap.get("免费主食").add(dtl); + } else if ("FFZS".equals(itemType)) { + dishMap.get("付费主食").add(dtl); + } else if ("DBXD".equals(itemType)) { + dishMap.get("打包/西点").add(dtl); + } else if ("DBZD".equals(itemType)) { + dishMap.get("打包/中点").add(dtl); + } + } + } + + // 填充数据 + int rowIndex = 2; + int maxRows = Math.max( + Math.max(dishMap.get("主荤菜").size(), dishMap.get("半荤菜").size()), + Math.max(dishMap.get("素菜").size(), dishMap.get("炖罐").size()) + ); + + for (int i = 0; i < maxRows; i++) { + Row dataRow = sheet.createRow(rowIndex++); + dataRow.setHeightInPoints(25); + + // 为所有列创建单元格并应用背景色 + for (int j = 0; j <= 7; j++) { + Cell cell = dataRow.createCell(j); + cell.setCellStyle(getDataCellStyle(workbook)); + } + + // 主荤菜 + if (i < dishMap.get("主荤菜").size()) { + PmDailyMenuDtl dtl = dishMap.get("主荤菜").get(i); + Cell mainCell0 = dataRow.getCell(0); + mainCell0.setCellValue(dtl.getItemName()); + Cell mainCell1 = dataRow.getCell(1); + mainCell1.setCellValue(dtl.getItemPrice().doubleValue()); + } + + // 半荤菜 + if (i < dishMap.get("半荤菜").size()) { + PmDailyMenuDtl dtl = dishMap.get("半荤菜").get(i); + Cell halfCell2 = dataRow.getCell(2); + halfCell2.setCellValue(dtl.getItemName()); + Cell halfCell3 = dataRow.getCell(3); + halfCell3.setCellValue(dtl.getItemPrice().doubleValue()); + } + + // 素菜 + if (i < dishMap.get("素菜").size()) { + PmDailyMenuDtl dtl = dishMap.get("素菜").get(i); + Cell vegCell4 = dataRow.getCell(4); + vegCell4.setCellValue(dtl.getItemName()); + Cell vegCell5 = dataRow.getCell(5); + vegCell5.setCellValue(dtl.getItemPrice().doubleValue()); + } + + // 炖罐 + if (i < dishMap.get("炖罐").size()) { + PmDailyMenuDtl dtl = dishMap.get("炖罐").get(i); + Cell stewCell6 = dataRow.getCell(6); + stewCell6.setCellValue(dtl.getItemName()); + Cell stewCell7 = dataRow.getCell(7); + stewCell7.setCellValue(dtl.getItemPrice().doubleValue()); + } else if (dishMap.get("炖罐").isEmpty()) { + // 只有当没有炖罐菜品时才显示"以实际供应为准" + Cell stewCell6 = dataRow.getCell(6); + stewCell6.setCellValue("以实际供应为准"); + } + } + + // 添加主食和免费项目 + // 免费主食 + if (!dishMap.get("免费主食").isEmpty()) { + Row freeRow = sheet.createRow(rowIndex++); + freeRow.setHeightInPoints(25); + // 为所有列创建单元格并应用背景色 + for (int i = 0; i <= 7; i++) { + Cell cell = freeRow.createCell(i); + if (i == 0) { + StringBuilder freeItems = new StringBuilder("免费:"); + List freeStapleList = dishMap.get("免费主食"); + for (int j = 0; j < freeStapleList.size(); j++) { + PmDailyMenuDtl dtl = freeStapleList.get(j); + freeItems.append(dtl.getItemName()); + if (j < freeStapleList.size() - 1) { + freeItems.append(" "); + } + } + cell.setCellValue(freeItems.toString()); + } + cell.setCellStyle(getContentCellStyle(workbook)); + } + sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(rowIndex-1, rowIndex-1, 0, 7)); + } + + // 付费主食 + if (!dishMap.get("付费主食").isEmpty()) { + Row paidRow = sheet.createRow(rowIndex++); + paidRow.setHeightInPoints(25); + // 为所有列创建单元格并应用背景色 + for (int i = 0; i <= 7; i++) { + Cell cell = paidRow.createCell(i); + if (i == 0) { + StringBuilder paidItems = new StringBuilder("付费主食:"); + List paidStapleList = dishMap.get("付费主食"); + for (int j = 0; j < paidStapleList.size(); j++) { + PmDailyMenuDtl dtl = paidStapleList.get(j); + paidItems.append(dtl.getItemName()); + paidItems.append(" ").append(dtl.getItemPrice().doubleValue()); + if (j < paidStapleList.size() - 1) { + paidItems.append(" "); + } + } + cell.setCellValue(paidItems.toString()); + } + cell.setCellStyle(getContentCellStyle(workbook)); + } + sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(rowIndex-1, rowIndex-1, 0, 7)); + } + + // 打包品种 + List westernList = dishMap.get("打包/西点"); + List chineseList = dishMap.get("打包/中点"); + if (!westernList.isEmpty() || !chineseList.isEmpty()) { + Row packRow = sheet.createRow(rowIndex++); + packRow.setHeightInPoints(25); + // 为所有列创建单元格并应用背景色 + for (int i = 0; i <= 7; i++) { + Cell cell = packRow.createCell(i); + if (i == 0) { + StringBuilder packItems = new StringBuilder("打包品种:"); + + // 西点 + if (!westernList.isEmpty()) { + packItems.append("西点: "); + for (int j = 0; j < westernList.size(); j++) { + PmDailyMenuDtl dtl = westernList.get(j); + packItems.append(dtl.getItemName()); + packItems.append(" ").append(dtl.getItemPrice().doubleValue()); + if (j < westernList.size() - 1) { + packItems.append(" "); + } + } + } + + // 中点 + if (!chineseList.isEmpty()) { + if (!westernList.isEmpty()) { + packItems.append(" "); + } + packItems.append("中点: "); + for (int j = 0; j < chineseList.size(); j++) { + PmDailyMenuDtl dtl = chineseList.get(j); + packItems.append(dtl.getItemName()); + packItems.append(" ").append(dtl.getItemPrice().doubleValue()); + if (j < chineseList.size() - 1) { + packItems.append(" "); + } + } + } + + cell.setCellValue(packItems.toString()); + } + cell.setCellStyle(getContentCellStyle(workbook)); + } + sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(rowIndex-1, rowIndex-1, 0, 7)); + } + + Row remarkRow = sheet.createRow(rowIndex++); + remarkRow.setHeightInPoints(25); + // 为所有列创建单元格并应用背景色,避免右下角出现缺口 + for (int i = 0; i <= 7; i++) { + Cell cell = remarkRow.createCell(i); + if (i == 0) { + cell.setCellValue("(注:当日菜品以实物为准)"); + } + cell.setCellStyle(getContentCellStyle(workbook)); + } + sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(rowIndex-1, rowIndex-1, 0, 7)); + } + + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment; filename=menu_export_" + new Date().getTime() + ".xlsx"); + + // 输出文件 + OutputStream outputStream = response.getOutputStream(); + workbook.write(outputStream); + outputStream.flush(); + outputStream.close(); + workbook.close(); + } + + /** + * 获取星期几 + */ + private String getWeekday(Date date) { + String[] weekdays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"}; + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int day = calendar.get(Calendar.DAY_OF_WEEK) - 1; + return weekdays[day]; + } + + /** + * 设置单元格背景色为钢蓝色 #4A80B9 + * @param style 单元格样式 + * @param workbook 工作簿 + */ + private void setSteelBlueBackground(CellStyle style, Workbook workbook) { + if (workbook instanceof XSSFWorkbook) { + // POI 3.17 版本的 XSSFColor 构造方式 + XSSFColor customColor = new XSSFColor(new java.awt.Color(74, 128, 185)); + XSSFCellStyle xssfStyle = (XSSFCellStyle) style; + xssfStyle.setFillForegroundColor(customColor); + } else if (workbook instanceof HSSFWorkbook) { + // POI 3.17 版本的 HSSFColor 设置方式 + HSSFWorkbook hssfWorkbook = (HSSFWorkbook) workbook; + HSSFPalette palette = hssfWorkbook.getCustomPalette(); + HSSFColor customColor = palette.findColor((byte) 74, (byte) 128, (byte) 185); + if (customColor == null) { + customColor = palette.addColor((byte) 74, (byte) 128, (byte) 185); + } + HSSFCellStyle hssfStyle = (HSSFCellStyle) style; + hssfStyle.setFillForegroundColor(customColor.getIndex()); + } else { + // 其他类型工作簿使用内置颜色 + style.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex()); + } + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + } + + /** + * 获取标题单元格样式 + */ + private CellStyle getTitleCellStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + // 添加边框 + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + Font font = workbook.createFont(); + font.setFontName("宋体"); + font.setFontHeightInPoints((short) 16); + font.setBold(true); + style.setFont(font); + // 使用钢蓝色 #4A80B9 + setSteelBlueBackground(style, workbook); + font.setColor(IndexedColors.WHITE.getIndex()); + return style; + } + + /** + * 获取表头单元格样式 + */ + private CellStyle getHeaderCellStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + // 添加边框 + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + Font font = workbook.createFont(); + font.setFontName("宋体"); + font.setFontHeightInPoints((short) 12); + font.setBold(true); + style.setFont(font); + // 使用钢蓝色 #4A80B9 + setSteelBlueBackground(style, workbook); + font.setColor(IndexedColors.WHITE.getIndex()); + return style; + } + + /** + * 获取内容单元格样式 + */ + private CellStyle getContentCellStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + style.setAlignment(HorizontalAlignment.LEFT); + style.setVerticalAlignment(VerticalAlignment.CENTER); + // 添加边框 + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + Font font = workbook.createFont(); + font.setFontName("宋体"); + font.setFontHeightInPoints((short) 11); + style.setFont(font); + // 使用钢蓝色 #4A80B9 + setSteelBlueBackground(style, workbook); + font.setColor(IndexedColors.WHITE.getIndex()); + return style; + } + + /** + * 获取数据单元格样式 + */ + private CellStyle getDataCellStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + style.setAlignment(HorizontalAlignment.LEFT); + style.setVerticalAlignment(VerticalAlignment.CENTER); + // 添加边框 + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + Font font = workbook.createFont(); + font.setFontName("宋体"); + font.setFontHeightInPoints((short) 11); + style.setFont(font); + // 使用钢蓝色 #4A80B9 + setSteelBlueBackground(style, workbook); + font.setColor(IndexedColors.WHITE.getIndex()); + return style; + } + + /** + * 导入菜单 + * @param file Excel文件 + */ + @PostMapping("/import") + public ResponseEntity> importMenu(@RequestParam("file") MultipartFile file) { + try { + // 验证文件 + if (file == null || file.isEmpty()) { + return ResponseEntity.ok(CommonResult.failed("文件不能为空")); + } + + // 读取Excel文件 + Workbook workbook = WorkbookFactory.create(file.getInputStream()); + Sheet sheet = workbook.getSheetAt(0); + + // 开始处理数据 + int successCount = 0; + int errorCount = 0; + + // 从第二行开始读取数据(第一行是表头) + for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) { + Row row = sheet.getRow(rowNum); + if (row == null) { + continue; + } + + try { + // 读取数据 + String menuDate = getCellValue(row.getCell(0)); + String canteenName = getCellValue(row.getCell(1)); + String mealType = getCellValue(row.getCell(2)); + String itemName = getCellValue(row.getCell(3)); + String itemType = getCellValue(row.getCell(4)); + String itemPriceStr = getCellValue(row.getCell(5)); + + // 翻译菜品类型(中文名称转字典键值) + itemType = translateItemType(itemType); + + // 翻译餐别(中文名称转字典键值) + mealType = translateMealType(mealType); + + // 翻译食堂名称(中文名称转字典键值) + canteenName = translateCanteenName(canteenName); + + // 验证必填字段 + if (StringUtils.isEmpty(menuDate) || StringUtils.isEmpty(canteenName) || + StringUtils.isEmpty(mealType) || StringUtils.isEmpty(itemName) || + StringUtils.isEmpty(itemType) || StringUtils.isEmpty(itemPriceStr)) { + errorCount++; + continue; + } + + // 解析价格 + BigDecimal itemPrice = new BigDecimal(itemPriceStr); + + // 查找或创建菜单 + PmDailyMenu menu = pmDailyMenuService.findByDateAndCanteenAndMealType(menuDate, canteenName, mealType); + if (menu == null) { + menu = new PmDailyMenu(); + menu.setMenuDate(new java.text.SimpleDateFormat("yyyy-MM-dd").parse(menuDate)); + menu.setCanteenName(canteenName); + menu.setMealType(mealType); + menu.setStatus("0"); // 未发布 + pmDailyMenuService.save(menu); + } + + // 创建菜品明细 + PmDailyMenuDtl dtl = new PmDailyMenuDtl(); + dtl.setMenuId(menu.getId()); + dtl.setItemName(itemName); + dtl.setItemType(itemType); + dtl.setItemPrice(itemPrice); + pmDailyMenuDtlService.save(dtl); + + successCount++; + } catch (Exception e) { + errorCount++; + } + } + + workbook.close(); + + return ResponseEntity.ok(CommonResult.success("导入成功,成功:" + successCount + ",失败:" + errorCount)); + } catch (Exception e) { + return ResponseEntity.ok(CommonResult.failed("导入失败:" + e.getMessage())); + } + } + + /** + * 获取单元格值 + */ + private String getCellValue(Cell cell) { + if (cell == null) { + return ""; + } + + switch (cell.getCellTypeEnum()) { + case STRING: + return cell.getStringCellValue().trim(); + case NUMERIC: + if (DateUtil.isCellDateFormatted(cell)) { + return new java.text.SimpleDateFormat("yyyy-MM-dd").format(cell.getDateCellValue()); + } else { + return String.valueOf(cell.getNumericCellValue()); + } + case BOOLEAN: + return String.valueOf(cell.getBooleanCellValue()); + case FORMULA: + try { + return String.valueOf(cell.getNumericCellValue()); + } catch (Exception e) { + return cell.getStringCellValue(); + } + default: + return ""; + } + } + + /** + * 翻译菜品类型(中文名称转字典键值) + */ + private String translateItemType(String itemType) { + if (StringUtils.isEmpty(itemType)) { + return itemType; + } + + // 从系统字典获取菜品类型映射 + List dictDataList = sysDictDataService.getByDictType("item_type"); + Map itemTypeMap = new HashMap<>(); + for (SysDictData dictData : dictDataList) { + itemTypeMap.put(dictData.getDictLabel(), dictData.getDictValue()); + } + + // 如果是中文名称,返回对应的字典键值 + if (itemTypeMap.containsKey(itemType)) { + return itemTypeMap.get(itemType); + } + + // 否则返回原值(可能已经是字典键值) + return itemType; + } + + /** + * 翻译餐别(中文名称转字典键值) + */ + private String translateMealType(String mealType) { + if (StringUtils.isEmpty(mealType)) { + return mealType; + } + + // 从系统字典获取餐别映射 + List dictDataList = sysDictDataService.getByDictType("meal_type"); + Map mealTypeMap = new HashMap<>(); + for (SysDictData dictData : dictDataList) { + mealTypeMap.put(dictData.getDictLabel(), dictData.getDictValue()); + } + + // 如果是中文名称,返回对应的字典键值 + if (mealTypeMap.containsKey(mealType)) { + return mealTypeMap.get(mealType); + } + + // 否则返回原值(可能已经是字典键值) + return mealType; + } + + /** + * 翻译食堂名称(中文名称转字典键值) + */ + private String translateCanteenName(String canteenName) { + if (StringUtils.isEmpty(canteenName)) { + return canteenName; + } + + // 从系统字典获取食堂名称映射 + List dictDataList = sysDictDataService.getByDictType("canteen_name"); + Map canteenNameMap = new HashMap<>(); + for (SysDictData dictData : dictDataList) { + canteenNameMap.put(dictData.getDictLabel(), dictData.getDictValue()); + } + + // 如果是中文名称,返回对应的字典键值 + if (canteenNameMap.containsKey(canteenName)) { + return canteenNameMap.get(canteenName); + } + + // 没有对应的情况下,取第一个字典值 + if (!dictDataList.isEmpty()) { + return dictDataList.get(0).getDictValue(); + } + + // 否则返回原值 + return canteenName; + } + + /** + * 下载导入模板 + * @param response 响应 + */ + @GetMapping("/import/template") + public void importTemplate(HttpServletResponse response) { + try { + // 创建Excel工作簿 + Workbook workbook = new XSSFWorkbook(); + Sheet sheet = workbook.createSheet("菜单导入模板"); + + // 创建表头 + Row headerRow = sheet.createRow(0); + String[] headers = {"日期", "食堂名称", "餐别", "菜品名称", "菜品类型", "菜品价格"}; + for (int i = 0; i < headers.length; i++) { + Cell cell = headerRow.createCell(i); + cell.setCellValue(headers[i]); + // 设置表头样式 + CellStyle style = workbook.createCellStyle(); + Font font = workbook.createFont(); + font.setFontName("宋体"); + font.setFontHeightInPoints((short) 12); + font.setBold(true); + style.setFont(font); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + // 添加边框 + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + cell.setCellStyle(style); + } + + // 设置列宽 + sheet.setColumnWidth(0, 15 * 256); + sheet.setColumnWidth(1, 15 * 256); + sheet.setColumnWidth(2, 10 * 256); + sheet.setColumnWidth(3, 20 * 256); + sheet.setColumnWidth(4, 15 * 256); + sheet.setColumnWidth(5, 10 * 256); + + + + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment; filename=菜单导入模板.xlsx"); + + // 输出文件 + OutputStream outputStream = response.getOutputStream(); + workbook.write(outputStream); + outputStream.flush(); + outputStream.close(); + workbook.close(); + } catch (Exception e) { + } + } + } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmDailyMenuDtlLikeController.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmDailyMenuDtlLikeController.java index dc90886..6fab293 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmDailyMenuDtlLikeController.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmDailyMenuDtlLikeController.java @@ -18,6 +18,7 @@ import com.zbkj.modules.autogencode.entity.PmDailyMenu; import com.zbkj.modules.autogencode.service.PmDailyMenuService; import com.zbkj.modules.autogencode.entity.PmDailyMenuDtl; import com.zbkj.modules.autogencode.service.PmDailyMenuDtlService; +import com.zbkj.service.service.SystemAdminService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -48,7 +49,8 @@ public class PmDailyMenuDtlLikeController { @Autowired private PmDailyMenuService pmDailyMenuService; - + @Autowired + private SystemAdminService systemAdminService; /** @@ -134,6 +136,26 @@ public class PmDailyMenuDtlLikeController { condition(queryWrapper, request); CommonPage page = CommonPage.restPage(pmDailyMenuDtlLikeService.pageList(queryWrapper, pageParamRequest)); + + // 这里进行翻译,是通过systemAdminService进行翻译 + if (page != null && page.getList() != null) { + for (PmDailyMenuDtlLike item : page.getList()) { + if (item.getUserId() != null) { + try { + // 通过systemAdminService根据userId获取用户信息 + // 假设SystemAdmin实体有getName()方法获取用户名称 + com.zbkj.common.model.system.SystemAdmin admin = systemAdminService.getById(item.getUserId()); + if (admin != null) { + // 将用户名称设置到PmDailyMenuDtlLike的某个字段中 + // 假设PmDailyMenuDtlLike有setUserName方法 + item.setUserName(admin.getRealName()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } return CommonResult.success(page); } @@ -186,10 +208,11 @@ public class PmDailyMenuDtlLikeController { * @param menuDtlId 菜单明细ID * @param likeType 评价类型:1-点赞,2-点踩 * @param uid 手机端用户ID(可选,如果提供则使用该ID,否则使用当前登录用户ID) + * @param remark 备注(可选) */ @ApiOperation(value = "对菜单明细进行点赞/点踩") @RequestMapping(value = "/like", method = RequestMethod.POST) - public CommonResult likeOrDislike(@RequestParam Long menuDtlId, @RequestParam String likeType, @RequestParam(required = false) Long uid) { + public CommonResult likeOrDislike(@RequestParam Long menuDtlId, @RequestParam String likeType, @RequestParam(required = false) Long uid, @RequestParam(required = false) String remark) { try { // 获取当前登录用户ID LoginUserVo loginUserVo = SecurityUtil.getLoginUserVo(); @@ -247,6 +270,7 @@ public class PmDailyMenuDtlLikeController { like.setUserId(userId); like.setMenuDate(menuDate); like.setLikeType(likeType); + like.setRemark(remark); like.setDelFlag("0"); like.setCreateBy(userId); like.setCreateTime(new Date()); diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmMaintenanceDispatchController.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmMaintenanceDispatchController.java index c3e1ceb..1a5eec7 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmMaintenanceDispatchController.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmMaintenanceDispatchController.java @@ -201,6 +201,7 @@ public class PmMaintenanceDispatchController { // 应用搜索条件 condition(queryWrapper, request); + queryWrapper.orderByDesc(PmMaintenanceDispatch::getAssignTime); CommonPage page = CommonPage.restPage(pmMaintenanceDispatchService.pageList(queryWrapper, pageParamRequest)); @@ -233,6 +234,8 @@ public class PmMaintenanceDispatchController { // 部门ID格式错误,忽略 } } + // 设置文件信息 + setDispatchFile(item); } return CommonResult.success(page); diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmMaintenanceOrderController.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmMaintenanceOrderController.java index 39506b9..bd4f7ef 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmMaintenanceOrderController.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/controller/PmMaintenanceOrderController.java @@ -17,10 +17,19 @@ import com.zbkj.common.response.CommonResult; import com.zbkj.common.page.CommonPage; import com.zbkj.common.utils.SecurityUtil; import com.zbkj.common.vo.LoginUserVo; +import com.zbkj.common.constants.Constants; +import com.zbkj.common.constants.UserConstants; +import com.zbkj.common.utils.DateUtil; import com.zbkj.service.service.SysDeptService; import com.zbkj.service.service.SystemAdminService; import com.zbkj.service.service.UserService; import com.zbkj.common.model.user.User; +import com.zbkj.common.constants.NotifyConstants; +import com.zbkj.service.service.TemplateMessageService; +import com.zbkj.service.service.SystemNotificationService; +import com.zbkj.service.service.UserTokenService; +import com.zbkj.common.model.system.SystemNotification; +import com.zbkj.common.model.user.UserToken; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -86,6 +95,12 @@ public class PmMaintenanceOrderController { private PmTenantHouseRelService pmTenantHouseRelService; @Autowired private SysDeptService sysDeptService; + @Autowired + private TemplateMessageService templateMessageService; + @Autowired + private SystemNotificationService systemNotificationService; + @Autowired + private UserTokenService userTokenService; /** @@ -489,9 +504,9 @@ public class PmMaintenanceOrderController { } // 办结时检查处理后文件 - if ("99".equals(pmMaintenanceOrder.getStatus()) && (pmMaintenanceOrder.getAfterProcessFiles() == null || pmMaintenanceOrder.getAfterProcessFiles().isEmpty())) { - return CommonResult.failed("办结时必须上传修复凭证"); - } +// if ("99".equals(pmMaintenanceOrder.getStatus()) && (pmMaintenanceOrder.getAfterProcessFiles() == null || pmMaintenanceOrder.getAfterProcessFiles().isEmpty())) { +// return CommonResult.failed("办结时必须上传修复凭证"); +// } if (pmMaintenanceOrderService.save(pmMaintenanceOrder)) { // 获取保存后的订单ID @@ -507,6 +522,9 @@ public class PmMaintenanceOrderController { return CommonResult.failed(); } + + // 写一个方法, 这里进行推送,首先查出对应的角色,然后对应的用户 + /** * 修改数据 */ @@ -518,9 +536,14 @@ public class PmMaintenanceOrderController { } // 办结时检查处理后文件 - if ("99".equals(pmMaintenanceOrder.getStatus()) && (pmMaintenanceOrder.getAfterProcessFiles() == null || pmMaintenanceOrder.getAfterProcessFiles().isEmpty())) { - return CommonResult.failed("办结时必须上传修复凭证"); - } +// if ("99".equals(pmMaintenanceOrder.getStatus()) && (pmMaintenanceOrder.getAfterProcessFiles() == null || pmMaintenanceOrder.getAfterProcessFiles().isEmpty())) { +// return CommonResult.failed("办结时必须上传修复凭证"); +// } + + // 记录原状态 + PmMaintenanceOrder oldOrder = pmMaintenanceOrderService.getById(pmMaintenanceOrder.getId()); + String oldStatus = oldOrder != null ? oldOrder.getStatus() : null; + String newStatus = pmMaintenanceOrder.getStatus(); if (pmMaintenanceOrderService.updateById(pmMaintenanceOrder)) { // 获取订单ID @@ -530,6 +553,11 @@ public class PmMaintenanceOrderController { // 处理报修派单记录列表 handleDispatchRecords(orderId, dispatchList); + + // 当从草稿(1)修改为待处理(0)时,发送推送通知 + if ("1".equals(oldStatus) && "0".equals(newStatus)) { + sendMaintenanceNotification(pmMaintenanceOrder); + } return CommonResult.success(); } @@ -575,11 +603,53 @@ public class PmMaintenanceOrderController { } } + + /** - * 处理报修派单记录列表 - * @param orderId 订单ID - * @param dispatchList 报修派单记录列表 + * 发送报修通知 + * @param pmMaintenanceOrder 报修订单 */ + private void sendMaintenanceNotification(PmMaintenanceOrder pmMaintenanceOrder) { + // 发送用户通知 + SystemNotification userNotification = systemNotificationService.getByMark(NotifyConstants.MAINTENANCE_NOTICE_MARK); + if (userNotification != null && userNotification.getIsWechat().equals(1)) { + // 获取用户的微信openid + UserToken userToken = userTokenService.getTokenByUserId(Math.toIntExact(pmMaintenanceOrder.getUid()), UserConstants.USER_TOKEN_TYPE_WECHAT); + if (userToken != null) { + // 构建通知内容 + HashMap temMap = new HashMap<>(); + temMap.put(Constants.WE_CHAT_TEMP_KEY_FIRST, "您的报修申请已提交成功!"); + temMap.put("keyword1", pmMaintenanceOrder.getOrderNo()); + temMap.put("keyword2", pmMaintenanceOrder.getFaultType()); + temMap.put("keyword3", pmMaintenanceOrder.getUrgencyLevel()); + temMap.put("keyword4", DateUtil.nowDateTimeStr()); + temMap.put(Constants.WE_CHAT_TEMP_KEY_END, "我们将尽快处理您的报修申请,请耐心等待。"); + // 发送微信模板消息 + templateMessageService.pushTemplateMessage(userNotification.getWechatId(), temMap, userToken.getToken()); + } + } + + // 发送管理员通知 + SystemNotification adminNotification = systemNotificationService.getByMark(NotifyConstants.MAINTENANCE_NOTICE_ADMIN_MARK); + if (adminNotification != null && adminNotification.getIsWechat().equals(1)) { + // 这里可以获取管理员的openid,假设系统有默认管理员或者根据部门获取 + // 暂时使用用户的openid作为示例 + UserToken userToken = userTokenService.getTokenByUserId(Math.toIntExact(pmMaintenanceOrder.getUid()), UserConstants.USER_TOKEN_TYPE_WECHAT); + if (userToken != null) { + // 构建通知内容 + HashMap temMap = new HashMap<>(); + temMap.put(Constants.WE_CHAT_TEMP_KEY_FIRST, "收到新的报修申请!"); + temMap.put("keyword1", pmMaintenanceOrder.getOrderNo()); + temMap.put("keyword2", pmMaintenanceOrder.getFaultType()); + temMap.put("keyword3", pmMaintenanceOrder.getUrgencyLevel()); + temMap.put("keyword4", DateUtil.nowDateTimeStr()); + temMap.put(Constants.WE_CHAT_TEMP_KEY_END, "请及时处理该报修申请。"); + // 发送微信模板消息 + templateMessageService.pushTemplateMessage(adminNotification.getWechatId(), temMap, userToken.getToken()); + } + } + } + private void handleDispatchRecords(Long orderId, List dispatchList) { // 处理报修派单记录列表 if (dispatchList != null && !dispatchList.isEmpty()) { @@ -637,16 +707,23 @@ public class PmMaintenanceOrderController { // 这里需要从前端传递修复照片,或者在前端进行检查 // 由于此接口主要用于状态和备注的快速修改,建议在前端进行修复照片的检查 // 如果前端没有传递修复照片,则返回错误 - if (params.get("afterProcessFiles") == null) { - return CommonResult.failed("办结时必须上传修复凭证"); - } +// if (params.get("afterProcessFiles") == null) { +// return CommonResult.failed("办结时必须上传修复凭证"); +// } } + // 记录原状态 + String oldStatus = pmMaintenanceOrder.getStatus(); + // 只修改状态和备注 pmMaintenanceOrder.setStatus(status); pmMaintenanceOrder.setRemark(remark); if (pmMaintenanceOrderService.updateById(pmMaintenanceOrder)) { + // 当从草稿(1)修改为待处理(0)时,发送推送通知 + if ("1".equals(oldStatus) && "0".equals(status)) { + sendMaintenanceNotification(pmMaintenanceOrder); + } return CommonResult.success(); } return CommonResult.failed(); diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/CkBillCargo.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/CkBillCargo.java index 73a65e1..f9e95ed 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/CkBillCargo.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/CkBillCargo.java @@ -206,6 +206,8 @@ public class CkBillCargo implements Serializable { * 有效期至 */ @ApiModelProperty(value = "有效期至") + @JsonFormat(pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") private Date expiryDate; /** * 备注 @@ -428,6 +430,37 @@ public class CkBillCargo implements Serializable { @TableField(exist = false) private String receiverName; + /** + * 分类ID + */ + @ApiModelProperty(value = "分类ID") + @TableField(exist = false) + private Long classId; + + /** + * 分类名称 + */ + @ApiModelProperty(value = "分类名称") + @TableField(exist = false) + private String className; + + /** + * 分类代码 + */ + @ApiModelProperty(value = "分类代码") + @TableField(exist = false) + private String classCode; + + @TableField(exist = false) + private String unitName; + + /** + * 分类ID列表(用于多选查询,逗号分隔) + */ + @ApiModelProperty(value = "分类ID列表(逗号分隔)") + @TableField(exist = false) + private String classIds; + } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/CkCargoStock.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/CkCargoStock.java index d77f262..7c973dc 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/CkCargoStock.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/CkCargoStock.java @@ -210,6 +210,35 @@ public class CkCargoStock implements Serializable { @ApiModelProperty(value = "货位ID") @TableField(exist = false) private Long locationId; + + /** + * 分类ID + */ + @ApiModelProperty(value = "分类ID") + @TableField(exist = false) + private Long classId; + + /** + * 分类名称 + */ + @ApiModelProperty(value = "分类名称") + @TableField(exist = false) + private String className; + + /** + * 类别代码 + */ + @ApiModelProperty(value = "类别代码") + @TableField(exist = false) + private String classCode; + + /** + * 单位 + */ + @ApiModelProperty(value = "单位") + @TableField(exist = false) + private String unit; + /** * 备注 */ diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/CkStockChange.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/CkStockChange.java index d783532..0ffcbe2 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/CkStockChange.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/CkStockChange.java @@ -1,5 +1,6 @@ package com.zbkj.modules.autogencode.entity; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.annotations.ApiModelProperty; @@ -46,6 +47,18 @@ public class CkStockChange implements Serializable { */ @ApiModelProperty(value = "货品名称") private String cargoName; + /** + * 物品类别 + */ + @ApiModelProperty(value = "物品类别") + @TableField(exist = false) + private String cargoCategory; + /** + * 规格型号 + */ + @ApiModelProperty(value = "规格型号") + @TableField(exist = false) + private String cargoSpec; /** * 仓库 */ diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/PmDailyMenuDtlLike.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/PmDailyMenuDtlLike.java index ad313e5..f15d196 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/PmDailyMenuDtlLike.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/PmDailyMenuDtlLike.java @@ -114,4 +114,17 @@ public class PmDailyMenuDtlLike implements Serializable { @TableField(exist = false) private Integer totalCount; + /** + * 备注 + */ + @ApiModelProperty(value = "备注") + private String remark; + + /** + * 用户名称(虚拟字段) + */ + @ApiModelProperty(value = "用户名称") + @TableField(exist = false) + private String userName; + } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/vo/ImportItemVO.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/vo/ImportItemVO.java new file mode 100644 index 0000000..e34af22 --- /dev/null +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/entity/vo/ImportItemVO.java @@ -0,0 +1,23 @@ +package com.zbkj.modules.autogencode.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ImportItemVO { + private String orderNo; // 单据编号 XSDD-20260403-0424 + private String orderDate; // 2026-04-03 + private String customer; // 福州市八方物业管理有限公司 + private String address; // 港口集团食堂 + + private String seq; // 序号 + private String goodsName; // 商品名称 + private String spec; // 规格型号 + private String unit; // 单位 + private BigDecimal qty; // 数量 + private BigDecimal price; // 单价 + private BigDecimal amount; // 金额 + private String remark; // 备注 +} + diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/CkStockChangeService.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/CkStockChangeService.java index fc19a6f..927d952 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/CkStockChangeService.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/CkStockChangeService.java @@ -3,6 +3,8 @@ package com.zbkj.modules.autogencode.service; import com.baomidou.mybatisplus.extension.service.IService; import com.zbkj.modules.autogencode.entity.CkStockChange; import com.zbkj.common.request.PageParamRequest; +import com.zbkj.common.request.InventoryRequest; +import com.zbkj.common.response.InventoryResponse; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -21,5 +23,18 @@ public interface CkStockChangeService extends IService { * @return */ List pageList(LambdaQueryWrapper queryWrapper, PageParamRequest pageParamRequest); + + /** + * 查询物资盘点表数据 + * @param request 查询条件 + * @return 物资盘点表数据列表 + */ + List getInventoryList(InventoryRequest request); + + /** + * 导出物资盘点表数据 + * @param request 查询条件 + */ + void exportInventory(InventoryRequest request); } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/PmDailyMenuService.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/PmDailyMenuService.java index 711ac9f..9658bec 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/PmDailyMenuService.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/PmDailyMenuService.java @@ -21,5 +21,14 @@ public interface PmDailyMenuService extends IService { * @return */ List pageList(LambdaQueryWrapper queryWrapper, PageParamRequest pageParamRequest); + + /** + * 根据日期、食堂、餐别查找菜单 + * @param menuDate 日期 + * @param canteenName 食堂名称 + * @param mealType 餐别 + * @return 菜单 + */ + PmDailyMenu findByDateAndCanteenAndMealType(String menuDate, String canteenName, String mealType); } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/SysDictDataService.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/SysDictDataService.java index e30c6fd..0bc205b 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/SysDictDataService.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/SysDictDataService.java @@ -21,5 +21,12 @@ public interface SysDictDataService extends IService { * @return */ List pageList(LambdaQueryWrapper queryWrapper, PageParamRequest pageParamRequest); + + /** + * 根据字典类型获取字典数据列表 + * @param dictType 字典类型 + * @return 字典数据列表 + */ + List getByDictType(String dictType); } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/CkBillServiceImpl.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/CkBillServiceImpl.java index ea7aebf..e54e540 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/CkBillServiceImpl.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/CkBillServiceImpl.java @@ -6,11 +6,16 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zbkj.common.model.system.SysDept; +import com.zbkj.common.model.system.SystemAdmin; import com.zbkj.common.response.CommonResult; import com.zbkj.common.utils.SecurityUtil; import com.zbkj.common.vo.LoginUserVo; import com.zbkj.modules.autogencode.entity.*; import com.zbkj.modules.autogencode.service.*; +import com.zbkj.modules.autogencode.entity.PmProductClass; +import com.zbkj.service.service.SysDeptService; +import com.zbkj.service.service.SystemAdminService; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; import org.springframework.beans.BeanUtils; @@ -55,6 +60,9 @@ public class CkBillServiceImpl extends ServiceImpl implements @Resource private CmWarehouseService cmWarehouseService; + + @Resource + private PmProductClassService pmProductClassService; @Resource private CmShelfService cmShelfService; @@ -76,6 +84,12 @@ public class CkBillServiceImpl extends ServiceImpl implements @Resource private CkBillNumberService ckBillNumberService; + + @Resource + private SysDeptService sysDeptService; + + @Resource + private SystemAdminService systemAdminService; /** * 带分页参数的列表查询实现 */ @@ -175,6 +189,11 @@ public class CkBillServiceImpl extends ServiceImpl implements stockWrapper.eq(CkCargoStock::getStockId,ckBillStock.getStockId()); stockWrapper.eq(CkCargoStock::getBillNumber,ckBillStock.getBillNumber()); CkCargoStock ckCargoStock = ckCargoStockService.getOne(stockWrapper); + // 检查库存记录是否存在 + if (ckCargoStock == null) { + TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚事务 + throw new RuntimeException("库存记录不存在"); + } ckCargoStock.setCargoNum(ckCargoStock.getCargoNum().add(ckBillStock.getCargoNum())); ckCargoStock.setCargoWt(ckCargoStock.getCargoWt().add(ckBillStock.getCargoWt())); ckCargoStock.setCargoVol(ckCargoStock.getCargoVol().add(ckBillStock.getCargoVol())); @@ -199,6 +218,73 @@ public class CkBillServiceImpl extends ServiceImpl implements wapper.eq(CkBillCargo::getBillId,ckBill.getId()); //单据明细 List ckBillCargos = ckBillCargoService.list(wapper); + + // 如果autoOut为1,准备生成出库单据 + CkBill outBill = null; + List outCargoList = null; + if ("1".equals(ckBill.getAutoOut())) { + // 生成一个出库单据 + outBill = new CkBill(); + outBill.setBillNumber(ckBillNumberService.getBillNumber("CK")); + outBill.setBillDate(new Date()); + outBill.setBillType("2"); // 出库 + outBill.setStatus("1"); + outBill.setBillStatus("0"); + outBill.setSourceType("1"); + outBill.setAuditStatus("0"); + outBill.setCancelStatus("0"); + // 使用第一个入库明细的货主信息 + if (ckBillCargos != null && !ckBillCargos.isEmpty()) { + CkBillCargo firstCargo = ckBillCargos.get(0); + outBill.setCustId(firstCargo.getCustId()); + outBill.setCustName(firstCargo.getCustName()); + } + outBill.setVoyageNo(ckBill.getVoyageNo()); + outBill.setVesselName(ckBill.getVesselName()); + outBill.setVesselId(ckBill.getVesselId()); + outBill.setAutoOut("0"); // 出库单据不需要自动出库 + outBill.setSubmitStatus(false); // 设置提交状态为false + + // 设置领用部门和领用人信息(从原单据中获取) + outBill.setReceiverDeptId(ckBill.getReceiverDeptId()); + outBill.setReceiverId(ckBill.getReceiverId()); + + // 如果原单据没有设置领用部门和领用人,从部门表和用户表中获取默认值 + if (outBill.getReceiverDeptId() == null || outBill.getReceiverId() == null) { + // 从部门表中获取默认领用部门 + if (outBill.getReceiverDeptId() == null && sysDeptService != null) { + try { + // 查询isReceiveDept为1的部门作为默认领用部门 + LambdaQueryWrapper deptWrapper = new LambdaQueryWrapper<>(); + deptWrapper.eq(SysDept::getIsReceiveDept, "1"); + List deptList = sysDeptService.list(deptWrapper); + if (deptList != null && !deptList.isEmpty()) { + outBill.setReceiverDeptId(deptList.get(0).getDeptId()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // 从用户表中获取默认领用人 + if (outBill.getReceiverId() == null && systemAdminService != null) { + try { + // 查询isDefaultReceiver为1的用户作为默认领用人 + LambdaQueryWrapper userWrapper = new LambdaQueryWrapper<>(); + userWrapper.eq(SystemAdmin::getIsDefaultReceiver, "1"); + List userList = systemAdminService.list(userWrapper); + if (userList != null && !userList.isEmpty()) { + outBill.setReceiverId(Long.valueOf(userList.get(0).getId())); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + // 创建出库明细列表 + outCargoList = new ArrayList<>(); + } for (int i = 0; i < ckBillCargos.size(); i++) { CkBillCargo ckBillCargo = ckBillCargos.get(i); ckBillCargo.setBillStatus("1"); @@ -233,51 +319,63 @@ public class CkBillServiceImpl extends ServiceImpl implements saveChange(ckBillCargo,ckBillStock); }); - // 如果autoOut为1,自动生成出库单据 直接用outCargo这个方法 - if ("1".equals(ckBill.getAutoOut())) { + // 如果autoOut为1,为每个库存记录创建出库明细 + if ("1".equals(ckBill.getAutoOut()) && outCargoList != null) { + // 为每个库存记录创建一个出库明细 for (CkBillStock ckBillStock : ckBillStocks) { - // 生成出库单据 - CkBill outBill = new CkBill(); - outBill.setBillNumber(ckBillNumberService.getBillNumber("CK")); - outBill.setBillDate(new Date()); - outBill.setBillType("2"); // 出库 - outBill.setStatus("1"); - outBill.setBillStatus("0"); - outBill.setSourceType("1"); - outBill.setAuditStatus("0"); - outBill.setCancelStatus("0"); - outBill.setCustId(ckBillCargo.getCustId()); - outBill.setCustName(ckBillCargo.getCustName()); - outBill.setVoyageNo(ckBill.getVoyageNo()); - outBill.setVesselName(ckBill.getVesselName()); - outBill.setVesselId(ckBill.getVesselId()); - outBill.setAutoOut("0"); // 出库单据不需要自动出库 - outBill.setSubmitStatus(false); // 设置提交状态为false + // 检查仓库和分类是否需要自动出库 + boolean needAutoOut = false; - // 创建出库明细 - CkBillCargo outCargo = new CkBillCargo(); - BeanUtils.copyProperties(ckBillCargo, outCargo, "id", "billId", "billStatus", "inoutType"); - outCargo.setBillStatus("0"); - outCargo.setInoutType("2"); // 出库 - outCargo.setSourceType("1"); - outCargo.setOutStockId(ckBillStock.getStockId()); - outCargo.setOutBillNumber(ckBillStock.getBillNumber()); - // 设置数量字段,从库存记录中获取 - outCargo.setCargoNum(ckBillStock.getCargoNum()); - outCargo.setCargoWt(ckBillStock.getCargoWt()); - outCargo.setCargoVol(ckBillStock.getCargoVol()); - outCargo.setCargoValue(ckBillStock.getCargoValue()); - outCargo.setVoyageNo(ckBill.getVoyageNo()); - outCargo.setVesselName(ckBill.getVesselName()); - outCargo.setVesselId(ckBill.getVesselId()); + // 检查仓库是否需要自动出库 + CmWarehouse warehouse = cmWarehouseService.getById(ckBillStock.getStockId()); + if (warehouse != null && "1".equals(warehouse.getAutoOut())) { + needAutoOut = true; + } - // 设置出库单据的明细列表 - List outCargoList = new ArrayList<>(); - outCargoList.add(outCargo); - outBill.setCkBillCargos(outCargoList); + // 检查分类是否需要自动出库 + if (!needAutoOut) { + // 首先通过cargoId查询CmCustProduct,获取分类ID + if (ckBillCargo.getCargoId() != null) { + CmCustProduct cmCustProduct = cmCustProductService.getById(ckBillCargo.getCargoId()); + if (cmCustProduct != null && cmCustProduct.getClassId() != null) { + // 然后通过分类ID查询PmProductClass,获取autoOut字段 + Long classId = cmCustProduct.getClassId(); + try { + if (pmProductClassService != null) { + PmProductClass productClass = pmProductClassService.getById(classId); + if (productClass != null && "1".equals(productClass.getAutoOut())) { + needAutoOut = true; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } - // 调用outCargo方法处理出库 - this.outCargo(outBill); + // 只要仓库或分类中有一个需要自动出库,就生成出库明细 + if (needAutoOut) { + // 创建出库明细 + CkBillCargo outCargo = new CkBillCargo(); + BeanUtils.copyProperties(ckBillCargo, outCargo, "id", "billId", "billStatus", "inoutType"); + outCargo.setBillStatus("0"); + outCargo.setInoutType("2"); // 出库 + outCargo.setSourceType("1"); + outCargo.setOutStockId(ckBillStock.getStockId()); + outCargo.setOutBillNumber(ckBillStock.getBillNumber()); + // 设置数量字段,从库存记录中获取 + outCargo.setCargoNum(ckBillStock.getCargoNum()); + outCargo.setCargoWt(ckBillStock.getCargoWt()); + outCargo.setCargoVol(ckBillStock.getCargoVol()); + outCargo.setCargoValue(ckBillStock.getCargoValue()); + outCargo.setVoyageNo(ckBill.getVoyageNo()); + outCargo.setVesselName(ckBill.getVesselName()); + outCargo.setVesselId(ckBill.getVesselId()); + + // 添加到明细列表 + outCargoList.add(outCargo); + } } } }else{ @@ -285,11 +383,15 @@ public class CkBillServiceImpl extends ServiceImpl implements for (int j = 0; j < ckBillStocks.size(); j++) { CkBillStock ckBillStock = ckBillStocks.get(j); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(CkCargoStock::getCustId,ckBillCargo.getCustId()); wrapper.eq(CkCargoStock::getCargoId,ckBillCargo.getCargoId()); wrapper.eq(CkCargoStock::getStockId,ckBillStock.getStockId()); wrapper.eq(CkCargoStock::getBillNumber,ckBillStock.getBillNumber()); CkCargoStock ckCargoStock = ckCargoStockService.getOne(wrapper); + // 检查库存记录是否存在 + if (ckCargoStock == null) { + TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚事务 + return CommonResult.failed("库存记录不存在"); + } Boolean flag = false; if(ckCargoStock.getCargoNum().compareTo(ckBillStock.getCargoNum())>=0){ flag = true; @@ -321,6 +423,29 @@ public class CkBillServiceImpl extends ServiceImpl implements } } } + + // 如果autoOut为1,调用outCargo方法处理出库 + if ("1".equals(ckBill.getAutoOut()) && outBill != null && outCargoList != null && !outCargoList.isEmpty()) { + // 设置出库单据的明细列表 + outBill.setCkBillCargos(outCargoList); + + // 调用outCargo方法处理出库 + this.outCargo(outBill); + + // 自动确认出库单据 + try { + // 直接调用warehouseWork方法确认单据,跳过审核步骤 + // 由于是自动生成的单据,我们直接设置为已审核状态 + outBill.setAuditStatus("1"); + this.updateById(outBill); + // 调用warehouseWork方法确认单据 + this.warehouseWork(outBill, false); + } catch (Exception e) { + e.printStackTrace(); + // 确认失败不影响主流程 + } + } + this.updateById(ckBill); ckBillCargoService.updateBatchById(ckBillCargos); return CommonResult.success(); @@ -757,3 +882,6 @@ public class CkBillServiceImpl extends ServiceImpl implements return warehouseWork(this.getById(id),true); } } + + + diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/CkStockChangeServiceImpl.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/CkStockChangeServiceImpl.java index 48be4e0..e016672 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/CkStockChangeServiceImpl.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/CkStockChangeServiceImpl.java @@ -5,18 +5,41 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zbkj.modules.autogencode.entity.SysDictData; +import com.zbkj.modules.autogencode.service.SysDictDataService; +import org.apache.poi.hssf.usermodel.HSSFSheet; import org.springframework.stereotype.Service; import com.zbkj.modules.autogencode.dao.CkStockChangeDao; import com.zbkj.modules.autogencode.entity.CkStockChange; +import com.zbkj.modules.autogencode.dao.CmCustDao; +import com.zbkj.modules.autogencode.entity.CmCust; import com.zbkj.modules.autogencode.service.CkStockChangeService; import com.zbkj.common.page.CommonPage; import com.zbkj.common.request.PageParamRequest; +import com.zbkj.common.request.InventoryRequest; +import com.zbkj.common.response.InventoryResponse; import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URLEncoder; import java.util.Map; import java.util.Arrays; import java.util.List; +import java.util.ArrayList; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.stream.Collectors; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.Objects; +import com.zbkj.modules.autogencode.entity.CmCustProduct; +import com.zbkj.modules.autogencode.dao.CmCustProductDao; @Service("ckStockChangeService") @@ -26,7 +49,16 @@ public class CkStockChangeServiceImpl extends ServiceImpl getInventoryList(InventoryRequest request) { + List result = new ArrayList<>(); + + // 构建查询条件 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + if (request.getCustId() != null) { + wrapper.eq(CkStockChange::getCustId, request.getCustId()); + } + if (request.getCargoId() != null) { + wrapper.eq(CkStockChange::getCargoId, request.getCargoId()); + } + if (request.getCargoName() != null) { + wrapper.eq(CkStockChange::getCargoName, request.getCargoName()); + } + if (request.getStockId() != null) { + wrapper.eq(CkStockChange::getStockId, request.getStockId()); + } + if (request.getStockName() != null) { + wrapper.eq(CkStockChange::getStockName, request.getStockName()); + } + + // 按时间升序排序,便于取最后一条记录 + wrapper.orderByAsc(CkStockChange::getChangeDate); + + // 查询所有库存变动记录 + List allChanges = dao.selectList(wrapper); + + // 1. 同bill_id + cargoId去重,取每组最后一条记录 + Map uniqueByBillAndCargo = new HashMap<>(); + for (CkStockChange change : allChanges) { + String key = change.getBillId() + "_" + change.getCargoId(); + // 由于已经按时间升序排序,后面的记录会覆盖前面的,最终保留最后一条 + uniqueByBillAndCargo.put(key, change); + } + List uniqueChanges = new ArrayList<>(uniqueByBillAndCargo.values()); + + // 2. 按 cargoName + hsCode + custId + stockId 分组 + Map> groupedByProduct = uniqueChanges.stream() + .collect(Collectors.groupingBy(change -> + change.getCargoName() + "_" + change.getHsCode() + "_" + change.getCustId() + "_" + change.getStockId())); + + // 3. 处理物品类别过滤 + if (request.getCargoCategory() != null && !request.getCargoCategory().isEmpty()) { + // 获取所有商品ID + List cargoIds = uniqueChanges.stream() + .map(CkStockChange::getCargoId) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + + if (!cargoIds.isEmpty()) { + // 查询商品分类信息 + List products = cmCustProductDao.selectList(new LambdaQueryWrapper() + .in(CmCustProduct::getId, cargoIds)); + + // 构建商品ID到分类ID的映射 + Map cargoClassMap = products.stream() + .collect(Collectors.toMap(CmCustProduct::getId, CmCustProduct::getClassId, (a, b) -> b)); + + // 构建商品ID到单位的映射 + Map cargoUnitMap = products.stream() + .collect(Collectors.toMap(CmCustProduct::getId, CmCustProduct::getUnit, (a, b) -> b)); + + // 过滤分组,只保留符合分类条件的商品 + Map> filteredGroups = new HashMap<>(); + for (Map.Entry> entry : groupedByProduct.entrySet()) { + List changes = entry.getValue(); + if (!changes.isEmpty()) { + CkStockChange firstChange = changes.get(0); + Long classId = cargoClassMap.get(firstChange.getCargoId()); + if (classId != null && request.getCargoCategory().contains(classId.toString())) { + filteredGroups.put(entry.getKey(), entry.getValue()); + } + } + } + groupedByProduct = filteredGroups; + } else { + // 没有商品ID,清空分组 + groupedByProduct.clear(); + } + } + + // 合计数据 + BigDecimal totalLastCargoNum = BigDecimal.ZERO; + BigDecimal totalLastCargoValue = BigDecimal.ZERO; + BigDecimal totalInCargoNum = BigDecimal.ZERO; + BigDecimal totalInCargoValue = BigDecimal.ZERO; + BigDecimal totalOutCargoNum = BigDecimal.ZERO; + BigDecimal totalOutCargoValue = BigDecimal.ZERO; + BigDecimal totalCargoNum = BigDecimal.ZERO; + BigDecimal totalCargoValue = BigDecimal.ZERO; + + // 查询所有商品的单位信息(通过字典翻译) + Map cargoUnitMap = new HashMap<>(); + List allCargoIds = groupedByProduct.values().stream() + .flatMap(list -> list.stream()) + .map(CkStockChange::getCargoId) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + if (!allCargoIds.isEmpty()) { + List products = cmCustProductDao.selectList(new LambdaQueryWrapper() + .in(CmCustProduct::getId, allCargoIds)); + + // 通过字典表 bm_measuring_unit 翻译单位 + List unitDictList = sysDictDataService.list(new LambdaQueryWrapper() + .eq(SysDictData::getDictType, "bm_measuring_unit") + .eq(SysDictData::getStatus, "0")); + Map unitTranslationMap = unitDictList.stream() + .collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel, (a, b) -> b)); + + // 设置翻译后的单位 + for (CmCustProduct product : products) { + String unitCode = product.getUnit(); + String translatedUnit = unitTranslationMap.getOrDefault(unitCode, unitCode != null ? unitCode : "吨"); + cargoUnitMap.put(product.getId(), translatedUnit); + } + } + + // 遍历每个商品组 + int index = 1; + for (Map.Entry> entry : groupedByProduct.entrySet()) { + List productChanges = entry.getValue(); + InventoryResponse response = new InventoryResponse(); + + // 获取商品基本信息(使用第一个记录的数据) + CkStockChange firstChange = productChanges.get(0); + response.setIndex(index++); + response.setCargoName(firstChange.getCargoName()); + response.setCargoSpec(firstChange.getCargoSpec()); + // 设置单位 + response.setUnit(cargoUnitMap.getOrDefault(firstChange.getCargoId(), "吨")); + // 构建品名及规格 + String cargoNameWithSpec = firstChange.getCargoName(); + if (firstChange.getCargoSpec() != null && !firstChange.getCargoSpec().isEmpty()) { + cargoNameWithSpec += "\n" + firstChange.getCargoSpec(); + } + response.setCargoNameWithSpec(cargoNameWithSpec); + + // 上期结存变量 + BigDecimal lastCargoNum = BigDecimal.ZERO; + BigDecimal lastCargoValue = BigDecimal.ZERO; + // 本期入库变量 + BigDecimal inCargoNum = BigDecimal.ZERO; + BigDecimal inCargoValue = BigDecimal.ZERO; + // 本期出库变量 + BigDecimal outCargoNum = BigDecimal.ZERO; + BigDecimal outCargoValue = BigDecimal.ZERO; + + // 遍历变动记录,计算上期结存和本期入库出库 + for (CkStockChange change : productChanges) { + if (request.getStartDate() != null) { + if (change.getChangeDate().before(request.getStartDate())) { + // 上期变动记录,用于计算上期结存 + if ("1".equals(change.getChangeType())) { + // 上期入库 + lastCargoNum = lastCargoNum.add(change.getChangeWt() != null ? change.getChangeWt() : BigDecimal.ZERO); + lastCargoValue = lastCargoValue.add(change.getChangeValue() != null ? change.getChangeValue() : BigDecimal.ZERO); + } else if ("2".equals(change.getChangeType())) { + // 上期出库 + lastCargoNum = lastCargoNum.subtract(change.getChangeWt() != null ? change.getChangeWt() : BigDecimal.ZERO); + lastCargoValue = lastCargoValue.subtract(change.getChangeValue() != null ? change.getChangeValue() : BigDecimal.ZERO); + } + } else if (request.getEndDate() != null && !change.getChangeDate().after(request.getEndDate())) { + // 本期变动记录 + if ("1".equals(change.getChangeType())) { + // 本期入库 + inCargoNum = inCargoNum.add(change.getChangeWt() != null ? change.getChangeWt() : BigDecimal.ZERO); + inCargoValue = inCargoValue.add(change.getChangeValue() != null ? change.getChangeValue() : BigDecimal.ZERO); + } else if ("2".equals(change.getChangeType())) { + // 本期出库 + outCargoNum = outCargoNum.add(change.getChangeWt() != null ? change.getChangeWt() : BigDecimal.ZERO); + outCargoValue = outCargoValue.add(change.getChangeValue() != null ? change.getChangeValue() : BigDecimal.ZERO); + } + } + } + } + + // 确保结存不为负数 + if (lastCargoNum.compareTo(BigDecimal.ZERO) < 0) { + lastCargoNum = BigDecimal.ZERO; + } + if (lastCargoValue.compareTo(BigDecimal.ZERO) < 0) { + lastCargoValue = BigDecimal.ZERO; + } + + // 设置响应数据 + response.setLastCargoNum(lastCargoNum); + response.setLastCargoValue(lastCargoValue); + response.setInCargoNum(inCargoNum); + response.setInCargoValue(inCargoValue); + response.setOutCargoNum(outCargoNum); + response.setOutCargoValue(outCargoValue); + + // 累计结存 = 上期结存 + 本期入库 - 本期出库 + BigDecimal totalNum = lastCargoNum.add(inCargoNum).subtract(outCargoNum); + BigDecimal totalValue = lastCargoValue.add(inCargoValue).subtract(outCargoValue); + response.setCargoNum(totalNum); + response.setCargoValue(totalValue); + + // 累加合计 + totalLastCargoNum = totalLastCargoNum.add(lastCargoNum); + totalLastCargoValue = totalLastCargoValue.add(lastCargoValue); + totalInCargoNum = totalInCargoNum.add(inCargoNum); + totalInCargoValue = totalInCargoValue.add(inCargoValue); + totalOutCargoNum = totalOutCargoNum.add(outCargoNum); + totalOutCargoValue = totalOutCargoValue.add(outCargoValue); + totalCargoNum = totalCargoNum.add(totalNum); + totalCargoValue = totalCargoValue.add(totalValue); + + result.add(response); + } + + // 添加合计行 + if (!result.isEmpty()) { + InventoryResponse totalResponse = new InventoryResponse(); + totalResponse.setCargoName("合计"); + totalResponse.setCargoNameWithSpec("合计"); + totalResponse.setLastCargoNum(totalLastCargoNum); + totalResponse.setLastCargoValue(totalLastCargoValue); + totalResponse.setInCargoNum(totalInCargoNum); + totalResponse.setInCargoValue(totalInCargoValue); + totalResponse.setOutCargoNum(totalOutCargoNum); + totalResponse.setOutCargoValue(totalOutCargoValue); + totalResponse.setCargoNum(totalCargoNum); + totalResponse.setCargoValue(totalCargoValue); + result.add(totalResponse); + } + + return result; + } + + // 标题样式:加粗、居中、14号 + private CellStyle getTitleStyle(Workbook wb) { + CellStyle style = wb.createCellStyle(); + Font font = wb.createFont(); + font.setFontName("微软雅黑"); + font.setFontHeightInPoints((short) 14); + font.setBold(true); + style.setFont(font); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + return style; + } + + // 表头样式:加粗、居中、11号 + private CellStyle getHeaderStyle(Workbook wb) { + CellStyle style = wb.createCellStyle(); + Font font = wb.createFont(); + font.setFontName("微软雅黑"); + font.setFontHeightInPoints((short) 11); + font.setBold(true); + style.setFont(font); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + return style; + } + + // 数据样式:居中、细边框 + private CellStyle getDataStyle(Workbook wb) { + CellStyle style = wb.createCellStyle(); + Font font = wb.createFont(); + font.setFontName("微软雅黑"); + font.setFontHeightInPoints((short) 10); + style.setFont(font); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + return style; + } + + // 合计金额样式:右对齐、加粗、细边框 + private CellStyle getTotalValueStyle(Workbook wb) { + CellStyle style = wb.createCellStyle(); + Font font = wb.createFont(); + font.setFontName("微软雅黑"); + font.setFontHeightInPoints((short) 11); + font.setBold(true); + style.setFont(font); + style.setAlignment(HorizontalAlignment.RIGHT); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + return style; + } + + // 底部签字样式 + private CellStyle getSignStyle(Workbook wb) { + CellStyle style = wb.createCellStyle(); + Font font = wb.createFont(); + font.setFontName("微软雅黑"); + font.setFontHeightInPoints((short) 10); + style.setFont(font); + return style; + } + + @Override + public void exportInventory(InventoryRequest request) { + // 获取盘点数据 + List inventoryList = getInventoryList(request); + + // 获取响应对象 + HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); + if (response == null) { + return; + } + + try { + // 1. 创建工作簿 + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet("物资盘点表"); + + // 2. 行高设置 + sheet.createRow(0).setHeight((short) 600); // 标题 + sheet.createRow(1).setHeight((short) 350); // 类别/供应商/日期 + sheet.createRow(2).setHeight((short) 380); // 表头1 + sheet.createRow(3).setHeight((short) 380); // 表头2 + + // ====================== 第1行:标题 ====================== + Row titleRow = sheet.getRow(0); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellValue("物资盘点表"); + titleCell.setCellStyle(getTitleStyle(wb)); + // 合并 A1:N1 + sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 13)); + + // ====================== 第2行:类别、供应商、日期 ====================== + Row infoRow = sheet.getRow(1); + infoRow.createCell(0).setCellValue("类别:所有类别"); + // 根据custId查询供应商名称 + String supplierName = ""; + if (request.getCustId() != null) { + CmCust cmCust = cmCustDao.selectById(request.getCustId()); + if (cmCust != null) { + supplierName = cmCust.getCustName(); + } + } + infoRow.createCell(6).setCellValue("供应商:" + supplierName); + // 使用传参中的结束日期 + String endDateStr = ""; + if (request.getEndDate() != null) { + endDateStr = new java.text.SimpleDateFormat("yyyy-MM-dd").format(request.getEndDate()); + } + infoRow.createCell(11).setCellValue("日期:" + endDateStr); + + // ====================== 第3-4行:双行表头 ====================== + Row headerRow1 = sheet.getRow(2); + Row headerRow2 = sheet.getRow(3); + CellStyle hs = getHeaderStyle(wb); + + // 双行表头结构(严格对齐你的表) + String[] h1 = { + "序号", "品名及规格", "上期结存", "", "", "本期入库", "", "", "本期出库", "", "", "累计结存", "", "" + }; + String[] h2 = { + "", "", "单位", "数量", "金额", "单位", "数量", "金额", "单位", "数量", "金额", "单位", "数量", "金额" + }; + + for (int i = 0; i < h1.length; i++) { + Cell c1 = headerRow1.createCell(i); + Cell c2 = headerRow2.createCell(i); + c1.setCellValue(h1[i]); + c2.setCellValue(h2[i]); + c1.setCellStyle(hs); + c2.setCellStyle(hs); + } + + // 合并表头(上期/入库/出库/结存) + sheet.addMergedRegion(new CellRangeAddress(2, 2, 2, 4)); // 上期 + sheet.addMergedRegion(new CellRangeAddress(2, 2, 5, 7)); // 入库 + sheet.addMergedRegion(new CellRangeAddress(2, 2, 8, 10)); // 出库 + sheet.addMergedRegion(new CellRangeAddress(2, 2, 11, 13)); // 结存 + + // ====================== 数据行 ====================== + CellStyle ds = getDataStyle(wb); + int dataRowIndex = 4; + + for (int i = 0; i < inventoryList.size(); i++) { + InventoryResponse item = inventoryList.get(i); + Row dataRow = sheet.createRow(dataRowIndex); + dataRow.setHeight((short) 300); + + // 判断是否为合计行(最后一行) + boolean isTotalRow = (i == inventoryList.size() - 1) && "合计".equals(item.getCargoNameWithSpec()); + CellStyle currentStyle = isTotalRow ? hs : ds; + + if (!isTotalRow) { + // 数据行:显示完整内容 + // 序号 + Cell indexCell = dataRow.createCell(0); + indexCell.setCellValue(item.getIndex() != null ? item.getIndex() : 0); + indexCell.setCellStyle(currentStyle); + + // 品名及规格 + Cell nameCell = dataRow.createCell(1); + nameCell.setCellValue(item.getCargoNameWithSpec() != null ? item.getCargoNameWithSpec() : ""); + nameCell.setCellStyle(currentStyle); + + // 上期结存 + String unit = item.getUnit() != null ? item.getUnit() : "吨"; + Cell lastUnitCell = dataRow.createCell(2); + lastUnitCell.setCellValue(unit); + lastUnitCell.setCellStyle(currentStyle); + + Cell lastNumCell = dataRow.createCell(3); + BigDecimal lastNum = item.getLastCargoNum() != null ? item.getLastCargoNum() : BigDecimal.ZERO; + lastNumCell.setCellValue(lastNum.doubleValue()); + lastNumCell.setCellStyle(currentStyle); + + Cell lastValueCell = dataRow.createCell(4); + BigDecimal lastValue = item.getLastCargoValue() != null ? item.getLastCargoValue() : BigDecimal.ZERO; + lastValueCell.setCellValue(lastValue.doubleValue()); + lastValueCell.setCellStyle(currentStyle); + + // 本期入库 + Cell inUnitCell = dataRow.createCell(5); + inUnitCell.setCellValue(unit); + inUnitCell.setCellStyle(currentStyle); + + Cell inNumCell = dataRow.createCell(6); + BigDecimal inNum = item.getInCargoNum() != null ? item.getInCargoNum() : BigDecimal.ZERO; + inNumCell.setCellValue(inNum.doubleValue()); + inNumCell.setCellStyle(currentStyle); + + Cell inValueCell = dataRow.createCell(7); + BigDecimal inValue = item.getInCargoValue() != null ? item.getInCargoValue() : BigDecimal.ZERO; + inValueCell.setCellValue(inValue.doubleValue()); + inValueCell.setCellStyle(currentStyle); + + // 本期出库 + Cell outUnitCell = dataRow.createCell(8); + outUnitCell.setCellValue(unit); + outUnitCell.setCellStyle(currentStyle); + + Cell outNumCell = dataRow.createCell(9); + BigDecimal outNum = item.getOutCargoNum() != null ? item.getOutCargoNum() : BigDecimal.ZERO; + outNumCell.setCellValue(outNum.doubleValue()); + outNumCell.setCellStyle(currentStyle); + + Cell outValueCell = dataRow.createCell(10); + BigDecimal outValue = item.getOutCargoValue() != null ? item.getOutCargoValue() : BigDecimal.ZERO; + outValueCell.setCellValue(outValue.doubleValue()); + outValueCell.setCellStyle(currentStyle); + + // 累计结存 + Cell cargoUnitCell = dataRow.createCell(11); + cargoUnitCell.setCellValue(unit); + cargoUnitCell.setCellStyle(currentStyle); + + Cell cargoNumCell = dataRow.createCell(12); + BigDecimal cargoNum = item.getCargoNum() != null ? item.getCargoNum() : BigDecimal.ZERO; + cargoNumCell.setCellValue(cargoNum.doubleValue()); + cargoNumCell.setCellStyle(currentStyle); + + Cell cargoValueCell = dataRow.createCell(13); + BigDecimal cargoValue = item.getCargoValue() != null ? item.getCargoValue() : BigDecimal.ZERO; + cargoValueCell.setCellValue(cargoValue.doubleValue()); + cargoValueCell.setCellStyle(currentStyle); + + dataRowIndex++; + } else { + // 合计行:每个大列内部合并单位、数量和金额列,只显示金额(右对齐) + CellStyle totalValueStyle = getTotalValueStyle(wb); + + // 序号(空) + Cell indexCell = dataRow.createCell(0); + indexCell.setCellStyle(currentStyle); + + // 品名及规格:显示"合计" + Cell nameCell = dataRow.createCell(1); + nameCell.setCellValue("合计"); + nameCell.setCellStyle(currentStyle); + + // 上期结存:合并单位(2)、数量(3)和金额(4)列,只显示金额(右对齐) + for (int col = 2; col <= 4; col++) { + Cell cell = dataRow.createCell(col); + cell.setCellStyle(totalValueStyle); + } + sheet.addMergedRegion(new CellRangeAddress(dataRowIndex, dataRowIndex, 2, 4)); + Cell lastValueCell = dataRow.getCell(2); + BigDecimal lastValue = item.getLastCargoValue() != null ? item.getLastCargoValue() : BigDecimal.ZERO; + lastValueCell.setCellValue(lastValue.doubleValue()); + + // 本期入库:合并单位(5)、数量(6)和金额(7)列,只显示金额(右对齐) + for (int col = 5; col <= 7; col++) { + Cell cell = dataRow.createCell(col); + cell.setCellStyle(totalValueStyle); + } + sheet.addMergedRegion(new CellRangeAddress(dataRowIndex, dataRowIndex, 5, 7)); + Cell inValueCell = dataRow.getCell(5); + BigDecimal inValue = item.getInCargoValue() != null ? item.getInCargoValue() : BigDecimal.ZERO; + inValueCell.setCellValue(inValue.doubleValue()); + + // 本期出库:合并单位(8)、数量(9)和金额(10)列,只显示金额(右对齐) + for (int col = 8; col <= 10; col++) { + Cell cell = dataRow.createCell(col); + cell.setCellStyle(totalValueStyle); + } + sheet.addMergedRegion(new CellRangeAddress(dataRowIndex, dataRowIndex, 8, 10)); + Cell outValueCell = dataRow.getCell(8); + BigDecimal outValue = item.getOutCargoValue() != null ? item.getOutCargoValue() : BigDecimal.ZERO; + outValueCell.setCellValue(outValue.doubleValue()); + + // 累计结存:合并单位(11)、数量(12)和金额(13)列,只显示金额(右对齐) + for (int col = 11; col <= 13; col++) { + Cell cell = dataRow.createCell(col); + cell.setCellStyle(totalValueStyle); + } + sheet.addMergedRegion(new CellRangeAddress(dataRowIndex, dataRowIndex, 11, 13)); + Cell cargoValueCell = dataRow.getCell(11); + BigDecimal cargoValue = item.getCargoValue() != null ? item.getCargoValue() : BigDecimal.ZERO; + cargoValueCell.setCellValue(cargoValue.doubleValue()); + + dataRowIndex++; + } + } + + // ====================== 底部签字 ====================== + Row signRow = sheet.createRow(dataRowIndex++); + signRow.setHeight((short) 300); + CellStyle ss = getSignStyle(wb); + + // 审核人:合并列0-4 + sheet.addMergedRegion(new CellRangeAddress(dataRowIndex - 1, dataRowIndex - 1, 0, 4)); + Cell auditorCell = signRow.createCell(0); + auditorCell.setCellValue("审核人:"); + auditorCell.setCellStyle(ss); + + // 仓管员:合并列5-9 + sheet.addMergedRegion(new CellRangeAddress(dataRowIndex - 1, dataRowIndex - 1, 5, 9)); + Cell warehouseCell = signRow.createCell(5); + warehouseCell.setCellValue("仓管员:"); + warehouseCell.setCellStyle(ss); + + // 领料人:合并列10-13 + sheet.addMergedRegion(new CellRangeAddress(dataRowIndex - 1, dataRowIndex - 1, 10, 13)); + Cell requesterCell = signRow.createCell(10); + requesterCell.setCellValue("领料人:"); + requesterCell.setCellStyle(ss); + + // 设置所有单元格样式 + for (int i = 0; i < 14; i++) { + Cell c = signRow.getCell(i); + if (c == null) { + c = signRow.createCell(i); + } + c.setCellStyle(ss); + } + + // ====================== 列宽 ====================== + int[] widths = {6, 24, 6, 10, 10, 6, 10, 10, 6, 10, 10, 6, 10, 10}; + for (int i = 0; i < widths.length; i++) { + sheet.setColumnWidth(i, widths[i] * 256); + } + + // ====================== 输出 ====================== + response.setContentType("application/vnd.ms-excel"); + String fileName = URLEncoder.encode("物资盘点表", "UTF-8").replaceAll("\\+", "%"); + response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + fileName + ".xls"); + wb.write(response.getOutputStream()); + wb.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/PmDailyMenuServiceImpl.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/PmDailyMenuServiceImpl.java index ecc3238..233b8e0 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/PmDailyMenuServiceImpl.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/PmDailyMenuServiceImpl.java @@ -17,6 +17,7 @@ import javax.annotation.Resource; import java.util.Map; import java.util.Arrays; import java.util.List; +import java.util.Date; @Service("pmDailyMenuService") @@ -38,5 +39,20 @@ public class PmDailyMenuServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper<>(); + try { + // 解析日期字符串为 Date 对象 + Date date = new java.text.SimpleDateFormat("yyyy-MM-dd").parse(menuDate); + queryWrapper.eq(PmDailyMenu::getMenuDate, date); + } catch (Exception e) { + // 如果日期解析失败,返回 null + return null; + } + queryWrapper.eq(PmDailyMenu::getCanteenName, canteenName); + queryWrapper.eq(PmDailyMenu::getMealType, mealType); + return dao.selectOne(queryWrapper); + } } diff --git a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/SysDictDataServiceImpl.java b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/SysDictDataServiceImpl.java index e9f1384..ea63c43 100644 --- a/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/SysDictDataServiceImpl.java +++ b/crmeb/crmeb-admin/src/main/java/com/zbkj/modules/autogencode/service/impl/SysDictDataServiceImpl.java @@ -38,5 +38,11 @@ public class SysDictDataServiceImpl extends ServiceImpl getByDictType(String dictType) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictData::getDictType, dictType); + return dao.selectList(queryWrapper); + } } diff --git a/crmeb/crmeb-common/src/main/java/com/zbkj/common/constants/NotifyConstants.java b/crmeb/crmeb-common/src/main/java/com/zbkj/common/constants/NotifyConstants.java index 08cb527..a9dc33e 100644 --- a/crmeb/crmeb-common/src/main/java/com/zbkj/common/constants/NotifyConstants.java +++ b/crmeb/crmeb-common/src/main/java/com/zbkj/common/constants/NotifyConstants.java @@ -39,4 +39,9 @@ public class NotifyConstants { public static final String PLACE_AN_ORDER_ADMIN_MARK = "adminPlaceAnOrder"; /** 用户发起退款(管理员)标记 */ public static final String APPLY_ORDER_REFUND_ADMIN_MARK = "adminApplyOrderRefund"; + + /** 报修通知标记 */ + public static final String MAINTENANCE_NOTICE_MARK = "maintenanceNotice"; + /** 报修通知(管理员)标记 */ + public static final String MAINTENANCE_NOTICE_ADMIN_MARK = "adminMaintenanceNotice"; } diff --git a/crmeb/crmeb-common/src/main/java/com/zbkj/common/model/wechat/WechatFans.java b/crmeb/crmeb-common/src/main/java/com/zbkj/common/model/wechat/WechatFans.java new file mode 100644 index 0000000..291abeb --- /dev/null +++ b/crmeb/crmeb-common/src/main/java/com/zbkj/common/model/wechat/WechatFans.java @@ -0,0 +1,82 @@ +package com.zbkj.common.model.wechat; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * 公众号关注用户表 + * +---------------------------------------------------------------------- + * | CRMEB [ CRMEB赋能开发者,助力企业发展 ] + * +---------------------------------------------------------------------- + * | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved. + * +---------------------------------------------------------------------- + * | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权 + * +---------------------------------------------------------------------- + * | Author: CRMEB Team + * +---------------------------------------------------------------------- + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("eb_wechat_fans") +@ApiModel(value="WechatFans对象", description="公众号关注用户表") +public class WechatFans implements Serializable { + + private static final long serialVersionUID=1L; + + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + @ApiModelProperty(value = "公众号openid") + private String openId; + + @ApiModelProperty(value = "unionid") + private String unionId; + + @ApiModelProperty(value = "用户昵称") + private String nickname; + + @ApiModelProperty(value = "性别:0未知,1-男,2-女") + private Integer sex; + + @ApiModelProperty(value = "用户个人资料填写的省份") + private String province; + + @ApiModelProperty(value = "普通用户个人资料填写的城市") + private String city; + + @ApiModelProperty(value = "国家,如中国为CN") + private String country; + + @ApiModelProperty(value = "用户头像") + private String headimgurl; + + @ApiModelProperty(value = "用户是否关注:0-未关注,1-已关注") + private Integer subscribe; + + @ApiModelProperty(value = "关注时间") + private Date subscribeTime; + + @ApiModelProperty(value = "备注") + private String remark; + + @ApiModelProperty(value = "分组ID") + private Integer groupId; + + @ApiModelProperty(value = "创建时间") + private Date createTime; + + @ApiModelProperty(value = "更新时间") + private Date updateTime; + + +} \ No newline at end of file diff --git a/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/PageParamRequest.java b/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/PageParamRequest.java index 567b29e..f3333be 100644 --- a/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/PageParamRequest.java +++ b/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/PageParamRequest.java @@ -25,4 +25,14 @@ public class PageParamRequest { @ApiModelProperty(value = "每页数量", example = Constants.DEFAULT_LIMIT + "") private int limit = Constants.DEFAULT_LIMIT; + // 兼容pageNum参数 + public void setPageNum(int pageNum) { + this.page = pageNum; + } + + // 兼容pageSize参数 + public void setPageSize(int pageSize) { + this.limit = pageSize; + } + } diff --git a/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/RegisterThirdUserRequest.java b/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/RegisterThirdUserRequest.java index abf0b0a..561a097 100644 --- a/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/RegisterThirdUserRequest.java +++ b/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/RegisterThirdUserRequest.java @@ -59,4 +59,7 @@ public class RegisterThirdUserRequest implements Serializable { @ApiModelProperty(value = "用户openId") private String openId; + + @ApiModelProperty(value = "unionId") + private String unionId; } diff --git a/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/SubscribeMessageRequest.java b/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/SubscribeMessageRequest.java new file mode 100644 index 0000000..df875b9 --- /dev/null +++ b/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/SubscribeMessageRequest.java @@ -0,0 +1,61 @@ +package com.zbkj.common.request; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 微信一次性订阅消息请求 + */ +@Data +@ApiModel(value = "SubscribeMessageRequest", description = "微信一次性订阅消息请求") +public class SubscribeMessageRequest { + + @ApiModelProperty(value = "接收消息的用户openid", required = true) + private String touser; + + @ApiModelProperty(value = "订阅消息模板ID", required = true) + private String templateId; + + @ApiModelProperty(value = "点击消息跳转链接(需ICP备案)") + private String url; + + @ApiModelProperty(value = "跳小程序配置") + private MiniProgram miniProgram; + + @ApiModelProperty(value = "订阅场景值", required = true) + private String scene; + + @ApiModelProperty(value = "订阅通知消息标题(15字以内)", required = true) + private String title; + + @ApiModelProperty(value = "消息内容", required = true) + private MessageData data; + + @Data + @ApiModel(value = "MiniProgram", description = "跳小程序配置") + public static class MiniProgram { + @ApiModelProperty(value = "小程序appid", required = true) + private String appid; + + @ApiModelProperty(value = "小程序页面路径", required = true) + private String pagepath; + } + + @Data + @ApiModel(value = "MessageData", description = "消息内容") + public static class MessageData { + @ApiModelProperty(value = "内容信息") + private Content content; + } + + @Data + @ApiModel(value = "Content", description = "内容信息") + public static class Content { + @ApiModelProperty(value = "消息文本(200字内)") + private String value; + + @ApiModelProperty(value = "字体颜色") + private String color; + } +} \ No newline at end of file diff --git a/crmeb/crmeb-common/src/main/java/com/zbkj/common/response/SubscribeMessageResponse.java b/crmeb/crmeb-common/src/main/java/com/zbkj/common/response/SubscribeMessageResponse.java new file mode 100644 index 0000000..b59443a --- /dev/null +++ b/crmeb/crmeb-common/src/main/java/com/zbkj/common/response/SubscribeMessageResponse.java @@ -0,0 +1,26 @@ +package com.zbkj.common.response; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 微信订阅消息发送响应 + */ +@Data +@ApiModel(value = "SubscribeMessageResponse", description = "微信订阅消息发送响应") +public class SubscribeMessageResponse { + + @ApiModelProperty(value = "错误码") + private Integer errcode; + + @ApiModelProperty(value = "错误信息") + private String errmsg; + + /** + * 判断是否发送成功 + */ + public boolean isSuccess() { + return errcode == null || errcode == 0; + } +} \ No newline at end of file diff --git a/crmeb/crmeb-common/src/main/java/com/zbkj/common/token/FrontTokenComponent.java b/crmeb/crmeb-common/src/main/java/com/zbkj/common/token/FrontTokenComponent.java index aea1819..8d65c69 100644 --- a/crmeb/crmeb-common/src/main/java/com/zbkj/common/token/FrontTokenComponent.java +++ b/crmeb/crmeb-common/src/main/java/com/zbkj/common/token/FrontTokenComponent.java @@ -175,7 +175,9 @@ public class FrontTokenComponent { "api/front/index/get/version", "api/front/image/domain", "api/front/product/leaderboard", - "api/autogencode/sysdictdata/types" + "api/autogencode/sysdictdata/types", + "api/front/bindingWx", + "api/front/unbindWx" }; return ArrayUtils.contains(routerList, uri); diff --git a/crmeb/crmeb-front/src/main/java/com/zbkj/front/CrmebFrontApplication.java b/crmeb/crmeb-front/src/main/java/com/zbkj/front/CrmebFrontApplication.java index c302066..bc2eade 100644 --- a/crmeb/crmeb-front/src/main/java/com/zbkj/front/CrmebFrontApplication.java +++ b/crmeb/crmeb-front/src/main/java/com/zbkj/front/CrmebFrontApplication.java @@ -7,6 +7,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.transaction.annotation.EnableTransactionManagement; import springfox.documentation.swagger2.annotations.EnableSwagger2; @@ -26,6 +27,7 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; @EnableSwagger2 @Configuration @EnableTransactionManagement +@EnableScheduling //开启定时任务 @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) //去掉数据源 @ComponentScan(basePackages = {"com.zbkj", "com.zbkj.front"}) @MapperScan(basePackages = {"com.zbkj.**.dao"}) diff --git a/crmeb/crmeb-front/src/main/java/com/zbkj/front/controller/WeChatController.java b/crmeb/crmeb-front/src/main/java/com/zbkj/front/controller/WeChatController.java index 6f66a0d..bdf5371 100644 --- a/crmeb/crmeb-front/src/main/java/com/zbkj/front/controller/WeChatController.java +++ b/crmeb/crmeb-front/src/main/java/com/zbkj/front/controller/WeChatController.java @@ -9,6 +9,7 @@ import com.zbkj.common.response.LoginResponse; import com.zbkj.common.response.WeChatJsSdkConfigResponse; import com.zbkj.front.service.UserCenterService; import com.zbkj.service.service.SystemNotificationService; +import com.zbkj.service.service.WechatFansService; import com.zbkj.service.service.WechatNewService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; @@ -51,6 +52,9 @@ public class WeChatController { @Autowired private SystemNotificationService systemNotificationService; + @Autowired + private WechatFansService wechatFansService; + /** * 通过微信code登录 */ @@ -113,7 +117,50 @@ public class WeChatController { public CommonResult> programMyTempList(@RequestParam(name = "type") String type){ return CommonResult.success(systemNotificationService.getMiniTempList(type)); } -} - + /** + * 同步公众号关注用户列表 + * 调用微信API获取关注者列表,并批量获取用户信息保存到服务器 + */ + @ApiOperation(value = "同步公众号关注用户列表") + @RequestMapping(value = "/fans/sync", method = RequestMethod.POST) + public CommonResult syncFans(){ + Integer count = wechatFansService.syncFans(); + return CommonResult.success(count, "同步成功"); + } + /** + * 发送一次性订阅消息 + * 通过unionId获取公众号openId,然后发送订阅消息 + */ + @ApiOperation(value = "发送一次性订阅消息") + @RequestMapping(value = "/subscribe/message/send", method = RequestMethod.POST) + @ApiImplicitParams({ + @ApiImplicitParam(name = "unionId", value = "用户unionId", dataType = "String", required = true), + @ApiImplicitParam(name = "templateId", value = "订阅消息模板ID", dataType = "String", required = true), + @ApiImplicitParam(name = "scene", value = "订阅场景值", dataType = "String", required = true), + @ApiImplicitParam(name = "title", value = "消息标题(15字以内)", dataType = "String", required = true), + @ApiImplicitParam(name = "content", value = "消息内容(200字以内)", dataType = "String", required = true), + @ApiImplicitParam(name = "url", value = "跳转链接(需ICP备案)", dataType = "String") + }) + public CommonResult sendSubscribeMessage(@RequestParam(value = "unionId") String unionId, + @RequestParam(value = "templateId") String templateId, + @RequestParam(value = "scene") String scene, + @RequestParam(value = "title") String title, + @RequestParam(value = "content") String content, + @RequestParam(value = "url", required = false) String url){ + // 通过unionId获取公众号openId + String openId = wechatFansService.getOpenIdByUnionId(unionId); + if (openId == null) { + return CommonResult.failed("未找到该用户的公众号openId"); + } + + // 发送订阅消息 + boolean success = wechatFansService.sendSubscribeMessage(openId, templateId, scene, title, content, url); + if (success) { + return CommonResult.success(true, "发送成功"); + } else { + return CommonResult.failed("发送失败"); + } + } +} diff --git a/crmeb/crmeb-front/src/main/java/com/zbkj/front/service/impl/UserCenterServiceImpl.java b/crmeb/crmeb-front/src/main/java/com/zbkj/front/service/impl/UserCenterServiceImpl.java index 4f232af..0cb8fca 100644 --- a/crmeb/crmeb-front/src/main/java/com/zbkj/front/service/impl/UserCenterServiceImpl.java +++ b/crmeb/crmeb-front/src/main/java/com/zbkj/front/service/impl/UserCenterServiceImpl.java @@ -547,6 +547,7 @@ public class UserCenterServiceImpl extends ServiceImpl implements request.setType(Constants.USER_LOGIN_TYPE_PROGRAM); request.setOpenId(response.getOpenId()); + request.setUnionId(response.getUnionId()); String key = SecureUtil.md5(response.getOpenId()); redisUtil.set(key, JSONObject.toJSONString(request), (long) (60 * 2), TimeUnit.MINUTES); loginResponse.setType("register"); diff --git a/crmeb/crmeb-service/src/main/java/com/zbkj/service/dao/SystemMenuDao.java b/crmeb/crmeb-service/src/main/java/com/zbkj/service/dao/SystemMenuDao.java index 25beaf2..b47e25b 100644 --- a/crmeb/crmeb-service/src/main/java/com/zbkj/service/dao/SystemMenuDao.java +++ b/crmeb/crmeb-service/src/main/java/com/zbkj/service/dao/SystemMenuDao.java @@ -28,4 +28,6 @@ public interface SystemMenuDao extends BaseMapper { * @return List */ List getMenusByUserId(Integer userId); + + List getMenusByUserIdAndPhoto(Integer userId); } diff --git a/crmeb/crmeb-service/src/main/java/com/zbkj/service/dao/WechatFansDao.java b/crmeb/crmeb-service/src/main/java/com/zbkj/service/dao/WechatFansDao.java new file mode 100644 index 0000000..8578a99 --- /dev/null +++ b/crmeb/crmeb-service/src/main/java/com/zbkj/service/dao/WechatFansDao.java @@ -0,0 +1,21 @@ +package com.zbkj.service.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.zbkj.common.model.wechat.WechatFans; +import org.apache.ibatis.annotations.Mapper; + +/** + * 公众号关注用户表 Mapper 接口 + * +---------------------------------------------------------------------- + * | CRMEB [ CRMEB赋能开发者,助力企业发展 ] + * +---------------------------------------------------------------------- + * | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved. + * +---------------------------------------------------------------------- + * | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权 + * +---------------------------------------------------------------------- + * | Author: CRMEB Team + * +---------------------------------------------------------------------- + */ +@Mapper +public interface WechatFansDao extends BaseMapper { +} \ No newline at end of file diff --git a/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/SystemMenuService.java b/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/SystemMenuService.java index bf03736..079e803 100644 --- a/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/SystemMenuService.java +++ b/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/SystemMenuService.java @@ -104,4 +104,11 @@ public interface SystemMenuService extends IService { * @return List */ List getMenusByUserId(Integer userId); + + /** + * 通过用户id获取权限(包含按钮权限) + * @param userId 用户id + * @return List + */ + List getMenusByUserIdAndPhoto(Integer userId); } diff --git a/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/WechatFansService.java b/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/WechatFansService.java new file mode 100644 index 0000000..884050e --- /dev/null +++ b/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/WechatFansService.java @@ -0,0 +1,60 @@ +package com.zbkj.service.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.zbkj.common.model.wechat.WechatFans; + +import java.util.List; + +/** + * 公众号关注用户服务接口 + * +---------------------------------------------------------------------- + * | CRMEB [ CRMEB赋能开发者,助力企业发展 ] + * +---------------------------------------------------------------------- + * | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved. + * +---------------------------------------------------------------------- + * | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权 + * +---------------------------------------------------------------------- + * | Author: CRMEB Team + * +---------------------------------------------------------------------- + */ +public interface WechatFansService extends IService { + + /** + * 同步公众号关注用户列表 + * @return 同步的用户数量 + */ + Integer syncFans(); + + /** + * 根据openId获取用户 + * @param openId 公众号openId + * @return WechatFans + */ + WechatFans getByOpenId(String openId); + + /** + * 批量保存用户 + * @param list 用户列表 + * @return 是否成功 + */ + boolean batchSaveOrUpdate(List list); + + /** + * 发送一次性订阅消息 + * @param openId 接收消息的用户openid + * @param templateId 订阅消息模板ID + * @param scene 订阅场景值 + * @param title 消息标题 + * @param content 消息内容 + * @param url 跳转链接 + * @return 是否发送成功 + */ + boolean sendSubscribeMessage(String openId, String templateId, String scene, String title, String content, String url); + + /** + * 通过unionId获取公众号openId + * @param unionId unionId + * @return 公众号openId + */ + String getOpenIdByUnionId(String unionId); +} \ No newline at end of file diff --git a/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/impl/SystemAdminServiceImpl.java b/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/impl/SystemAdminServiceImpl.java index b02fc9e..34cfc81 100644 --- a/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/impl/SystemAdminServiceImpl.java +++ b/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/impl/SystemAdminServiceImpl.java @@ -30,6 +30,7 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; @Service @@ -572,6 +573,7 @@ public class SystemAdminServiceImpl extends ServiceImpl getMenusByUserIdAndPhoto(Integer userId) { + return dao.getMenusByUserIdAndPhoto(userId); + } + /** * 根据菜单id获取所有下级对象 * @param pid 菜单id diff --git a/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/impl/UserServiceImpl.java b/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/impl/UserServiceImpl.java index ed119dc..0933212 100644 --- a/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/impl/UserServiceImpl.java +++ b/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/impl/UserServiceImpl.java @@ -1095,6 +1095,7 @@ public class UserServiceImpl extends ServiceImpl implements UserS user.setSpreadTime(DateUtil.nowDateTime()); user.setSex(Integer.parseInt(thirdUserRequest.getSex())); user.setAddres(thirdUserRequest.getCountry() + "," + thirdUserRequest.getProvince() + "," + thirdUserRequest.getCity()); + user.setUnionId(thirdUserRequest.getUnionId()); return user; } diff --git a/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/impl/WechatFansServiceImpl.java b/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/impl/WechatFansServiceImpl.java new file mode 100644 index 0000000..91665b0 --- /dev/null +++ b/crmeb/crmeb-service/src/main/java/com/zbkj/service/service/impl/WechatFansServiceImpl.java @@ -0,0 +1,270 @@ +package com.zbkj.service.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zbkj.common.model.wechat.WechatFans; +import com.zbkj.common.utils.RestTemplateUtil; +import com.zbkj.service.dao.WechatFansDao; +import com.zbkj.service.service.WechatFansService; +import com.zbkj.service.service.WechatNewService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 公众号关注用户服务实现类 + * +---------------------------------------------------------------------- + * | CRMEB [ CRMEB赋能开发者,助力企业发展 ] + * +---------------------------------------------------------------------- + * | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved. + * +---------------------------------------------------------------------- + * | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权 + * +---------------------------------------------------------------------- + * | Author: CRMEB Team + * +---------------------------------------------------------------------- + */ +@Service +public class WechatFansServiceImpl extends ServiceImpl implements WechatFansService { + + private static final Logger logger = LoggerFactory.getLogger(WechatFansServiceImpl.class); + + @Autowired + private WechatNewService wechatNewService; + + @Autowired + private RestTemplateUtil restTemplateUtil; + + /** + * 获取公众号关注用户列表URL + */ + private static final String GET_FANS_URL = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=%s&next_openid=%s"; + + /** + * 批量获取用户信息URL + */ + private static final String BATCH_GET_USER_INFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=%s"; + + /** + * 发送一次性订阅消息URL + */ + private static final String SEND_SUBSCRIBE_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/subscribe?access_token=%s"; + + @Override + @Transactional(rollbackFor = Exception.class) + public Integer syncFans() { + int totalCount = 0; + String nextOpenId = ""; + String accessToken = wechatNewService.getPublicAccessToken(); + + try { + do { + String url = String.format(GET_FANS_URL, accessToken, nextOpenId); + String result = restTemplateUtil.getLink(url); + JSONObject jsonObject = JSONObject.parseObject(result); + + if (jsonObject.containsKey("errcode") && jsonObject.getInteger("errcode") != 0) { + logger.error("获取公众号关注用户列表失败: {}", jsonObject.getString("errmsg")); + break; + } + + Integer total = jsonObject.getInteger("total"); + Integer count = jsonObject.getInteger("count"); + nextOpenId = jsonObject.getString("next_openid"); + + if (count != null && count > 0) { + JSONObject data = jsonObject.getJSONObject("data"); + if (data != null) { + JSONArray openIdArray = data.getJSONArray("openid"); + if (openIdArray != null && !openIdArray.isEmpty()) { + List fansList = batchGetUserInfo(accessToken, openIdArray); + + if (!fansList.isEmpty()) { + batchSaveOrUpdate(fansList); + totalCount += fansList.size(); + } + } + } + } + + if (count == null || count == 0 || ObjectUtil.isEmpty(nextOpenId)) { + break; + } + + } while (true); + + logger.info("公众号关注用户同步完成,共同步 {} 个用户", totalCount); + } catch (Exception e) { + logger.error("同步公众号关注用户失败", e); + } + + return totalCount; + } + + private List batchGetUserInfo(String accessToken, JSONArray openIdArray) { + List fansList = new ArrayList<>(); + + try { + for (int i = 0; i < openIdArray.size(); i += 100) { + int end = Math.min(i + 100, openIdArray.size()); + JSONArray batchArray = new JSONArray(); + + for (int j = i; j < end; j++) { + JSONObject item = new JSONObject(); + item.put("openid", openIdArray.getString(j)); + item.put("lang", "zh_CN"); + batchArray.add(item); + } + + JSONObject requestBody = new JSONObject(); + requestBody.put("user_list", batchArray); + + String url = String.format(BATCH_GET_USER_INFO_URL, accessToken); + String result = restTemplateUtil.postStringData(url, requestBody.toJSONString()); + JSONObject jsonObject = JSONObject.parseObject(result); + + if (jsonObject.containsKey("errcode") && jsonObject.getInteger("errcode") != 0) { + logger.error("批量获取用户信息失败: {}", jsonObject.getString("errmsg")); + continue; + } + + JSONArray userInfoList = jsonObject.getJSONArray("user_info_list"); + if (userInfoList != null && !userInfoList.isEmpty()) { + for (int j = 0; j < userInfoList.size(); j++) { + JSONObject userInfo = userInfoList.getJSONObject(j); + WechatFans fans = convertToWechatFans(userInfo); + fansList.add(fans); + } + } + } + } catch (Exception e) { + logger.error("批量获取用户信息异常", e); + } + + return fansList; + } + + private WechatFans convertToWechatFans(JSONObject userInfo) { + WechatFans fans = new WechatFans(); + fans.setOpenId(userInfo.getString("openid")); + fans.setUnionId(userInfo.getString("unionid")); + fans.setNickname(userInfo.getString("nickname")); + fans.setSex(userInfo.getInteger("sex")); + fans.setProvince(userInfo.getString("province")); + fans.setCity(userInfo.getString("city")); + fans.setCountry(userInfo.getString("country")); + fans.setHeadimgurl(userInfo.getString("headimgurl")); + fans.setSubscribe(userInfo.getInteger("subscribe")); + + Long subscribeTime = userInfo.getLong("subscribe_time"); + if (subscribeTime != null) { + fans.setSubscribeTime(new Date(subscribeTime * 1000L)); + } + + fans.setRemark(userInfo.getString("remark")); + fans.setGroupId(userInfo.getInteger("groupid")); + fans.setCreateTime(DateUtil.date()); + fans.setUpdateTime(DateUtil.date()); + + return fans; + } + + @Override + public WechatFans getByOpenId(String openId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(WechatFans::getOpenId, openId); + return getOne(wrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean batchSaveOrUpdate(List list) { + if (list == null || list.isEmpty()) { + return true; + } + + for (WechatFans fans : list) { + WechatFans existing = getByOpenId(fans.getOpenId()); + if (existing != null) { + fans.setId(existing.getId()); + fans.setCreateTime(existing.getCreateTime()); + } + saveOrUpdate(fans); + } + return true; + } + + /** + * 定时同步公众号关注用户(测试用:每分钟执行一次) + * 正式环境建议改为:@Scheduled(cron = "0 0 2 * * ?") 每天凌晨2点执行 + */ +// @Scheduled(cron = "0 * * * * ?") + public void scheduledSyncFans() { + logger.info("========== 开始定时同步公众号关注用户 =========="); + try { + Integer count = syncFans(); + logger.info("定时同步公众号关注用户完成,同步 {} 个用户", count); + } catch (Exception e) { + logger.error("定时同步公众号关注用户失败", e); + } + logger.info("========== 定时同步公众号关注用户结束 =========="); + } + + @Override + public boolean sendSubscribeMessage(String openId, String templateId, String scene, String title, String content, String url) { + try { + String accessToken = wechatNewService.getPublicAccessToken(); + + JSONObject requestBody = new JSONObject(); + requestBody.put("touser", openId); + requestBody.put("template_id", templateId); + requestBody.put("scene", scene); + requestBody.put("title", title); + + if (url != null && !url.isEmpty()) { + requestBody.put("url", url); + } + + JSONObject data = new JSONObject(); + JSONObject contentObj = new JSONObject(); + contentObj.put("value", content); + contentObj.put("color", "#173177"); + data.put("content", contentObj); + requestBody.put("data", data); + + String requestUrl = String.format(SEND_SUBSCRIBE_MESSAGE_URL, accessToken); + String result = restTemplateUtil.postStringData(requestUrl, requestBody.toJSONString()); + JSONObject jsonObject = JSONObject.parseObject(result); + + Integer errcode = jsonObject.getInteger("errcode"); + if (errcode == null || errcode == 0) { + logger.info("发送一次性订阅消息成功,openId: {}", openId); + return true; + } else { + logger.error("发送一次性订阅消息失败,openId: {}, error: {}", openId, jsonObject.getString("errmsg")); + return false; + } + } catch (Exception e) { + logger.error("发送一次性订阅消息异常", e); + return false; + } + } + + @Override + public String getOpenIdByUnionId(String unionId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(WechatFans::getUnionId, unionId); + WechatFans fans = getOne(wrapper); + return fans != null ? fans.getOpenId() : null; + } +} \ No newline at end of file diff --git a/crmeb/crmeb-service/src/main/java/com/zbkj/service/util/WeiXinUtil.java b/crmeb/crmeb-service/src/main/java/com/zbkj/service/util/WeiXinUtil.java index 2fde833..dabed0b 100644 --- a/crmeb/crmeb-service/src/main/java/com/zbkj/service/util/WeiXinUtil.java +++ b/crmeb/crmeb-service/src/main/java/com/zbkj/service/util/WeiXinUtil.java @@ -37,14 +37,14 @@ public class WeiXinUtil { // 日期格式 private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 微信小程序appid - private static String appid="wx72fabe1da6e6fb82" ; + private static String appid="wxacb083448570908a" ; // 微信小程序appsecret - private static String appsecret="24479b73fc113f01048e7236378c6ce8"; + private static String appsecret="b9289b898e0bc822827fa8f65dfb71bd"; // 微信公众号appid - private static String gzhappid="wxe8539d3b62c9a06c"; + private static String gzhappid="wx49d7d3aba9479a85"; // 微信公众号appsecret - private static String gzhappsecret="84f607283501d0177ca7595f709c20ca"; + private static String gzhappsecret="d4aaa97c1a169cede51f3dbb6c44c574"; // 微信开放号appid private static String openAppid = ""; diff --git a/crmeb/crmeb-service/src/main/resources/mapper/system/SystemMenuMapper.xml b/crmeb/crmeb-service/src/main/resources/mapper/system/SystemMenuMapper.xml index f3b868d..87fe5e6 100644 --- a/crmeb/crmeb-service/src/main/resources/mapper/system/SystemMenuMapper.xml +++ b/crmeb/crmeb-service/src/main/resources/mapper/system/SystemMenuMapper.xml @@ -24,4 +24,15 @@ ORDER BY m.sort ASC, m.id ASC + +