Compare commits
284 Commits
7db0796c2f
...
test
| Author | SHA1 | Date | |
|---|---|---|---|
| 206e4e786b | |||
| 0179a35942 | |||
| 4de755ca60 | |||
| 358e5a46b9 | |||
| ee36c8c902 | |||
| 7e699fb80e | |||
| 80d639d887 | |||
| 87ae3e6674 | |||
| 359c3c18e4 | |||
| 4f2f2db20f | |||
| 0094e4aaff | |||
| 8b7c346ec4 | |||
| bde1d7d86e | |||
| 87eab12d51 | |||
| 880b4751c5 | |||
| 3a104d2d8c | |||
| a024b45940 | |||
| 6675808dfc | |||
| 1ba431540d | |||
| 0d0613e810 | |||
| 47b95b6bfd | |||
| e7fd7f9059 | |||
| 1ffaf424b1 | |||
| 8ca5af1ff0 | |||
| 621128530a | |||
| fb9a4e1b29 | |||
| 89aadf97db | |||
| 90147ed4e3 | |||
| cf336f7a67 | |||
| d2c75e0c51 | |||
| 17ee9731e8 | |||
| 146d3469b6 | |||
| 27715b3e13 | |||
| b5299e4490 | |||
| 82164cb00c | |||
| 34fa8e23c1 | |||
| efd92b5663 | |||
| 7869205afc | |||
| f82d9c667f | |||
| 7e1041391a | |||
| 99cdfd86d3 | |||
| 41a80f9794 | |||
| 8cb2d03062 | |||
| 4ceb0bc830 | |||
| f7384c88f6 | |||
| beba9e9caa | |||
| b7d0674fda | |||
| 0545de88b4 | |||
| 2430e344a0 | |||
| 5db27e00ba | |||
| a06962d1dd | |||
| 5f679b5a6e | |||
| 02763f092e | |||
| b6718f9361 | |||
| a58de9bb7e | |||
| a211f06663 | |||
| 41cefd5fd5 | |||
| fca999c384 | |||
| 2e5ab2bfc1 | |||
| 3e5ef25153 | |||
| 9004a1b921 | |||
| cf6b90008d | |||
| fbc138f041 | |||
| 3c49175cea | |||
| f26e03efb1 | |||
| 1a0483c7fe | |||
| da9efc422e | |||
| e6a9fcf0a2 | |||
| de5784cbac | |||
| 16afa0d590 | |||
| 2632e5aa78 | |||
| ace1eb06c0 | |||
| f31bb4c54d | |||
| b8b943ef51 | |||
| f2d27f3502 | |||
| 9220696519 | |||
| 8633019788 | |||
| 4148c447fc | |||
| 61099b2514 | |||
| 9e3f8314f1 | |||
| 7abb7fdb06 | |||
| cd25055159 | |||
| 489f99dc7b | |||
| af3fb0aa2f | |||
| e4c16263ab | |||
| 020aace3f9 | |||
| 709c315971 | |||
| 6f342d424f | |||
| a2e986fa6a | |||
| 7807534e5d | |||
| e415843b14 | |||
| b4da248815 | |||
| 1090d838a6 | |||
| 6ced4f237f | |||
| 87c75922aa | |||
| 44df6b1a1b | |||
| 270b762443 | |||
| 36433863af | |||
| 04ce44647a | |||
| fde53681d1 | |||
| 93c081775d | |||
| 25801fe42f | |||
| 891fdea7d3 | |||
| 1727386811 | |||
| 218001e6ae | |||
| 308526ecee | |||
| 116b3b74e4 | |||
| 557d43a0b8 | |||
| 942d5932ae | |||
| 51a89b18e1 | |||
| 846a41c6df | |||
| 04be15deab | |||
| 328702a618 | |||
| 910a73ed28 | |||
| 5cccbf510f | |||
| 93da04fe5e | |||
| 1ebc86ddcf | |||
| 6b111088c8 | |||
| 8d4e3363ae | |||
| 7399174bd6 | |||
| 8208a7393f | |||
| 826d95d3c1 | |||
| 071a1d7f55 | |||
| c6509fca51 | |||
| f0012ac563 | |||
| 98ae525403 | |||
| c87f49a14b | |||
| 6b78e13e5c | |||
| 86255c45f6 | |||
| 844b501686 | |||
| 229f1f9bad | |||
| 42d12a3fa3 | |||
| 3481cfd634 | |||
| 7d3854bb4f | |||
| e93f0c0233 | |||
| 1b1b934d87 | |||
| 1ba5822a86 | |||
| 17e80b3a18 | |||
| 9d4dbdcbe6 | |||
| d185995dc7 | |||
| bc33e11437 | |||
| 8ed97fed4a | |||
| 6b10a41b9d | |||
| 02c49e3efe | |||
| b091d47f64 | |||
| c3456ca92a | |||
| 59eb4b23cc | |||
| fc21b08318 | |||
| b72f3a7014 | |||
| eafc8af89a | |||
| d02d3dd881 | |||
| 3bb7267b0b | |||
| 1d47d5781b | |||
| 5f082d905d | |||
| a5e9511857 | |||
| 56340d0ea0 | |||
| 786feec859 | |||
| 1eb0e45513 | |||
| 1688cddac7 | |||
| d7f49cef58 | |||
| df7220d333 | |||
| 00a690abe9 | |||
| 8ed7b3146b | |||
| 1e4711326c | |||
| 2bd417b84e | |||
| 3652546ea4 | |||
| 1a0dc9c982 | |||
| 173e22a58e | |||
| acfdfd5cbf | |||
| db787ec7f7 | |||
| b4e696b27c | |||
| 4238c31d9d | |||
| b55f0010ed | |||
| 4067bfc277 | |||
| fe30345045 | |||
| 4d4065b9e9 | |||
| 0ba975656b | |||
| 65d382d256 | |||
| 7fcd3b6089 | |||
| 7e53b41a3c | |||
| 4659c63ef9 | |||
| f3fa3e1396 | |||
| be0b067516 | |||
| f80af4a2f6 | |||
| 7924450b89 | |||
| 2bae431e74 | |||
| 8fba64e2d6 | |||
| 7e175808bf | |||
| 74762c8043 | |||
| bdce5e3086 | |||
| 2ed0e0c77d | |||
| b3374fa44c | |||
| 76cc5b5781 | |||
| 7f4fc3df8c | |||
| 209d545d5b | |||
| 90a0194a99 | |||
| ab6216922a | |||
| 4bee108661 | |||
| 2bb562372e | |||
| 55b5ed8951 | |||
| 48d938c4f7 | |||
| dac0c0f252 | |||
| de34cad95d | |||
| d46d1596e8 | |||
| 84939a8875 | |||
| fcebf9282c | |||
| fc94a15f76 | |||
| 4ec28de073 | |||
| 23236ec141 | |||
| d010783c97 | |||
| f07386f4aa | |||
| 6fb0ba3edc | |||
| c064d925ca | |||
| 92000956fc | |||
| c04191c316 | |||
| b8e2a8e31a | |||
| 1e8a449dda | |||
| 26a3285571 | |||
| 9e61f7f55c | |||
| a4440d7932 | |||
| 35f1048190 | |||
| 8f1cf5c755 | |||
| 36837f94bf | |||
| dd2efef456 | |||
| d7f7ebd981 | |||
| be3856a59c | |||
| 501056bb17 | |||
| c30c88bbe8 | |||
| dba75c3a77 | |||
| e41c7b072f | |||
| 2d5a2c4bab | |||
| 66d95a0da9 | |||
| 7655713319 | |||
| eca8855b8d | |||
| acf4757144 | |||
| 7063dc7218 | |||
| 9166220579 | |||
| b772c95813 | |||
| 29e804dae2 | |||
| 45969a4d29 | |||
| 8093cb52e7 | |||
| 58d7ec90b0 | |||
| 383ef55e00 | |||
| a754e8dcc6 | |||
| 1ae97cd622 | |||
| 1986a901d9 | |||
| 5bb9000e8e | |||
| df1a2fd731 | |||
| b945c9f37b | |||
| 6b570f948c | |||
| 374c8b8bbb | |||
| 69852907f3 | |||
| 92fc2f1ad0 | |||
| a48b40c649 | |||
| 3cf85af180 | |||
| a19d97d111 | |||
| 60d4614695 | |||
| db6f5e882a | |||
| 52f714c760 | |||
| 9ff89e36ee | |||
| 6a07786e75 | |||
| a15a96f881 | |||
| 1d22bd91fd | |||
| db396458c5 | |||
| e227c6a80f | |||
| eb67ccc3cb | |||
| 9e391888f5 | |||
| b0719d2ae9 | |||
| 15e005fd20 | |||
| 9824e05919 | |||
| b54cb1b018 | |||
| b3f79ffc4c | |||
| 2431a5caac | |||
| 760e3807f3 | |||
| 132688b9cf | |||
| 2689138b6a | |||
| 622d2a4c40 | |||
| 02481b102c | |||
| ca010a4242 | |||
| f92da4782d | |||
| 0a7f639138 | |||
| 5126a5c163 | |||
| a22039defc | |||
| 085d01dc15 |
@@ -0,0 +1,38 @@
|
||||
# 应收账单行级权限
|
||||
|
||||
规则独立于 `@DataScope` 和角色管理的 `dataScope`,现有菜单、按钮权限保持不变。
|
||||
|
||||
| roleKey | 数据范围 |
|
||||
| --- | --- |
|
||||
| ORA_6041 / ORA_6163 | 销售员、商务专员:本人负责客户 |
|
||||
| ORA_6040 | 本部门人员负责客户,不含下级部门 |
|
||||
| ORA_6042 / ORA_6043 | 财务经理、应收财务:全部账单 |
|
||||
|
||||
超级管理员沿用系统判断。仅参与当前入口功能授权的有效角色合并范围;未知角色、空权限对象默认无数据。财务本期不使用账单 `create_user_id`。应付财务不参与应收账单权限规则。
|
||||
|
||||
归属链为 `ar_bill.customer_id → ctr_customer.sales_user_id → sys_user.user_id / dept_id`。销售员和商务专员均通过客户的 `sales_user_id` 与当前登录用户 ID 匹配本人客户。客户转交后,历史账单跟随当前归属变化。全量角色可见未关联销售、销售已删除、客户已删除的历史账单;受限角色不放行无有效归属的数据。业务员仅停用但未删除时,其部门经理仍可查看。
|
||||
|
||||
核心实现位于 `bms-system/src/main/java/com/bms/ar/security` 和 `mapper/ar/ArBillScopeMapper.xml`。内部权限对象作为独立 Mapper 参数传递,不绑定 HTTP 查询对象;后台生成和导出任务继续使用独立的内部读取方法。
|
||||
|
||||
接入范围包括列表、汇总、列表导出、详情/费用、日志、生成预览及提交、生成任务、导出任务及下载、客户账单入口,以及客户/收款页面的待核销账单、核销记录、核销和反核销。应收筛选及生成客户选项分别使用 `/ar/bill/customer-options`、`/ar/bill/generate/customer-options`。
|
||||
|
||||
用户编辑、调部门、停用、分配角色,以及角色编辑、停用、删除和授权关系调整,会在事务提交后让受影响的登录会话失效;相关用户需要重新登录。不改变角色配置本身。
|
||||
|
||||
## 本地验证
|
||||
|
||||
在 `bms-back` 下执行(PowerShell):
|
||||
|
||||
```powershell
|
||||
mvn -o -pl bms-admin -am -DskipTests compile
|
||||
```
|
||||
|
||||
本次新增的行级权限测试用例及其辅助脚本已按要求删除。
|
||||
|
||||
在 `bms-front` 下执行 `npm run build:prod` 验证前端构建。
|
||||
|
||||
## 部署前核实
|
||||
|
||||
- 本次没有新增数据库字段、权限配置表或迁移 SQL。
|
||||
- 目标环境 `ctr_customer.sales_user_id` 必须已经统一为系统用户 ID,不能仍是旧业务员工号。现有迁移文件位于 `sql/customer+supplier/20261028_customer_sales_user_migration.sql`,需要按原有流程确认执行状态,本次不自动执行。
|
||||
- 使用真实销售 A/B、经理、财务和商务专员账号检查数据范围与功能授权,特别是生产角色实际拥有的接口权限。
|
||||
- 上线前仍需在 MySQL 5.7 环境联调,并检查实际数据的执行计划。
|
||||
@@ -28,6 +28,7 @@ public class BmsApplication
|
||||
" | (_,_).' __ ___(_ o _)' \n" +
|
||||
" | |\\ \\ | || |(_,_)' \n" +
|
||||
" | | \\ `' /| `-' / \n" +
|
||||
|
||||
" | | \\ / \\ / \n" +
|
||||
" ''-' `'-' `-..-' ");
|
||||
}
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ import com.bms.price.search.IBillSearchStore;
|
||||
import com.bms.web.controller.price.BillingController;
|
||||
|
||||
/**
|
||||
* Persist quote/bill API failures to Elasticsearch (validation, JSON parse, business, unexpected).
|
||||
* Uses {@link EsBizLogSupport#saveApiFail} so service/controller can also call without duplicate docs.
|
||||
* 把报价/计费接口失败写入 Elasticsearch(校验、JSON 解析、业务异常、未预期异常)。
|
||||
* 通过 {@link EsBizLogSupport#saveApiFail} 写入,service/controller 也可调用且不会重复落文档。
|
||||
*/
|
||||
@Order(0)
|
||||
@RestControllerAdvice(assignableTypes = { TmsPriceController.class, BillingController.class })
|
||||
|
||||
+12
-12
@@ -29,10 +29,10 @@ import com.bms.price.service.IPrcOrderBillService;
|
||||
import com.bms.price.service.ITmsPriceQuoteService;
|
||||
|
||||
/**
|
||||
* TMS open API: freight quote.
|
||||
* TMS 开放接口:运费试算。
|
||||
*
|
||||
* POST /api/tms/price/quote
|
||||
* Header X-Api-Code must equal bms.tms.api-code
|
||||
* 请求头 X-Api-Code 必须等于 bms.tms.api-code
|
||||
*
|
||||
* @author bms
|
||||
*/
|
||||
@@ -59,9 +59,9 @@ public class TmsPriceController extends BaseController
|
||||
private IBillSearchStore billSearchStore;
|
||||
|
||||
/**
|
||||
* Quote by customer + product + country + weight (+ destPostcode when country has zones).
|
||||
* Zone resolve: destPostcode -> prc_zone_detail/prc_zone -> product_country.zone_code.
|
||||
* Match: customer package FIXED > DISCOUNT > product default.
|
||||
* 按客户 + 产品 + 国家 + 重量试算(国家配置了分区时需传 destPostcode)。
|
||||
* 分区解析:destPostcode -> prc_zone_detail/prc_zone -> product_country.zone_code。
|
||||
* 匹配顺序:客户套餐 FIXED > DISCOUNT > 产品默认价。
|
||||
*/
|
||||
@PostMapping("/quote")
|
||||
public AjaxResult quote(
|
||||
@@ -83,9 +83,9 @@ public class TmsPriceController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync order and persist prc_order_bill / prc_income_bill_fee.
|
||||
* Same price match as quote (bound set + active version by bizDate, else default).
|
||||
* Same orderNo upserts.
|
||||
* 同步订单并落库 prc_order_bill / prc_income_bill_fee。
|
||||
* 价格匹配与试算一致(绑定套餐 + 按 bizDate 取生效版本,否则取默认价)。
|
||||
* 相同 orderNo 执行 upsert。
|
||||
*/
|
||||
@PostMapping("/bill")
|
||||
public AjaxResult bill(
|
||||
@@ -106,8 +106,8 @@ public class TmsPriceController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Warehouse inbound: create billing task (PENDING/REBILL/REFUND) and publish MQ.
|
||||
* Consumer queries Oracle by transferNo/orderNo then inserts prc_order_bill.
|
||||
* 仓库入库:创建计费任务(PENDING/REBILL/REFUND)并发送 MQ。
|
||||
* 消费端按 transferNo/orderNo 查询 Oracle,然后插入 prc_order_bill。
|
||||
* POST /api/tms/price/billing-task
|
||||
*/
|
||||
@PostMapping("/billing-task")
|
||||
@@ -121,7 +121,7 @@ public class TmsPriceController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Query billing task writeback status.
|
||||
* 查询计费任务回写状态。
|
||||
* GET /api/tms/price/billing-task/{taskId}
|
||||
*/
|
||||
@GetMapping("/billing-task/{taskId}")
|
||||
@@ -135,7 +135,7 @@ public class TmsPriceController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Query order bill by orderNo after sync.
|
||||
* 同步完成后按 orderNo 查询订单账单。
|
||||
*/
|
||||
@GetMapping("/bill/by-order/{orderNo}")
|
||||
public AjaxResult getBillByOrder(
|
||||
|
||||
+78
-7
@@ -11,6 +11,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.bms.common.config.BmsConfig;
|
||||
@@ -18,6 +19,8 @@ import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.common.utils.file.FileUploadUtils;
|
||||
import com.bms.common.utils.file.FileUtils;
|
||||
import com.bms.common.utils.file.MimeTypeUtils;
|
||||
import com.bms.common.utils.file.OssStorage;
|
||||
import com.bms.framework.config.ServerConfig;
|
||||
|
||||
/**
|
||||
@@ -34,6 +37,9 @@ public class CommonController
|
||||
@Autowired
|
||||
private ServerConfig serverConfig;
|
||||
|
||||
@Autowired
|
||||
private OssStorage ossStorage;
|
||||
|
||||
private static final String FILE_DELIMITER = ",";
|
||||
|
||||
/**
|
||||
@@ -94,6 +100,56 @@ public class CommonController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用上传请求(OSS 存储,返回 oss:// 存储标识)。
|
||||
* 客户附件、公司主体、收款凭证等业务附件走此接口;
|
||||
* 下载统一经 /common/download/resource?resource=oss://... 鉴权转发。
|
||||
*/
|
||||
@PostMapping("/upload/oss")
|
||||
public AjaxResult uploadFileOss(MultipartFile file,
|
||||
@RequestParam(value = "module", required = false) String module) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
if (file == null || file.isEmpty())
|
||||
{
|
||||
return AjaxResult.error("上传文件不能为空");
|
||||
}
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
// 扩展名白名单与大小校验(默认 50MB,图片/文档/压缩包/视频/pdf)
|
||||
FileUploadUtils.assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
|
||||
String extension = "";
|
||||
int dotIndex = originalFilename != null ? originalFilename.lastIndexOf('.') : -1;
|
||||
if (dotIndex >= 0)
|
||||
{
|
||||
extension = originalFilename.substring(dotIndex + 1).toLowerCase();
|
||||
}
|
||||
// module 仅允许字母数字下划线中划线,防止对象 key 注入
|
||||
String moduleKey = StringUtils.defaultIfBlank(module, "misc").replaceAll("[^a-zA-Z0-9_-]", "");
|
||||
if (moduleKey.isEmpty())
|
||||
{
|
||||
moduleKey = "misc";
|
||||
}
|
||||
String datePath = new java.text.SimpleDateFormat("yyyy/MM/dd").format(new java.util.Date());
|
||||
String objectKey = "attachment/" + moduleKey + "/" + datePath + "/"
|
||||
+ java.util.UUID.randomUUID().toString().replace("-", "") + "." + extension;
|
||||
String storageKey = ossStorage.upload(file.getInputStream(), file.getSize(), objectKey, file.getContentType());
|
||||
String downloadUrl = "/common/download/resource?resource="
|
||||
+ java.net.URLEncoder.encode(storageKey, java.nio.charset.StandardCharsets.UTF_8);
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
ajax.put("url", downloadUrl);
|
||||
ajax.put("fileName", storageKey);
|
||||
ajax.put("newFileName", originalFilename);
|
||||
ajax.put("originalFilename", originalFilename);
|
||||
return ajax;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("upload file to oss failed", e);
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用上传请求(多个)
|
||||
*/
|
||||
@@ -132,7 +188,7 @@ public class CommonController
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地资源通用下载
|
||||
* 通用资源下载:本地 profile 路径或 oss://bucket/key(AR 账单风格)。
|
||||
*/
|
||||
@GetMapping("/download/resource")
|
||||
public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
|
||||
@@ -140,15 +196,30 @@ public class CommonController
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!FileUtils.checkAllowDownload(resource))
|
||||
if (StringUtils.isEmpty(resource))
|
||||
{
|
||||
throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
|
||||
throw new Exception("resource empty");
|
||||
}
|
||||
boolean ossFile = resource.startsWith("oss://");
|
||||
if (!ossFile && !FileUtils.checkAllowDownload(resource))
|
||||
{
|
||||
throw new Exception(StringUtils.format("resource ({}) not allowed", resource));
|
||||
}
|
||||
if (ossFile)
|
||||
{
|
||||
// 扩展名校验仍适用于对象 key 后缀。
|
||||
if (!FileUtils.checkAllowDownload(resource))
|
||||
{
|
||||
throw new Exception(StringUtils.format("resource ({}) not allowed", resource));
|
||||
}
|
||||
String downloadName = StringUtils.substringAfterLast(resource, "/");
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
FileUtils.setAttachmentResponseHeader(response, downloadName);
|
||||
ossStorage.download(resource, response.getOutputStream());
|
||||
return;
|
||||
}
|
||||
// 本地资源路径
|
||||
String localPath = BmsConfig.getProfile();
|
||||
// 数据库资源地址
|
||||
String downloadPath = localPath + FileUtils.stripPrefix(resource);
|
||||
// 下载名称
|
||||
String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
FileUtils.setAttachmentResponseHeader(response, downloadName);
|
||||
@@ -156,7 +227,7 @@ public class CommonController
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("下载文件失败", e);
|
||||
log.error("download resource failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
@@ -5,6 +5,7 @@ import java.util.List;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerAttachment;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerCompany;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerContact;
|
||||
import com.bms.customer.customerinfo.vo.UnlinkedSalesCustomerVO;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -22,6 +23,7 @@ import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomer;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerOption;
|
||||
import com.bms.customer.customerinfo.service.IBmsCustomerService;
|
||||
import com.bms.common.utils.poi.ExcelUtil;
|
||||
import com.bms.common.core.page.TableDataInfo;
|
||||
@@ -51,6 +53,32 @@ public class BmsCustomerController extends BaseController
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/** 查询未关联业务员的客户列表 */
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
|
||||
@GetMapping("/unlinked-sales-user/list")
|
||||
public TableDataInfo unlinkedSalesUserList()
|
||||
{
|
||||
startPage();
|
||||
BmsCustomer query = new BmsCustomer();
|
||||
List<UnlinkedSalesCustomerVO> list = bmsCustomerService.selectUnlinkedSalesUserCustomerList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户下拉选项。
|
||||
* 只返回客户ID、客户编码、客户名称,供各页面下拉框使用。
|
||||
* 查询参数 keywords 按客户名称、客户编码、对外代码模糊匹配,多行之间为或者关系。
|
||||
* 分页参数与原下拉一致,避免一次查出全部客户。
|
||||
*/
|
||||
// @PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
|
||||
@GetMapping("/optionselect")
|
||||
public TableDataInfo optionselect(BmsCustomer query)
|
||||
{
|
||||
startPage();
|
||||
List<BmsCustomerOption> list = bmsCustomerService.selectCustomerOptions(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出客户信息列表
|
||||
*/
|
||||
|
||||
+65
-3
@@ -1,7 +1,9 @@
|
||||
package com.bms.web.controller.customer.customermoney;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -11,16 +13,22 @@ import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.bms.ar.domain.ArBalanceWriteoff;
|
||||
import com.bms.ar.service.IArBalanceWriteoffService;
|
||||
import com.bms.receivable.domain.ArBill;
|
||||
import com.bms.receivable.domain.ArBalanceWriteoff;
|
||||
import com.bms.receivable.domain.vo.ArBillListExportRow;
|
||||
import com.bms.receivable.domain.vo.ArBillQuery;
|
||||
import com.bms.receivable.domain.vo.ArBillSummary;
|
||||
import com.bms.receivable.service.IArBalanceWriteoffService;
|
||||
import com.bms.receivable.service.IArBillService;
|
||||
import com.bms.common.annotation.Log;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.core.page.TableDataInfo;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.common.utils.poi.ExcelUtil;
|
||||
|
||||
/**
|
||||
* 客户余额核销应收账单 Controller(账单查看统一走应收管理-应收账单页面)
|
||||
* 客户余额账单查询与核销 Controller
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-09-16
|
||||
@@ -32,6 +40,60 @@ public class CustomerBillController extends BaseController
|
||||
@Autowired
|
||||
private IArBalanceWriteoffService balanceWriteoffService;
|
||||
|
||||
@Autowired
|
||||
private IArBillService billService;
|
||||
|
||||
/**
|
||||
* 查询指定客户的应收账单(分页)。客户ID来自路径并覆盖查询对象中的同名参数。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:bill')")
|
||||
@GetMapping("/list/{customerId}")
|
||||
public AjaxResult billList(@PathVariable("customerId") Long customerId, ArBillQuery query)
|
||||
{
|
||||
if (customerId == null || customerId <= 0L)
|
||||
{
|
||||
AjaxResult result = success();
|
||||
result.put("rows", Collections.emptyList());
|
||||
result.put("total", 0L);
|
||||
result.put("summary", new ArBillSummary());
|
||||
return result;
|
||||
}
|
||||
ArBillQuery billQuery = query == null ? new ArBillQuery() : query;
|
||||
billQuery.setCustomerId(customerId);
|
||||
startPage();
|
||||
List<ArBill> list = billService.selectArBillList(billQuery);
|
||||
TableDataInfo pageData = getDataTable(list);
|
||||
AjaxResult result = success();
|
||||
result.put("rows", pageData.getRows());
|
||||
result.put("total", pageData.getTotal());
|
||||
result.put("summary", billService.selectArBillSummary(billQuery));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按当前筛选条件导出指定客户的全部应收账单,不受列表分页参数影响。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:bill') and @ss.hasPermi('customersupplier:customerinfo:export')")
|
||||
@Log(title = "客户账单", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export/{customerId}")
|
||||
public void export(HttpServletResponse response, @PathVariable("customerId") Long customerId, ArBillQuery query)
|
||||
{
|
||||
List<ArBillListExportRow> rows = new ArrayList<>();
|
||||
if (customerId != null && customerId > 0L)
|
||||
{
|
||||
ArBillQuery billQuery = query == null ? new ArBillQuery() : query;
|
||||
// 客户ID固定取自路径,忽略请求参数中可能出现的 customerId。
|
||||
billQuery.setCustomerId(customerId);
|
||||
List<ArBill> bills = billService.selectArBillList(billQuery);
|
||||
for (ArBill bill : bills)
|
||||
{
|
||||
rows.add(ArBillListExportRow.from(bill));
|
||||
}
|
||||
}
|
||||
ExcelUtil<ArBillListExportRow> util = new ExcelUtil<>(ArBillListExportRow.class);
|
||||
util.exportExcel(response, rows, "客户账单");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询客户可核销账单(已确认/部分核销,未核销额大于0)
|
||||
*/
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.bms.web.controller.data;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.bms.common.annotation.Log;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.core.page.TableDataInfo;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.system.domain.BmsBizRule;
|
||||
import com.bms.system.service.IBmsBizRuleService;
|
||||
|
||||
/**
|
||||
* 规则配置
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/data/bizRule")
|
||||
public class BmsBizRuleController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IBmsBizRuleService bizRuleService;
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:bizRule:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(BmsBizRule query)
|
||||
{
|
||||
startPage();
|
||||
List<BmsBizRule> list = bizRuleService.selectList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:bizRule:query')")
|
||||
@GetMapping("/{ruleId}")
|
||||
public AjaxResult getInfo(@PathVariable Long ruleId)
|
||||
{
|
||||
return success(bizRuleService.selectById(ruleId));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:bizRule:add')")
|
||||
@Log(title = "biz rule", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody BmsBizRule row)
|
||||
{
|
||||
row.setCreateBy(getUsername());
|
||||
return toAjax(bizRuleService.insert(row));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:bizRule:edit')")
|
||||
@Log(title = "biz rule", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody BmsBizRule row)
|
||||
{
|
||||
row.setUpdateBy(getUsername());
|
||||
return toAjax(bizRuleService.update(row));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:bizRule:remove')")
|
||||
@Log(title = "biz rule", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ruleIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] ruleIds)
|
||||
{
|
||||
return toAjax(bizRuleService.deleteByIds(ruleIds));
|
||||
}
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
package com.bms.web.controller.data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.bms.common.annotation.Log;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.core.page.TableDataInfo;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.common.enums.PaymentMesEnum;
|
||||
import com.bms.price.domain.PayPostcodeGroup;
|
||||
import com.bms.price.domain.PayPostcodeLine;
|
||||
import com.bms.price.service.IPayPostcodeService;
|
||||
|
||||
/**
|
||||
* 数据管理下的应付邮编配置。Excel 导入后按国家和附加费金额合并。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/data/payPostcode")
|
||||
public class PayPostcodeController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IPayPostcodeService payPostcodeService;
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(PayPostcodeGroup query)
|
||||
{
|
||||
startPage();
|
||||
List<PayPostcodeGroup> list = payPostcodeService.selectCountryList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:query')")
|
||||
@GetMapping("/zones")
|
||||
public AjaxResult zones(PayPostcodeGroup query)
|
||||
{
|
||||
return success(payPostcodeService.selectZoneList(query));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:edit')")
|
||||
@Log(title = "应付邮编配置", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/zones")
|
||||
public AjaxResult addZone(@RequestBody PayPostcodeGroup zone)
|
||||
{
|
||||
return toAjax(payPostcodeService.addZone(zone, getUsername()));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:edit')")
|
||||
@Log(title = "应付邮编配置", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/zones")
|
||||
public AjaxResult editZone(@RequestBody PayPostcodeGroup zone)
|
||||
{
|
||||
return toAjax(payPostcodeService.updateZone(zone));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:remove')")
|
||||
@Log(title = "应付邮编配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/zones")
|
||||
public AjaxResult removeZone(PayPostcodeGroup zone)
|
||||
{
|
||||
return toAjax(payPostcodeService.deleteZone(zone));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:edit')")
|
||||
@Log(title = "应付邮编配置", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/country")
|
||||
public AjaxResult editCountry(@RequestBody PayPostcodeGroup country)
|
||||
{
|
||||
return toAjax(payPostcodeService.updateCountry(country));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:remove')")
|
||||
@Log(title = "应付邮编配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/country")
|
||||
public AjaxResult removeCountry(PayPostcodeGroup country)
|
||||
{
|
||||
return toAjax(payPostcodeService.deleteCountry(country));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:edit')")
|
||||
@Log(title = "应付邮编配置", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/lines")
|
||||
public AjaxResult addLine(@RequestBody PayPostcodeLine line)
|
||||
{
|
||||
return toAjax(payPostcodeService.addLine(line));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:edit')")
|
||||
@Log(title = "应付邮编配置", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/lines")
|
||||
public AjaxResult editLine(@RequestBody PayPostcodeLine line)
|
||||
{
|
||||
return toAjax(payPostcodeService.updateLine(line));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:remove')")
|
||||
@Log(title = "应付邮编配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/lines/{lineIds}")
|
||||
public AjaxResult removeLines(@PathVariable Long[] lineIds)
|
||||
{
|
||||
return toAjax(payPostcodeService.deleteLines(lineIds));
|
||||
}
|
||||
|
||||
/** 按国家去重区块,供附加费编辑分区下拉 */
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:list')")
|
||||
@GetMapping("/blockOptions")
|
||||
public AjaxResult blockOptions(@RequestParam(required = false) String countryCode,
|
||||
@RequestParam(required = false) String countryName)
|
||||
{
|
||||
return success(payPostcodeService.selectBlockTitlesByCountry(countryCode, countryName));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:query')")
|
||||
@GetMapping("/{groupId}")
|
||||
public AjaxResult getInfo(@PathVariable Long groupId)
|
||||
{
|
||||
return success(payPostcodeService.selectGroupById(groupId));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:edit')")
|
||||
@Log(title = "应付邮编配置", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody PayPostcodeGroup group)
|
||||
{
|
||||
return toAjax(payPostcodeService.updateGroup(group));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:query')")
|
||||
@GetMapping("/line")
|
||||
public TableDataInfo lines(PayPostcodeLine query)
|
||||
{
|
||||
startPage();
|
||||
List<PayPostcodeLine> list = payPostcodeService.selectLineList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:import')")
|
||||
@Log(title = "应付邮编配置", businessType = BusinessType.IMPORT)
|
||||
@PostMapping(value = "/import", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public AjaxResult importExcel(MultipartFile file) throws Exception
|
||||
{
|
||||
if (file == null || file.isEmpty())
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_PICK_XLS.exception();
|
||||
}
|
||||
String name = file.getOriginalFilename() == null ? "" : file.getOriginalFilename().toLowerCase(Locale.ROOT);
|
||||
if (!name.endsWith(".xlsx"))
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_XLSX_ONLY.exception();
|
||||
}
|
||||
return success(payPostcodeService.importExcel(file.getBytes(), file.getOriginalFilename(), getUsername()));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:payPostcode:remove')")
|
||||
@Log(title = "应付邮编配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{groupIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] groupIds)
|
||||
{
|
||||
return toAjax(payPostcodeService.deleteByIds(groupIds));
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package com.bms.web.controller.masterdata;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.bms.common.annotation.Log;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.core.page.TableDataInfo;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.common.utils.poi.ExcelUtil;
|
||||
import com.bms.masterdata.domain.LineProduct;
|
||||
import com.bms.masterdata.service.ILineProductService;
|
||||
|
||||
/**
|
||||
* 路线产品管理(srv_line_product)
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/masterdata/lineproduct")
|
||||
public class LineProductController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ILineProductService lineProductService;
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:lineProduct:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(LineProduct query)
|
||||
{
|
||||
startPage();
|
||||
List<LineProduct> list = lineProductService.selectLineProductList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:lineProduct:export')")
|
||||
@Log(title = "路线产品", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, LineProduct query)
|
||||
{
|
||||
List<LineProduct> list = lineProductService.selectLineProductList(query);
|
||||
ExcelUtil<LineProduct> util = new ExcelUtil<LineProduct>(LineProduct.class);
|
||||
util.exportExcel(response, list, "路线产品");
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:lineProduct:query')")
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getInfo(@PathVariable String id)
|
||||
{
|
||||
return success(lineProductService.selectLineProductById(id));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:lineProduct:add')")
|
||||
@Log(title = "路线产品", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody LineProduct product)
|
||||
{
|
||||
String username = getUsername();
|
||||
product.setCreateName(username);
|
||||
product.setModifier(username);
|
||||
return toAjax(lineProductService.insertLineProduct(product));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:lineProduct:edit')")
|
||||
@Log(title = "路线产品", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody LineProduct product)
|
||||
{
|
||||
product.setModifier(getUsername());
|
||||
return toAjax(lineProductService.updateLineProduct(product));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:lineProduct:edit')")
|
||||
@Log(title = "路线产品", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/changeStatus")
|
||||
public AjaxResult changeStatus(@RequestBody LineProduct product)
|
||||
{
|
||||
product.setModifier(getUsername());
|
||||
return toAjax(lineProductService.updateLineProductStatus(product));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('data:lineProduct:remove')")
|
||||
@Log(title = "路线产品", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable String[] ids)
|
||||
{
|
||||
return toAjax(lineProductService.deleteLineProductByIds(ids));
|
||||
}
|
||||
}
|
||||
+98
-1
@@ -1,17 +1,29 @@
|
||||
package com.bms.web.controller.masterdata;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.bms.common.annotation.Log;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.core.page.TableDataInfo;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.common.utils.poi.ExcelUtil;
|
||||
import com.bms.masterdata.domain.ServiceChannel;
|
||||
import com.bms.masterdata.service.IServiceChannelService;
|
||||
|
||||
/**
|
||||
* 服务渠道(老系统镜像 srv_service_channel)
|
||||
* 渠道管理(srv_service_channel)
|
||||
* 下拉选项仍走 /optionselect,登录即可,供供应商绑定渠道使用。
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
@@ -23,6 +35,32 @@ public class ServiceChannelController extends BaseController
|
||||
@Autowired
|
||||
private IServiceChannelService serviceChannelService;
|
||||
|
||||
/**
|
||||
* 分页查询渠道列表。
|
||||
* 渠道编码、渠道名称、服务商编码、服务商名称、目的港/目的地区域、发货人名称均可检索,名称类条件为模糊匹配。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(ServiceChannel query)
|
||||
{
|
||||
startPage();
|
||||
List<ServiceChannel> list = serviceChannelService.selectServiceChannelList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出渠道列表,筛选条件与列表一致。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:export')")
|
||||
@Log(title = "渠道管理", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, ServiceChannel query)
|
||||
{
|
||||
List<ServiceChannel> list = serviceChannelService.selectServiceChannelList(query);
|
||||
ExcelUtil<ServiceChannel> util = new ExcelUtil<ServiceChannel>(ServiceChannel.class);
|
||||
util.exportExcel(response, list, "渠道数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务渠道下拉选项(非分页)。
|
||||
* 供供应商绑定渠道等场景选择渠道;不做权限控制(仍需登录)。
|
||||
@@ -33,4 +71,63 @@ public class ServiceChannelController extends BaseController
|
||||
List<ServiceChannel> list = serviceChannelService.selectServiceChannelList(query);
|
||||
return success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询渠道详情
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:query')")
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getInfo(@PathVariable String id)
|
||||
{
|
||||
return success(serviceChannelService.selectServiceChannelById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增渠道
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:add')")
|
||||
@Log(title = "渠道管理", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody ServiceChannel channel)
|
||||
{
|
||||
String username = getUsername();
|
||||
channel.setCreateName(username);
|
||||
channel.setModifier(username);
|
||||
return toAjax(serviceChannelService.insertServiceChannel(channel));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改渠道
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:edit')")
|
||||
@Log(title = "渠道管理", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody ServiceChannel channel)
|
||||
{
|
||||
channel.setModifier(getUsername());
|
||||
return toAjax(serviceChannelService.updateServiceChannel(channel));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用或停用渠道
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:edit')")
|
||||
@Log(title = "渠道管理", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/changeStatus")
|
||||
public AjaxResult changeStatus(@RequestBody ServiceChannel channel)
|
||||
{
|
||||
channel.setModifier(getUsername());
|
||||
return toAjax(serviceChannelService.updateServiceChannelStatus(channel));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除渠道。已被产品渠道或供应商引用时不允许删除。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:remove')")
|
||||
@Log(title = "渠道管理", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable String[] ids)
|
||||
{
|
||||
return toAjax(serviceChannelService.deleteServiceChannelByIds(ids));
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -24,7 +24,7 @@ import com.bms.price.enums.BillTaskSuccessStatus;
|
||||
import com.bms.price.service.IPrcBillingTaskService;
|
||||
|
||||
/**
|
||||
* Monitor: failed / waiting prc_billing_task list and manual resync.
|
||||
* 监控:失败/等待中的 prc_billing_task 列表,以及手工重同步。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/monitor/billingTask")
|
||||
@@ -33,7 +33,7 @@ public class BillingTaskMonitorController extends BaseController
|
||||
@Autowired
|
||||
private IPrcBillingTaskService billingTaskService;
|
||||
|
||||
/** Default list = FAILED abnormal tasks. */
|
||||
/** 默认列表 = FAILED 异常任务。 */
|
||||
@PreAuthorize("@ss.hasPermi('monitor:billingTask:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(PrcBillingTask query)
|
||||
@@ -58,7 +58,7 @@ public class BillingTaskMonitorController extends BaseController
|
||||
return success(billingTaskService.getById(taskId));
|
||||
}
|
||||
|
||||
/** Manual resync one task (reset WAITING then process). */
|
||||
/** 手工重同步单个任务(先重置为 WAITING 再处理)。 */
|
||||
@PreAuthorize("@ss.hasPermi('monitor:billingTask:retry')")
|
||||
@Log(title = "billing task retry", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/retry/{taskId}")
|
||||
@@ -67,7 +67,7 @@ public class BillingTaskMonitorController extends BaseController
|
||||
return success(billingTaskService.retryTask(taskId));
|
||||
}
|
||||
|
||||
/** Manual resync selected tasks. */
|
||||
/** 手工重同步选中的任务。 */
|
||||
@PreAuthorize("@ss.hasPermi('monitor:billingTask:retry')")
|
||||
@Log(title = "billing task batch retry", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/retry")
|
||||
@@ -116,8 +116,8 @@ public class BillingTaskMonitorController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Async resync by customer + bill_time (batched ID pages to avoid OOM).
|
||||
* Returns asyncTaskId; poll {@code GET /retryTask/{asyncTaskId}}.
|
||||
* 按客户 + 计费时间异步重同步(ID 分页批处理,避免 OOM)。
|
||||
* 返回 asyncTaskId,通过 {@code GET /retryTask/{asyncTaskId}} 轮询进度。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:billingTask:retry')")
|
||||
@Log(title = "billing task filter retry", businessType = BusinessType.UPDATE)
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ import com.bms.price.search.BillSearchPage;
|
||||
import com.bms.price.search.IBillSearchStore;
|
||||
|
||||
/**
|
||||
* Generic Elasticsearch business-log viewer.
|
||||
* 通用 Elasticsearch 业务日志查看。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/monitor/eslog")
|
||||
@@ -39,7 +39,7 @@ public class EsLogController extends BaseController
|
||||
BillSearchPage page = billSearchStore.search(logType, bizId, status, pageNum, pageSize);
|
||||
TableDataInfo rsp = new TableDataInfo();
|
||||
rsp.setCode(HttpStatus.SUCCESS);
|
||||
rsp.setMsg("\u67e5\u8be2\u6210\u529f");
|
||||
rsp.setMsg("查询成功");
|
||||
rsp.setRows(page.getRows());
|
||||
rsp.setTotal(page.getTotal());
|
||||
return rsp;
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package com.bms.web.controller.monitor;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.bms.common.constant.HttpStatus;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.core.page.PageDomain;
|
||||
import com.bms.common.core.page.TableDataInfo;
|
||||
import com.bms.common.core.page.TableSupport;
|
||||
import com.bms.price.search.BillSearchPage;
|
||||
import com.bms.price.search.EsLogType;
|
||||
import com.bms.price.search.IBillSearchStore;
|
||||
import com.bms.price.search.PriceChangeGroupPage;
|
||||
|
||||
/**
|
||||
* 来自 ES 的价格变更前后日志(销售价 / 产品价 / 红线价)。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/monitor/priceLog")
|
||||
public class PriceLogController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IBillSearchStore billSearchStore;
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:priceLog:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(
|
||||
@RequestParam(value = "bizId", required = false) String bizId,
|
||||
@RequestParam(value = "status", required = false) String status,
|
||||
@RequestParam(value = "psvId", required = false) Long psvId,
|
||||
@RequestParam(value = "priceKind", required = false) String priceKind,
|
||||
@RequestParam(value = "productCode", required = false) String productCode,
|
||||
@RequestParam(value = "updateTime", required = false) String updateTime,
|
||||
@RequestParam(value = "updateTimePrefix", required = false) String updateTimePrefix,
|
||||
@RequestParam(value = "countryKeyword", required = false) String countryKeyword)
|
||||
{
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue();
|
||||
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue();
|
||||
BillSearchPage page = billSearchStore.search(EsLogType.PRICE_CHANGE, bizId, status, psvId, priceKind,
|
||||
productCode, updateTime, updateTimePrefix, countryKeyword, pageNum, pageSize);
|
||||
TableDataInfo rsp = new TableDataInfo();
|
||||
rsp.setCode(HttpStatus.SUCCESS);
|
||||
rsp.setMsg("查询成功");
|
||||
rsp.setRows(page.getRows());
|
||||
rsp.setTotal(page.getTotal());
|
||||
return rsp;
|
||||
}
|
||||
|
||||
/** 按 productCode + 写入时间分组,用于套餐版本下钻。 */
|
||||
@PreAuthorize("@ss.hasPermi('monitor:priceLog:list')")
|
||||
@GetMapping("/groups")
|
||||
public TableDataInfo groups(
|
||||
@RequestParam(value = "psvId", required = true) Long psvId,
|
||||
@RequestParam(value = "status", required = false) String status,
|
||||
@RequestParam(value = "priceKind", required = false) String priceKind)
|
||||
{
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue();
|
||||
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue();
|
||||
PriceChangeGroupPage page = billSearchStore.searchPriceChangeGroups(psvId, priceKind, status, pageNum,
|
||||
pageSize);
|
||||
TableDataInfo rsp = new TableDataInfo();
|
||||
rsp.setCode(HttpStatus.SUCCESS);
|
||||
rsp.setMsg("查询成功");
|
||||
rsp.setRows(page.getRows());
|
||||
rsp.setTotal(page.getTotal());
|
||||
return rsp;
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:priceLog:list')")
|
||||
@GetMapping("/{docId}")
|
||||
public AjaxResult getInfo(@PathVariable("docId") String docId)
|
||||
{
|
||||
return success(billSearchStore.getByDocId(docId));
|
||||
}
|
||||
}
|
||||
+208
-24
@@ -24,16 +24,29 @@ import com.bms.common.core.page.TableSupport;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.price.domain.PrcBillingTaskRetryTask;
|
||||
import com.bms.price.domain.vo.TabOrderCompareTask;
|
||||
import com.bms.price.domain.vo.TabOrderMonitorQuery;
|
||||
import com.bms.price.domain.vo.TabOrderMonitorVo;
|
||||
import com.bms.price.domain.vo.TabOrderProductCheckResult;
|
||||
import com.bms.price.oracle.CustomerKeywordResolver;
|
||||
import com.bms.price.oracle.TabOrderPageResult;
|
||||
import com.bms.price.oracle.TabOrderMonitorService;
|
||||
import com.bms.price.oracle.TabOrderProductCheckService;
|
||||
import com.bms.price.search.BillSearchPage;
|
||||
import com.bms.price.search.EsLogType;
|
||||
import com.bms.price.search.IBillSearchStore;
|
||||
import com.bms.price.search.TabOrderSyncEsLog;
|
||||
import com.bms.price.task.TabOrderCompareAsyncRunner;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import com.bms.common.config.BmsConfig;
|
||||
import com.bms.common.utils.DateUtils;
|
||||
import com.bms.common.utils.file.FileUtils;
|
||||
import com.bms.common.utils.uuid.IdUtils;
|
||||
import com.bms.price.domain.vo.TabOrderExportTask;
|
||||
import com.bms.price.task.TabOrderExportAsyncRunner;
|
||||
import com.bms.price.task.TabOrderSyncAsyncRunner;
|
||||
|
||||
/**
|
||||
@@ -52,6 +65,12 @@ public class TabOrderMonitorController extends BaseController
|
||||
@Autowired
|
||||
private TabOrderSyncAsyncRunner tabOrderSyncAsyncRunner;
|
||||
|
||||
@Autowired
|
||||
private TabOrderCompareAsyncRunner tabOrderCompareAsyncRunner;
|
||||
|
||||
@Autowired
|
||||
private TabOrderExportAsyncRunner tabOrderExportAsyncRunner;
|
||||
|
||||
@Autowired
|
||||
private IBillSearchStore billSearchStore;
|
||||
|
||||
@@ -61,10 +80,61 @@ public class TabOrderMonitorController extends BaseController
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(TabOrderMonitorQuery query)
|
||||
{
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue();
|
||||
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue();
|
||||
return listPage(query, pageNum, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 查询支持较长的多单号筛选,避免把单号列表拼进 GET URL。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list')")
|
||||
@PostMapping("/list")
|
||||
public TableDataInfo listPost(@RequestBody TabOrderMonitorQuery query,
|
||||
@RequestParam(value = "pageNum", required = false) Integer pageNum,
|
||||
@RequestParam(value = "pageSize", required = false) Integer pageSize)
|
||||
{
|
||||
return listPage(query, pageNum == null ? 1 : pageNum.intValue(),
|
||||
pageSize == null ? 10 : pageSize.intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 比对缺失转单号回填搜索专用:仅按转单号列查询,
|
||||
* 不走普通列表/三列单号收集路径。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list')")
|
||||
@PostMapping("/listByTransferNos")
|
||||
public TableDataInfo listByTransferNos(@RequestBody TabOrderMonitorQuery query,
|
||||
@RequestParam(value = "pageNum", required = false) Integer pageNum,
|
||||
@RequestParam(value = "pageSize", required = false) Integer pageSize)
|
||||
{
|
||||
TableDataInfo rsp = new TableDataInfo();
|
||||
rsp.setCode(HttpStatus.SUCCESS);
|
||||
rsp.setMsg("\u67e5\u8be2\u6210\u529f");
|
||||
rsp.setMsg("查询成功");
|
||||
if (query == null || !hasNonBlankItem(query.getOrderIdentifiers()))
|
||||
{
|
||||
rsp.setRows(Collections.emptyList());
|
||||
rsp.setTotal(0L);
|
||||
return rsp;
|
||||
}
|
||||
// 清空时间,避免共用解析器误复用按月时间窗口
|
||||
query.setBeginTime(null);
|
||||
query.setEndTime(null);
|
||||
customerKeywordResolver.resolve(query);
|
||||
TabOrderPageResult page = tabOrderMonitorService.searchPageByTransferNos(
|
||||
query, pageNum == null ? 1 : pageNum.intValue(), pageSize == null ? 10 : pageSize.intValue());
|
||||
rsp.setRows(page.getRows());
|
||||
rsp.setTotal(page.getTotal());
|
||||
return rsp;
|
||||
}
|
||||
|
||||
private TableDataInfo listPage(TabOrderMonitorQuery query, int pageNum, int pageSize)
|
||||
{
|
||||
TableDataInfo rsp = new TableDataInfo();
|
||||
rsp.setCode(HttpStatus.SUCCESS);
|
||||
rsp.setMsg("查询成功");
|
||||
if (!hasListFilters(query))
|
||||
{
|
||||
rsp.setRows(Collections.emptyList());
|
||||
@@ -73,18 +143,53 @@ public class TabOrderMonitorController extends BaseController
|
||||
}
|
||||
// 在切换 Oracle 数据源之前,把客户名称解析为客户代码
|
||||
customerKeywordResolver.resolve(query);
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue();
|
||||
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue();
|
||||
long total = tabOrderMonitorService.count(query);
|
||||
List<TabOrderMonitorVo> rows = total <= 0L
|
||||
? Collections.emptyList()
|
||||
: tabOrderMonitorService.selectPage(query, pageNum, pageSize);
|
||||
rsp.setRows(rows);
|
||||
rsp.setTotal(total);
|
||||
// 大批量单号场景一次收集即可同时得到总数和当前页,避免重复扫描 Oracle
|
||||
TabOrderPageResult page = tabOrderMonitorService.searchPage(query, pageNum, pageSize);
|
||||
rsp.setRows(page.getRows());
|
||||
rsp.setTotal(page.getTotal());
|
||||
return rsp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单比对(异步):按客户+提货时间分页比对转单号(上游有/本地无)。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list')")
|
||||
@Log(title = "order source compare", businessType = BusinessType.OTHER)
|
||||
@PostMapping("/compareByFilter")
|
||||
public AjaxResult compareByFilter(@RequestBody TabOrderMonitorQuery query)
|
||||
{
|
||||
if (!hasRequiredFilters(query))
|
||||
{
|
||||
return error("订单比对需先选择客户和提货时间");
|
||||
}
|
||||
// 比对不受列表单号/回填影响
|
||||
query.setOrderIdentifiers(null);
|
||||
query.setOrderId(null);
|
||||
query.setOrgOrderId(null);
|
||||
query.setTransferOrderId(null);
|
||||
customerKeywordResolver.resolve(query);
|
||||
String asyncTaskId = UUID.randomUUID().toString().replace("-", "");
|
||||
tabOrderCompareAsyncRunner.put(asyncTaskId, TabOrderCompareTask.STATUS_PENDING, 0, "queued",
|
||||
0, 0, 0, null, null);
|
||||
tabOrderCompareAsyncRunner.run(asyncTaskId, query);
|
||||
Map<String, Object> data = new HashMap<String, Object>();
|
||||
data.put("asyncTaskId", asyncTaskId);
|
||||
TabOrderSyncEsLog.save(billSearchStore, asyncTaskId, "COMPARE", query, data, "SUCCESS", null);
|
||||
return success(data);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list')")
|
||||
@GetMapping("/compareTask/{asyncTaskId}")
|
||||
public AjaxResult compareTaskProgress(@PathVariable("asyncTaskId") String asyncTaskId)
|
||||
{
|
||||
TabOrderCompareTask task = tabOrderCompareAsyncRunner.get(asyncTaskId);
|
||||
if (task == null)
|
||||
{
|
||||
return error("compare task not found or expired");
|
||||
}
|
||||
return success(task);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list')")
|
||||
@PostMapping("/checkProducts")
|
||||
public AjaxResult checkProducts(@RequestBody TabOrderMonitorQuery query)
|
||||
@@ -114,17 +219,22 @@ public class TabOrderMonitorController extends BaseController
|
||||
return success(tabOrderMonitorService.selectOrderIdsByProduct(query));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync')")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync') or @ss.hasPermi('price:orderBill:rebill')")
|
||||
@Log(title = "order source sync filter", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/syncByFilter")
|
||||
public AjaxResult syncByFilter(@RequestBody TabOrderMonitorQuery query)
|
||||
{
|
||||
if (!hasRequiredFilters(query))
|
||||
// 仅覆盖上游字段(不触发计费)。筛选条件与列表查询一致。
|
||||
if (!hasListFilters(query))
|
||||
{
|
||||
return error("customerCodes and pickup time required");
|
||||
return error("orderIdentifiers or (customer and pickup time) required");
|
||||
}
|
||||
customerKeywordResolver.resolve(query);
|
||||
tabOrderMonitorService.count(query);
|
||||
// 含大批量单号时,异步任务启动时会自行分批收集,这里不预先 count,避免重复全量扫描
|
||||
if (!hasNonBlankItem(query.getOrderIdentifiers()))
|
||||
{
|
||||
tabOrderMonitorService.count(query);
|
||||
}
|
||||
String asyncTaskId = UUID.randomUUID().toString().replace("-", "");
|
||||
tabOrderSyncAsyncRunner.put(asyncTaskId, PrcBillingTaskRetryTask.STATUS_PENDING, 0, "queued",
|
||||
0, 0, 0, 0, null);
|
||||
@@ -135,7 +245,7 @@ public class TabOrderMonitorController extends BaseController
|
||||
return success(data);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync') or @ss.hasPermi('monitor:orderSource:list')")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync') or @ss.hasPermi('monitor:orderSource:list') or @ss.hasPermi('price:orderBill:rebill')")
|
||||
@GetMapping("/syncTask/{asyncTaskId}")
|
||||
public AjaxResult syncTaskProgress(@PathVariable("asyncTaskId") String asyncTaskId)
|
||||
{
|
||||
@@ -147,7 +257,8 @@ public class TabOrderMonitorController extends BaseController
|
||||
return success(task);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync')")
|
||||
/** 选中行:用 Oracle 数据覆盖本地账单字段;不做报价/重算。 */
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync') or @ss.hasPermi('price:orderBill:rebill')")
|
||||
@Log(title = "order source sync selected", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/sync")
|
||||
public AjaxResult syncSelected(@RequestBody List<TabOrderMonitorVo> rows)
|
||||
@@ -171,7 +282,7 @@ public class TabOrderMonitorController extends BaseController
|
||||
return success(data);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list')")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list') or @ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/syncLog/list")
|
||||
public TableDataInfo syncLogList(
|
||||
@RequestParam(value = "bizId", required = false) String bizId,
|
||||
@@ -183,13 +294,13 @@ public class TabOrderMonitorController extends BaseController
|
||||
BillSearchPage page = billSearchStore.search(EsLogType.ORDER_SOURCE_SYNC, bizId, status, pageNum, pageSize);
|
||||
TableDataInfo rsp = new TableDataInfo();
|
||||
rsp.setCode(HttpStatus.SUCCESS);
|
||||
rsp.setMsg("\u67e5\u8be2\u6210\u529f");
|
||||
rsp.setMsg("查询成功");
|
||||
rsp.setRows(page.getRows());
|
||||
rsp.setTotal(page.getTotal());
|
||||
return rsp;
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list')")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list') or @ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/syncLog/{docId}")
|
||||
public AjaxResult syncLogInfo(@PathVariable("docId") String docId)
|
||||
{
|
||||
@@ -207,13 +318,14 @@ public class TabOrderMonitorController extends BaseController
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(row.getOrderId()))
|
||||
{
|
||||
return prefix + "_" + row.getOrderId();
|
||||
}
|
||||
// 优先使用转单号作为 bizId(与 BILL / 上游同步 ES 一致)。
|
||||
if (StringUtils.isNotEmpty(row.getTransferOrderId()))
|
||||
{
|
||||
return prefix + "_" + row.getTransferOrderId();
|
||||
return prefix + "_" + row.getTransferOrderId().trim();
|
||||
}
|
||||
if (StringUtils.isNotEmpty(row.getOrderId()))
|
||||
{
|
||||
return prefix + "_" + row.getOrderId().trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,6 +335,78 @@ public class TabOrderMonitorController extends BaseController
|
||||
/**
|
||||
* 列表查询:有任一单号即可查询;无单号时必须客户(名称或代码)+提货时间。
|
||||
*/
|
||||
|
||||
/**
|
||||
* 异步导出订单数源:按当前筛选(须有单号,或客户+提货时间)。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:export')")
|
||||
@Log(title = "订单数源", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public AjaxResult export(@RequestBody(required = false) TabOrderMonitorQuery query)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
query = new TabOrderMonitorQuery();
|
||||
}
|
||||
if (!hasListFilters(query))
|
||||
{
|
||||
return error("请先设置列表查询条件后再导出");
|
||||
}
|
||||
customerKeywordResolver.resolve(query);
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
String fileName = "订单数源_" + DateUtils.dateTimeNow() + ".xlsx";
|
||||
tabOrderExportAsyncRunner.put(taskId, TabOrderExportTask.STATUS_PENDING, 0, "queued", null,
|
||||
fileName, null, 0);
|
||||
tabOrderExportAsyncRunner.run(taskId, query, fileName);
|
||||
Map<String, Object> data = new HashMap<String, Object>();
|
||||
data.put("taskId", taskId);
|
||||
return success(data);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:export')")
|
||||
@GetMapping("/export/task/{taskId}")
|
||||
public AjaxResult exportTask(@PathVariable("taskId") String taskId)
|
||||
{
|
||||
TabOrderExportTask task = tabOrderExportAsyncRunner.get(taskId);
|
||||
if (task == null)
|
||||
{
|
||||
return error("export task not found or expired");
|
||||
}
|
||||
return success(task);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:export')")
|
||||
@GetMapping("/export/download/{taskId}")
|
||||
public void exportDownload(@PathVariable("taskId") String taskId, HttpServletResponse response)
|
||||
throws Exception
|
||||
{
|
||||
TabOrderExportTask task = tabOrderExportAsyncRunner.get(taskId);
|
||||
if (task == null)
|
||||
{
|
||||
throw new com.bms.common.exception.ServiceException("export task not found or expired");
|
||||
}
|
||||
if (!TabOrderExportTask.STATUS_SUCCESS.equals(task.getStatus())
|
||||
|| StringUtils.isEmpty(task.getStorageKey()))
|
||||
{
|
||||
throw new com.bms.common.exception.ServiceException("export file not ready");
|
||||
}
|
||||
if (!FileUtils.checkAllowDownload(task.getStorageKey()))
|
||||
{
|
||||
throw new com.bms.common.exception.ServiceException("export file missing");
|
||||
}
|
||||
Path downloadRoot = Paths.get(BmsConfig.getDownloadPath()).toAbsolutePath().normalize();
|
||||
Path filePath = downloadRoot.resolve(task.getStorageKey()).normalize();
|
||||
if (!filePath.startsWith(downloadRoot) || !Files.isRegularFile(filePath))
|
||||
{
|
||||
throw new com.bms.common.exception.ServiceException("export file missing");
|
||||
}
|
||||
String fileName = StringUtils.isNotEmpty(task.getFileName()) ? task.getFileName() : (taskId + ".xlsx");
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
FileUtils.setAttachmentResponseHeader(response, fileName);
|
||||
FileUtils.writeBytes(filePath.toString(), response.getOutputStream());
|
||||
}
|
||||
|
||||
|
||||
private static boolean hasListFilters(TabOrderMonitorQuery query)
|
||||
{
|
||||
return hasRequiredFilters(query) || hasNonBlankItem(query.getOrderIdentifiers());
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package com.bms.web.controller.monitor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.bms.common.annotation.Log;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.core.page.TableDataInfo;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.price.migration.domain.MigBiJobLog;
|
||||
import com.bms.price.migration.service.IUuidToNewMonitorService;
|
||||
|
||||
/**
|
||||
* 监控:UUID -> bigint 阴影同步进度与开关
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/monitor/uuidToNew")
|
||||
public class UuidToNewMonitorController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IUuidToNewMonitorService monitorService;
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:uuidToNew:list')")
|
||||
@GetMapping("/progress")
|
||||
public AjaxResult progress()
|
||||
{
|
||||
return success(monitorService.getProgress());
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:uuidToNew:list')")
|
||||
@GetMapping("/jobLog/list")
|
||||
public TableDataInfo jobLogList(MigBiJobLog query)
|
||||
{
|
||||
startPage();
|
||||
List<MigBiJobLog> list = monitorService.selectJobLogList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开/关闭连续同步。
|
||||
* body: { "enabled": true, "threadCount": 2, "batchSize": 200 }
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:uuidToNew:switch')")
|
||||
@Log(title = "UUID阴影同步开关", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/enabled")
|
||||
public AjaxResult setEnabled(@RequestBody Map<String, Object> body)
|
||||
{
|
||||
boolean enabled = false;
|
||||
Integer threadCount = null;
|
||||
Integer batchSize = null;
|
||||
if (body != null)
|
||||
{
|
||||
if (body.get("enabled") != null)
|
||||
{
|
||||
enabled = Boolean.parseBoolean(String.valueOf(body.get("enabled")));
|
||||
}
|
||||
threadCount = toInt(body.get("threadCount"));
|
||||
batchSize = toInt(body.get("batchSize"));
|
||||
}
|
||||
monitorService.setEnabled(enabled, threadCount, batchSize);
|
||||
return success(monitorService.getProgress());
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅开关关闭时修改线程/批次。
|
||||
* body: { "threadCount": 2, "batchSize": 200 }
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:uuidToNew:switch')")
|
||||
@Log(title = "UUID阴影同步配置", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/config")
|
||||
public AjaxResult updateConfig(@RequestBody Map<String, Object> body)
|
||||
{
|
||||
Integer threadCount = null;
|
||||
Integer batchSize = null;
|
||||
if (body != null)
|
||||
{
|
||||
threadCount = toInt(body.get("threadCount"));
|
||||
batchSize = toInt(body.get("batchSize"));
|
||||
}
|
||||
monitorService.updateRuntimeConfig(threadCount, batchSize);
|
||||
return success(monitorService.getProgress());
|
||||
}
|
||||
|
||||
private Integer toInt(Object v)
|
||||
{
|
||||
if (v == null || "".equals(String.valueOf(v).trim()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
return Integer.valueOf(String.valueOf(v).trim());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package com.bms.web.controller.price;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.bms.common.annotation.Log;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.common.utils.poi.ExcelUtil;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFeeImportRow;
|
||||
import com.bms.price.airbill.domain.SrvBillAirRoleFeeCommand;
|
||||
import com.bms.price.airbill.domain.SrvBillAirRoleFeeQuery;
|
||||
import com.bms.price.domain.vo.SrvBillAirQuery;
|
||||
import com.bms.price.service.ISrvBillAirFeeService;
|
||||
import com.bms.price.service.ISrvBillAirService;
|
||||
|
||||
/**
|
||||
* 空运、报关、清关三个角色往提单上添加费用。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/price/airFee")
|
||||
public class AirFeeRoleController extends BaseController
|
||||
{
|
||||
private static final int MAX_BILLS = 50;
|
||||
|
||||
@Autowired
|
||||
private ISrvBillAirFeeService srvBillAirFeeService;
|
||||
|
||||
@Autowired
|
||||
private ISrvBillAirService srvBillAirService;
|
||||
|
||||
/** 按提单号查出可绑定的提单,支持一次多选。 */
|
||||
@PreAuthorize("@ss.hasAnyPermi('price:airFee:air,price:airFee:declare,price:airFee:clear')")
|
||||
@GetMapping("/bills")
|
||||
public AjaxResult bills(@RequestParam(value = "billCodes", required = false) String billCodes)
|
||||
{
|
||||
List<String> codes = splitCodes(billCodes);
|
||||
if (codes.isEmpty())
|
||||
{
|
||||
return success(List.of());
|
||||
}
|
||||
if (codes.size() > MAX_BILLS)
|
||||
{
|
||||
return error("单次最多选择" + MAX_BILLS + "个提单");
|
||||
}
|
||||
SrvBillAirQuery query = new SrvBillAirQuery();
|
||||
query.setBillCodes(codes);
|
||||
return success(srvBillAirService.selectSrvBillAirList(query));
|
||||
}
|
||||
|
||||
/** 查询已绑定到这些提单、且属于当前入口的费用。 */
|
||||
@PreAuthorize("@ss.hasAnyPermi('price:airFee:air,price:airFee:declare,price:airFee:clear')")
|
||||
@GetMapping("/list")
|
||||
public AjaxResult list(@RequestParam("scene") String scene, @RequestParam(value = "billCodes", required = false) String billCodes)
|
||||
{
|
||||
SrvBillAirRoleFeeQuery query = new SrvBillAirRoleFeeQuery();
|
||||
query.setScene(scene);
|
||||
query.setBillCodes(splitCodes(billCodes));
|
||||
return success(srvBillAirFeeService.listRoleFees(query));
|
||||
}
|
||||
|
||||
/** 保存当前入口的费用。空运写入 b 段,报关和清关写入 c 段。 */
|
||||
@PreAuthorize("@ss.hasAnyPermi('price:airFee:air,price:airFee:declare,price:airFee:clear')")
|
||||
@Log(title = "提单角色费用", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/save")
|
||||
public AjaxResult save(@RequestBody SrvBillAirRoleFeeCommand command)
|
||||
{
|
||||
return success(srvBillAirFeeService.saveRole(command));
|
||||
}
|
||||
|
||||
/** 下载导入模板,模板中带一行示例。 */
|
||||
@PreAuthorize("@ss.hasAnyPermi('price:airFee:air,price:airFee:declare,price:airFee:clear')")
|
||||
@PostMapping("/importTemplate")
|
||||
public void importTemplate(HttpServletResponse response)
|
||||
{
|
||||
SrvBillAirFeeImportRow example = new SrvBillAirFeeImportRow();
|
||||
example.setFeeName("燃油附加费");
|
||||
example.setPrice(new BigDecimal("1000"));
|
||||
example.setCurrency("EUR");
|
||||
example.setRemark("导入时需清空这行示例数据");
|
||||
List<SrvBillAirFeeImportRow> rows = new ArrayList<>();
|
||||
rows.add(example);
|
||||
new ExcelUtil<>(SrvBillAirFeeImportRow.class).exportExcel(response, rows, "提单费用导入模板");
|
||||
}
|
||||
|
||||
/** 解析导入文件,不直接落库。示例行会被跳过。 */
|
||||
@PreAuthorize("@ss.hasAnyPermi('price:airFee:air,price:airFee:declare,price:airFee:clear')")
|
||||
@Log(title = "提单角色费用导入", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/import")
|
||||
public AjaxResult importFee(MultipartFile file, @RequestParam("scene") String scene) throws Exception
|
||||
{
|
||||
if (file == null || file.isEmpty())
|
||||
{
|
||||
return error("请选择导入文件");
|
||||
}
|
||||
return success(srvBillAirFeeService.parseImport(scene, file.getInputStream()));
|
||||
}
|
||||
|
||||
private static List<String> splitCodes(String billCodes)
|
||||
{
|
||||
LinkedHashSet<String> codes = new LinkedHashSet<>();
|
||||
if (billCodes != null)
|
||||
{
|
||||
for (String part : billCodes.split("[,,\\s]+"))
|
||||
{
|
||||
if (part != null && !part.isBlank())
|
||||
{
|
||||
codes.add(part.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(codes);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user