Compare commits
116 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d5ffc4e06 | |||
| 1ba431540d | |||
| f03aa7be6c | |||
| 0d0613e810 | |||
| 47b95b6bfd | |||
| e7fd7f9059 | |||
| 1ffaf424b1 | |||
| 8ca5af1ff0 | |||
| 621128530a | |||
| 73ce7f4eae | |||
| fb9a4e1b29 | |||
| 89aadf97db | |||
| 90147ed4e3 | |||
| cf336f7a67 | |||
| d2c75e0c51 | |||
| 17ee9731e8 | |||
| 146d3469b6 | |||
| b49600ab9f | |||
| 27715b3e13 | |||
| b5299e4490 | |||
| 82164cb00c | |||
| 34fa8e23c1 | |||
| efd92b5663 | |||
| 7869205afc | |||
| f82d9c667f | |||
| 7e1041391a | |||
| 99cdfd86d3 | |||
| 41a80f9794 | |||
| 8cb2d03062 | |||
| 4ceb0bc830 | |||
| f7384c88f6 | |||
| beba9e9caa | |||
| b7d0674fda | |||
| 0545de88b4 | |||
| fb41b2a86c | |||
| 2430e344a0 | |||
| 5db27e00ba | |||
| a06962d1dd | |||
| 5f679b5a6e | |||
| 02763f092e | |||
| b6718f9361 | |||
| a58de9bb7e | |||
| a211f06663 | |||
| 41cefd5fd5 | |||
| fe30b8576b | |||
| 65fcb6895a | |||
| d4f5bb8a4c | |||
| fca999c384 | |||
| e71103af58 | |||
| 2e5ab2bfc1 | |||
| 79cc524c03 | |||
| 3e5ef25153 | |||
| 9004a1b921 | |||
| cf6b90008d | |||
| fbc138f041 | |||
| 3c49175cea | |||
| f26e03efb1 | |||
| 1a0483c7fe | |||
| da9efc422e | |||
| e6a9fcf0a2 | |||
| de5784cbac | |||
| 16afa0d590 | |||
| 2632e5aa78 | |||
| ace1eb06c0 | |||
| f31bb4c54d | |||
| f2d27f3502 | |||
| efc7ecbd01 | |||
| b8bbf36767 | |||
| 92cb19fbea | |||
| 4cb82ec107 | |||
| e0dc755ee3 | |||
| 6a02de8069 | |||
| 974f12429a | |||
| e06f07b3dd | |||
| 956fb08bea | |||
| 8d34f84cbb | |||
| b9cdb6c3f9 | |||
| f57af555ea | |||
| 220b01127b | |||
| aa61dc2d1c | |||
| 86ed86fc68 | |||
| fba8b786c0 | |||
| a4bda4d636 | |||
| 00e36cdf3f | |||
| 8eb5dca4f0 | |||
| b671ebefae | |||
| fb5d6d46a8 | |||
| 039c0d4858 | |||
| 43299e5329 | |||
| 3fe34c471c | |||
| 134855fc31 | |||
| 01cee41301 | |||
| 1f0d757e29 | |||
| 762cad2c0d | |||
| 7ef3f40a4f | |||
| 58e41b669d | |||
| 9a48abbacd | |||
| f95edbdf0a | |||
| b30dfdf675 | |||
| 9a393fe7dc | |||
| a2468099c2 | |||
| cbed4dc2a3 | |||
| d66b96b2ff | |||
| cdfc90556d | |||
| dfa90d6e33 | |||
| 4a34075022 | |||
| 6a03300f2f | |||
| 7496f8cbc6 | |||
| 2f2305825a | |||
| b79cd6f995 | |||
| 70a3c34c20 | |||
| 2b5633190e | |||
| 77dfb4291b | |||
| d71b7f04fe | |||
| f8ae347846 | |||
| 94991a1ed3 |
+10
-10
@@ -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(
|
||||
|
||||
+2
-2
@@ -188,7 +188,7 @@ public class CommonController
|
||||
}
|
||||
|
||||
/**
|
||||
* Common resource download: local profile path or oss://bucket/key (AR bill style).
|
||||
* 通用资源下载:本地 profile 路径或 oss://bucket/key(AR 账单风格)。
|
||||
*/
|
||||
@GetMapping("/download/resource")
|
||||
public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
|
||||
@@ -207,7 +207,7 @@ public class CommonController
|
||||
}
|
||||
if (ossFile)
|
||||
{
|
||||
// Extension check still applies to the object key suffix.
|
||||
// 扩展名校验仍适用于对象 key 后缀。
|
||||
if (!FileUtils.checkAllowDownload(resource))
|
||||
{
|
||||
throw new Exception(StringUtils.format("resource ({}) not allowed", resource));
|
||||
|
||||
+7
-7
@@ -13,13 +13,13 @@ 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.ArBill;
|
||||
import com.bms.ar.domain.ArBalanceWriteoff;
|
||||
import com.bms.ar.domain.vo.ArBillListExportRow;
|
||||
import com.bms.ar.domain.vo.ArBillQuery;
|
||||
import com.bms.ar.domain.vo.ArBillSummary;
|
||||
import com.bms.ar.service.IArBalanceWriteoffService;
|
||||
import com.bms.ar.service.IArBillService;
|
||||
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;
|
||||
|
||||
+13
-3
@@ -12,6 +12,7 @@ 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;
|
||||
@@ -19,7 +20,7 @@ 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.exception.ServiceException;
|
||||
import com.bms.common.enums.PaymentMesEnum;
|
||||
import com.bms.price.domain.PayPostcodeGroup;
|
||||
import com.bms.price.domain.PayPostcodeLine;
|
||||
import com.bms.price.service.IPayPostcodeService;
|
||||
@@ -43,6 +44,15 @@ public class PayPostcodeController extends BaseController
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/** 按国家去重区块,供附加费编辑分区下拉 */
|
||||
@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)
|
||||
@@ -74,12 +84,12 @@ public class PayPostcodeController extends BaseController
|
||||
{
|
||||
if (file == null || file.isEmpty())
|
||||
{
|
||||
throw new ServiceException("请选择 Excel 文件");
|
||||
throw PaymentMesEnum.BMS_PAY_PICK_XLS.exception();
|
||||
}
|
||||
String name = file.getOriginalFilename() == null ? "" : file.getOriginalFilename().toLowerCase(Locale.ROOT);
|
||||
if (!name.endsWith(".xlsx"))
|
||||
{
|
||||
throw new ServiceException("只支持 xlsx 文件");
|
||||
throw PaymentMesEnum.BMS_PAY_XLSX_ONLY.exception();
|
||||
}
|
||||
return success(payPostcodeService.importExcel(file.getBytes(), file.getOriginalFilename(), getUsername()));
|
||||
}
|
||||
|
||||
+5
-5
@@ -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)
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ public class PriceLogController extends BaseController
|
||||
return rsp;
|
||||
}
|
||||
|
||||
/** Group by productCode + write time for package-version drill-down. */
|
||||
/** 按 productCode + 写入时间分组,用于套餐版本下钻。 */
|
||||
@PreAuthorize("@ss.hasPermi('monitor:priceLog:list')")
|
||||
@GetMapping("/groups")
|
||||
public TableDataInfo groups(
|
||||
|
||||
+4
-4
@@ -119,7 +119,7 @@ public class TabOrderMonitorController extends BaseController
|
||||
rsp.setTotal(0L);
|
||||
return rsp;
|
||||
}
|
||||
// clear time so shared resolvers cannot accidentally reuse month window
|
||||
// 清空时间,避免共用解析器误复用按月时间窗口
|
||||
query.setBeginTime(null);
|
||||
query.setEndTime(null);
|
||||
customerKeywordResolver.resolve(query);
|
||||
@@ -224,7 +224,7 @@ public class TabOrderMonitorController extends BaseController
|
||||
@PostMapping("/syncByFilter")
|
||||
public AjaxResult syncByFilter(@RequestBody TabOrderMonitorQuery query)
|
||||
{
|
||||
// Upstream field overwrite only (no billing). Same filters as list query.
|
||||
// 仅覆盖上游字段(不触发计费)。筛选条件与列表查询一致。
|
||||
if (!hasListFilters(query))
|
||||
{
|
||||
return error("orderIdentifiers or (customer and pickup time) required");
|
||||
@@ -257,7 +257,7 @@ public class TabOrderMonitorController extends BaseController
|
||||
return success(task);
|
||||
}
|
||||
|
||||
/** Selected rows: overwrite local bill fields from Oracle; does not quote/rebill. */
|
||||
/** 选中行:用 Oracle 数据覆盖本地账单字段;不做报价/重算。 */
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync') or @ss.hasPermi('price:orderBill:rebill')")
|
||||
@Log(title = "order source sync selected", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/sync")
|
||||
@@ -318,7 +318,7 @@ public class TabOrderMonitorController extends BaseController
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Prefer transferNo as bizId (same as BILL / upstream sync ES).
|
||||
// 优先使用转单号作为 bizId(与 BILL / 上游同步 ES 一致)。
|
||||
if (StringUtils.isNotEmpty(row.getTransferOrderId()))
|
||||
{
|
||||
return prefix + "_" + row.getTransferOrderId().trim();
|
||||
|
||||
+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);
|
||||
}
|
||||
}
|
||||
+75
-56
@@ -96,7 +96,7 @@ public class BillingController extends BaseController
|
||||
private IBillSearchStore billSearchStore;
|
||||
|
||||
/**
|
||||
* Historical quote list.
|
||||
* 历史报价列表。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:quote')")
|
||||
@GetMapping("/quote/list")
|
||||
@@ -108,7 +108,7 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Products of the customer's bound effective package version (trial product dropdown).
|
||||
* 客户已绑定生效套餐版本下的产品(试算产品下拉)。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:quote')")
|
||||
@GetMapping("/quote/products")
|
||||
@@ -124,7 +124,7 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Historical quote detail with fee lines.
|
||||
* 历史报价详情,含费用明细行。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:quote')")
|
||||
@GetMapping("/quote/{quoteId}")
|
||||
@@ -134,7 +134,7 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Same matching logic as TMS open API, for BMS console trial.
|
||||
* 与 TMS 开放接口使用相同匹配逻辑,供 BMS 后台试算使用。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:quote')")
|
||||
@PostMapping("/quote")
|
||||
@@ -147,13 +147,29 @@ public class BillingController extends BaseController
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:bill')")
|
||||
@GetMapping("/bill/list")
|
||||
public TableDataInfo billList(PrcOrderBill query)
|
||||
{
|
||||
return doBillList(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 计费列表:大批单号等筛选放请求体,避免 GET URL 过长触发 HTTP 431。
|
||||
* pageNum/pageSize 仍走 URL 查询参数,供 PageHelper 读取。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:bill')")
|
||||
@PostMapping("/bill/list")
|
||||
public TableDataInfo billListPost(@RequestBody(required = false) PrcOrderBill query)
|
||||
{
|
||||
return doBillList(query);
|
||||
}
|
||||
|
||||
private TableDataInfo doBillList(PrcOrderBill query)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
query = new PrcOrderBill();
|
||||
}
|
||||
// Billing list only shows success and the three soft misses.
|
||||
query.setBillingFailStatuses(limitBillingListFailStatus(query.getBillingFailStatuses()));
|
||||
// 计费列表只展示计费成功的订单。
|
||||
query.setBillingFailStatuses(limitBillingListFailStatus());
|
||||
query.setBillingFailStatus(null);
|
||||
startPage();
|
||||
List<PrcOrderBill> list = prcOrderBillService.selectBillList(query);
|
||||
@@ -161,8 +177,8 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Async export billing list by current filters (status limited to 0-3).
|
||||
* Requires at least one meaningful filter. Poll task then download.
|
||||
* 按当前筛选条件异步导出计费列表(状态限定为成功 success=0)。
|
||||
* 需至少一个有效筛选条件。轮询任务完成后再下载。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:export')")
|
||||
@Log(title = "计费列表", businessType = BusinessType.EXPORT)
|
||||
@@ -173,12 +189,12 @@ public class BillingController extends BaseController
|
||||
{
|
||||
query = new PrcOrderBill();
|
||||
}
|
||||
// Filter gate uses the request as-is; defaults applied only for query scope.
|
||||
// 筛选条件校验使用原始请求;默认值仅用于查询范围。
|
||||
if (!OrderBillExportSupport.hasExportFilter(query))
|
||||
{
|
||||
return error(MessageUtils.message("price.bill.export.filterRequired"));
|
||||
}
|
||||
query.setBillingFailStatuses(limitBillingListFailStatus(query.getBillingFailStatuses()));
|
||||
query.setBillingFailStatuses(limitBillingListFailStatus());
|
||||
query.setBillingFailStatus(null);
|
||||
return successTask(prcOrderBillService.submitOrderBillExport(
|
||||
PrcOrderBillExportTask.TYPE_BILLING, query));
|
||||
@@ -204,32 +220,13 @@ public class BillingController extends BaseController
|
||||
prcOrderBillService.downloadOrderBillExport(taskId, response);
|
||||
}
|
||||
|
||||
private static List<Integer> limitBillingListFailStatus(List<Integer> requested)
|
||||
private static List<Integer> limitBillingListFailStatus()
|
||||
{
|
||||
// Soft-fail list: success + sales 1/2/3 + product-catalog 23/24/25
|
||||
List<Integer> allowed = Arrays.asList(
|
||||
Integer.valueOf(0), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3),
|
||||
Integer.valueOf(23), Integer.valueOf(24), Integer.valueOf(25));
|
||||
if (requested == null || requested.isEmpty())
|
||||
{
|
||||
return new ArrayList<Integer>(allowed);
|
||||
}
|
||||
List<Integer> kept = new ArrayList<Integer>();
|
||||
for (Integer status : requested)
|
||||
{
|
||||
if (status != null && allowed.contains(status) && !kept.contains(status))
|
||||
{
|
||||
kept.add(status);
|
||||
}
|
||||
}
|
||||
if (kept.isEmpty())
|
||||
{
|
||||
kept.add(Integer.valueOf(-1));
|
||||
}
|
||||
return kept;
|
||||
// 计费列表仅包含成功数据(billing_fail_status = 0)。
|
||||
return new ArrayList<Integer>(Arrays.asList(Integer.valueOf(0)));
|
||||
}
|
||||
|
||||
/** All prc_order_bill rows. Billing list stays limited to status 0-3. */
|
||||
/** 全部 prc_order_bill 记录。计费列表仍限定为状态 0。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/orderBill/list")
|
||||
public TableDataInfo orderBillList(PrcOrderBill query)
|
||||
@@ -240,8 +237,30 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Async export order list by current filters.
|
||||
* Requires at least one meaningful filter (default orderStatuses alone is not enough).
|
||||
* POST 列表:筛选(尤其是大批量单号)放请求体,避免 GET URL 过长触发 HTTP 431。
|
||||
* pageNum/pageSize 仍走 URL 查询参数,供 TableSupport/PageHelper 读取。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:orderBill:list')")
|
||||
@PostMapping("/orderBill/list")
|
||||
public TableDataInfo orderBillListPost(@RequestBody(required = false) PrcOrderBill query)
|
||||
{
|
||||
startPage();
|
||||
List<PrcOrderBill> list = prcOrderBillService.selectBillList(query == null ? new PrcOrderBill() : query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
|
||||
/** 订单列表弹窗:按 billId 查询计费失败原因(受数据权限控制)。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/orderBill/{billId}/failReason")
|
||||
public AjaxResult orderBillFailReason(@PathVariable("billId") String billId)
|
||||
{
|
||||
return success(prcOrderBillService.getBillingFailReason(billId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按当前筛选条件异步导出订单列表。
|
||||
* 需至少一个有效筛选条件(仅默认 orderStatuses 不够)。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:orderBill:export')")
|
||||
@Log(title = "订单列表", businessType = BusinessType.EXPORT)
|
||||
@@ -273,8 +292,8 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare local order-bill products vs customer bound package SALES products
|
||||
* (includes product_code_mapping).
|
||||
* 对比本地订单账单产品与客户绑定套餐的 SALES 产品
|
||||
* (包含 product_code_mapping)。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:orderBill:list')")
|
||||
@PostMapping("/orderBill/checkProducts")
|
||||
@@ -284,7 +303,7 @@ public class BillingController extends BaseController
|
||||
return success(data);
|
||||
}
|
||||
|
||||
/** Drill-down order bills for one product under the same check filters. */
|
||||
/** 在同一校验筛选条件下,下钻查看单个产品的订单账单。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:orderBill:list')")
|
||||
@PostMapping("/orderBill/checkProducts/orders")
|
||||
public AjaxResult listOrderBillsByProduct(@RequestBody PrcOrderBill query)
|
||||
@@ -293,8 +312,8 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Order-list rebill from local prc_order_bill only (no Oracle TAB_ORDER).
|
||||
* Poll {@code GET /orderBill/rebill/task/{taskId}}.
|
||||
* 订单列表重算,仅基于本地 prc_order_bill(不查询 Oracle TAB_ORDER)。
|
||||
* 轮询 {@code GET /orderBill/rebill/task/{taskId}}。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:orderBill:rebill')")
|
||||
@Log(title = "Order bill local rebill", businessType = BusinessType.UPDATE)
|
||||
@@ -325,7 +344,7 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Order no options for remote-search dropdown (bill list multi search).
|
||||
* 远程搜索下拉的订单号选项(计费列表多选搜索)。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:bill') or @ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/bill/orderNos")
|
||||
@@ -367,8 +386,8 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* ES BILL logs for this order (billing / upstream sync / fail snapshots).
|
||||
* bizId prefers transferNo then orderNo (same as write path).
|
||||
* 该订单的 ES BILL 日志(计费 / 上游同步 / 失败快照)。
|
||||
* bizId 优先取 transferNo,其次取 orderNo(与写入路径一致)。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:bill') or @ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/bill/{billId}/history")
|
||||
@@ -393,7 +412,7 @@ public class BillingController extends BaseController
|
||||
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue();
|
||||
String primary = EsBizLogSupport.billBizId(bill.getTransferNo(), bill.getOrderNo());
|
||||
BillSearchPage page = billSearchStore.search(EsLogType.BILL, primary, null, pageNum, pageSize);
|
||||
// Also pull by orderNo when transferNo was used as bizId, so older orderNo-keyed rows appear.
|
||||
// 当 bizId 使用 transferNo 时,再按 orderNo 拉取一次,以便出现更早的以 orderNo 为 key 的记录。
|
||||
if (StringUtils.isNotEmpty(bill.getTransferNo()) && StringUtils.isNotEmpty(bill.getOrderNo())
|
||||
&& !bill.getTransferNo().trim().equalsIgnoreCase(bill.getOrderNo().trim())
|
||||
&& pageNum == 1)
|
||||
@@ -458,7 +477,7 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Refund one receivable bill via billing task billType=REFUND.
|
||||
* 通过计费任务 billType=REFUND 退费单笔应收账单。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:refund')")
|
||||
@Log(title = "Billing refund", businessType = BusinessType.UPDATE)
|
||||
@@ -495,7 +514,7 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch update order credit arrears status (ARREARS -> NORMAL) with available-credit check.
|
||||
* 批量更新订单信用欠款状态(ARREARS -> NORMAL),并校验可用额度。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:arrearsUpdate')")
|
||||
@Log(title = "Billing credit arrears", businessType = BusinessType.UPDATE)
|
||||
@@ -555,7 +574,7 @@ public class BillingController extends BaseController
|
||||
return success(prcOrderBillService.addIncomeFee(request));
|
||||
}
|
||||
|
||||
/** Preview matched bills for batch add fee (filter: customer / transferNo / country). */
|
||||
/** 批量加费前预览匹配的账单(筛选:客户 / transferNo / 国家)。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:feeAdd')")
|
||||
@PostMapping("/bill/fee/batch/preview")
|
||||
public AjaxResult previewBatchAddIncomeFee(@RequestBody BatchAddIncomeBillFeeRequest request)
|
||||
@@ -563,7 +582,7 @@ public class BillingController extends BaseController
|
||||
return success(prcOrderBillService.previewBatchAddIncomeFee(request));
|
||||
}
|
||||
|
||||
/** Submit async batch add fee; poll /bill/fee/batch/task/{taskId}. */
|
||||
/** 提交异步批量加费;轮询 /bill/fee/batch/task/{taskId}。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:feeAdd')")
|
||||
@Log(title = "Billing batch income fee", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/bill/fee/batch")
|
||||
@@ -587,7 +606,7 @@ public class BillingController extends BaseController
|
||||
return success(task);
|
||||
}
|
||||
|
||||
/** Async recalc product_amount only. Poll /bill/recalcPrice/task/{taskId}. */
|
||||
/** 仅异步重算 product_amount。轮询 /bill/recalcPrice/task/{taskId}。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:recalc')")
|
||||
@Log(title = "Billing recalc product amount", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/bill/recalcProduct")
|
||||
@@ -601,7 +620,7 @@ public class BillingController extends BaseController
|
||||
return successTask(prcOrderBillService.submitRecalcBillPrice(request));
|
||||
}
|
||||
|
||||
/** Async recalc redline_amount only. Poll /bill/recalcPrice/task/{taskId}. */
|
||||
/** 仅异步重算 redline_amount。轮询 /bill/recalcPrice/task/{taskId}。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:recalc')")
|
||||
@Log(title = "Billing recalc redline amount", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/bill/recalcRedline")
|
||||
@@ -658,7 +677,7 @@ public class BillingController extends BaseController
|
||||
return success(result);
|
||||
}
|
||||
|
||||
/** Update charge_weight (keep weight), then rebill. */
|
||||
/** 更新 charge_weight(保留 weight),随后重新计费。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:chargeWeightEdit')")
|
||||
@Log(title = "Billing charge weight", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/bill/chargeWeight")
|
||||
@@ -668,7 +687,7 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Download import template. fillByFilter=true fills orderNo/customer from list filters.
|
||||
* 下载导入模板。fillByFilter=true 时按列表筛选条件填充 orderNo/customer。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:chargeWeightEdit')")
|
||||
@PostMapping("/bill/chargeWeight/importTemplate")
|
||||
@@ -690,8 +709,8 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Async import charge_weight updates and rebill.
|
||||
* Returns taskId; poll /bill/chargeWeight/import/task/{taskId}.
|
||||
* 异步导入 charge_weight 更新并重新计费。
|
||||
* 返回 taskId;轮询 /bill/chargeWeight/import/task/{taskId}。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:chargeWeightEdit')")
|
||||
@Log(title = "Billing charge weight import", businessType = BusinessType.IMPORT)
|
||||
@@ -722,7 +741,7 @@ public class BillingController extends BaseController
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Import task already queued; attachment failure should not block rebill
|
||||
// 导入任务已入队;附件保存失败不应阻塞后续重新计费
|
||||
Map<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put("taskId", taskId);
|
||||
payload.put("attachmentWarn", e.getMessage());
|
||||
@@ -743,7 +762,7 @@ public class BillingController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* List charge-weight import Excel attachment records.
|
||||
* 查询收费重导入的 Excel 附件记录。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:chargeWeightEdit')")
|
||||
@GetMapping("/bill/chargeWeight/import/attachments")
|
||||
|
||||
+11
-11
@@ -37,7 +37,7 @@ import com.bms.price.service.IPrcPriceExcelFileService;
|
||||
import com.bms.price.service.IPrcPriceSetService;
|
||||
|
||||
/**
|
||||
* Exclusive quote (shares prc_price_set with PACKAGE)
|
||||
* 独家报价(与 PACKAGE 共用 prc_price_set 表)
|
||||
*
|
||||
* @author bms
|
||||
*/
|
||||
@@ -82,7 +82,7 @@ public class ExclusiveQuoteController extends BaseController
|
||||
set.setCreateBy(getUsername());
|
||||
set.setUpdateBy(getUsername());
|
||||
String taskId = priceSetService.submitSaveAsync(set);
|
||||
// Do not pass bare String to success(): overload puts it into msg, not data.
|
||||
// 不要向 success() 直接传字符串:该重载会把字符串放进 msg 而不是 data。
|
||||
Map<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put("taskId", taskId);
|
||||
return success(payload);
|
||||
@@ -111,7 +111,7 @@ public class ExclusiveQuoteController extends BaseController
|
||||
return toAjax(priceSetService.updatePrcPriceSet(set));
|
||||
}
|
||||
|
||||
/** Update package name and note only. */
|
||||
/** 仅更新套餐名称与备注。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:exclusive:edit')")
|
||||
@Log(title = "Exclusive quote basic", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/basic")
|
||||
@@ -200,7 +200,7 @@ public class ExclusiveQuoteController extends BaseController
|
||||
return toAjax(priceSetService.deleteVersion(psvId, getUsername()));
|
||||
}
|
||||
|
||||
/** Import excel attachments for a version (prc_price_excel_file.psv_id). */
|
||||
/** 版本的 Excel 附件导入(prc_price_excel_file.psv_id)。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:exclusive:list')")
|
||||
@GetMapping("/version/{psvId}/excelFiles")
|
||||
public AjaxResult versionExcelFiles(@PathVariable Long psvId)
|
||||
@@ -253,7 +253,7 @@ public class ExclusiveQuoteController extends BaseController
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/** All detail rows for one product (no page); used by edit-price drawer. */
|
||||
/** 单个产品的全部明细行(不分页),供改价抽屉使用。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:exclusive:list')")
|
||||
@GetMapping("/detail/byProduct")
|
||||
public AjaxResult detailByProduct(@RequestParam("psvId") Long psvId,
|
||||
@@ -262,7 +262,7 @@ public class ExclusiveQuoteController extends BaseController
|
||||
return success(priceSetService.selectDetailListByProduct(psvId, productCode));
|
||||
}
|
||||
|
||||
/** All detail rows for a version (no page); batch prefetch instead of N byProduct calls. */
|
||||
/** 某个版本的全部明细行(不分页);用批量预取替代 N 次 byProduct 调用。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:exclusive:list')")
|
||||
@GetMapping("/detail/byVersion")
|
||||
public AjaxResult detailByVersion(@RequestParam("psvId") Long psvId)
|
||||
@@ -318,7 +318,7 @@ public class ExclusiveQuoteController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Preview pull product library (no DB write). For create wizard before finish.
|
||||
* 预览拉取产品库(不写库),用于新建向导提交前的预览。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:exclusive:edit') or @ss.hasPermi('price:exclusive:forceEdit') or @ss.hasPermi('price:exclusive:add')")
|
||||
@PostMapping("/detail/previewPullFromLibrary")
|
||||
@@ -328,8 +328,8 @@ public class ExclusiveQuoteController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Preview Excel import (no DB write). Optional detailsJson for updateOnly / build-price merge.
|
||||
* Optional countryCode = single-country dedicated-line Excel (same as product country import).
|
||||
* 预览 Excel 导入(不写库)。可选 detailsJson,用于 updateOnly / 构建价格合并。
|
||||
* 可选 countryCode = 单国家专线 Excel(与按产品国家导入一致)。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:exclusive:edit') or @ss.hasPermi('price:exclusive:forceEdit') or @ss.hasPermi('price:exclusive:add')")
|
||||
@PostMapping("/detail/previewImportPrices")
|
||||
@@ -359,7 +359,7 @@ public class ExclusiveQuoteController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Async pull product library into version details. Returns taskId; poll /save/task/{taskId}.
|
||||
* 异步拉取产品库到版本明细。返回 taskId,通过 /save/task/{taskId} 轮询进度。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:exclusive:edit') or @ss.hasPermi('price:exclusive:forceEdit')")
|
||||
@Log(title = "Exclusive quote拉取产品", businessType = BusinessType.IMPORT)
|
||||
@@ -376,7 +376,7 @@ public class ExclusiveQuoteController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Async batch write of detail surcharge snapshots. Returns taskId; poll /save/task/{taskId}.
|
||||
* 异步批量写入明细附加费快照。返回 taskId,通过 /save/task/{taskId} 轮询进度。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:exclusive:edit') or @ss.hasPermi('price:exclusive:forceEdit')")
|
||||
@Log(title = "Exclusive quote附加费", businessType = BusinessType.UPDATE)
|
||||
|
||||
+357
@@ -0,0 +1,357 @@
|
||||
package com.bms.web.controller.price;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
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.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.exception.ServiceException;
|
||||
import com.bms.price.domain.PayProduct;
|
||||
import com.bms.price.domain.PaySaveCommand;
|
||||
import com.bms.price.domain.PrcCustomerPriceSet;
|
||||
import com.bms.price.domain.PrcCustomerPriceSetBatch;
|
||||
import com.bms.price.domain.PrcPriceSet;
|
||||
import com.bms.price.service.IPayProductService;
|
||||
import com.bms.price.service.IPrcPriceSetService;
|
||||
|
||||
/**
|
||||
* 打单套餐:套餐壳 + 应收价卡(pay_*)。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/price/label")
|
||||
public class LabelPackageController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IPrcPriceSetService priceSetService;
|
||||
|
||||
@Autowired
|
||||
private IPayProductService payProductService;
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(PrcPriceSet set)
|
||||
{
|
||||
set.setSetCategory(PrcPriceSet.CATEGORY_LABEL);
|
||||
startPage();
|
||||
List<PrcPriceSet> list = priceSetService.selectPrcPriceSetList(set);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:query')")
|
||||
@GetMapping("/{priceSetId:[0-9]+}")
|
||||
public AjaxResult getInfo(@PathVariable Long priceSetId)
|
||||
{
|
||||
priceSetService.assertSetCategory(priceSetId, PrcPriceSet.CATEGORY_LABEL);
|
||||
return success(priceSetService.selectPrcPriceSetById(priceSetId));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:edit')")
|
||||
@Log(title = "打单套餐基本信息", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/basic")
|
||||
public AjaxResult editBasic(@RequestBody PrcPriceSet set)
|
||||
{
|
||||
priceSetService.assertSetCategory(set.getPriceSetId(), PrcPriceSet.CATEGORY_LABEL);
|
||||
set.setUpdateBy(getUsername());
|
||||
return toAjax(priceSetService.updateBasicInfo(set));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:audit')")
|
||||
@Log(title = "打单套餐停用", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/{priceSetId}/disable")
|
||||
public AjaxResult disable(@PathVariable Long priceSetId)
|
||||
{
|
||||
priceSetService.assertSetCategory(priceSetId, PrcPriceSet.CATEGORY_LABEL);
|
||||
return toAjax(priceSetService.disable(priceSetId, getUsername()));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:audit')")
|
||||
@Log(title = "打单套餐启用", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/{priceSetId}/enable")
|
||||
public AjaxResult enable(@PathVariable Long priceSetId)
|
||||
{
|
||||
priceSetService.assertSetCategory(priceSetId, PrcPriceSet.CATEGORY_LABEL);
|
||||
return toAjax(priceSetService.enable(priceSetId, getUsername()));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:remove')")
|
||||
@Log(title = "打单套餐删除", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{priceSetIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] priceSetIds)
|
||||
{
|
||||
if (priceSetIds != null)
|
||||
{
|
||||
for (Long id : priceSetIds)
|
||||
{
|
||||
priceSetService.assertSetCategory(id, PrcPriceSet.CATEGORY_LABEL);
|
||||
}
|
||||
}
|
||||
return toAjax(priceSetService.deletePrcPriceSetByIds(priceSetIds));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:import')")
|
||||
@PostMapping("/preview")
|
||||
public AjaxResult preview(@RequestParam("file") MultipartFile file,
|
||||
@RequestParam(value = "dictValue", required = false) String dictValue,
|
||||
@RequestParam(value = "progressId", required = false) String progressId,
|
||||
@RequestParam(value = "priceSetId", required = false) Long priceSetId) throws Exception
|
||||
{
|
||||
if (file == null || file.isEmpty())
|
||||
{
|
||||
throw new ServiceException("请上传价卡文件");
|
||||
}
|
||||
String name = file.getOriginalFilename();
|
||||
if (name == null || !name.toLowerCase(Locale.ROOT).endsWith(".xlsx"))
|
||||
{
|
||||
throw new ServiceException("请上传xlsx价卡");
|
||||
}
|
||||
if (priceSetId != null)
|
||||
{
|
||||
priceSetService.assertSetCategory(priceSetId, PrcPriceSet.CATEGORY_LABEL);
|
||||
}
|
||||
// 打单独立预览:已存在按本套餐判断,不与应付公共模板互挡
|
||||
return success(payProductService.previewLabel(file.getBytes(), name, dictValue, progressId, priceSetId));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:import')")
|
||||
@GetMapping("/preview/progress")
|
||||
public AjaxResult parseProgress(@RequestParam("progressId") String progressId)
|
||||
{
|
||||
return success(payProductService.parseProgress(progressId));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:import')")
|
||||
@Log(title = "打单套餐导入", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importSave")
|
||||
public AjaxResult importSave(@RequestBody PaySaveCommand command)
|
||||
{
|
||||
if (command == null)
|
||||
{
|
||||
throw new ServiceException("保存参数不能为空");
|
||||
}
|
||||
String message = payProductService.saveLabel(command);
|
||||
Map<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put("message", message);
|
||||
payload.put("priceSetId", command.getPriceSetId());
|
||||
return success(payload);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:list')")
|
||||
@GetMapping("/{priceSetId}/versions")
|
||||
public AjaxResult versions(@PathVariable Long priceSetId)
|
||||
{
|
||||
priceSetService.assertSetCategory(priceSetId, PrcPriceSet.CATEGORY_LABEL);
|
||||
return success(payProductService.selectLabelVersions(priceSetId));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:edit')")
|
||||
@Log(title = "打单套餐启动版本", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/{priceSetId}/versions/{versionNo}/activate")
|
||||
public AjaxResult activateVersion(@PathVariable Long priceSetId, @PathVariable Integer versionNo,
|
||||
@RequestBody Map<String, Object> body)
|
||||
{
|
||||
priceSetService.assertSetCategory(priceSetId, PrcPriceSet.CATEGORY_LABEL);
|
||||
Date effectiveTime = parseEffectiveTime(body == null ? null : body.get("effectiveTime"));
|
||||
String message = payProductService.activateLabelVersion(priceSetId, versionNo, effectiveTime);
|
||||
Map<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put("message", message);
|
||||
return success(payload);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:remove')")
|
||||
@Log(title = "打单套餐删除版本", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{priceSetId}/versions/{versionNo}")
|
||||
public AjaxResult deleteVersion(@PathVariable Long priceSetId, @PathVariable Integer versionNo)
|
||||
{
|
||||
priceSetService.assertSetCategory(priceSetId, PrcPriceSet.CATEGORY_LABEL);
|
||||
return toAjax(payProductService.deleteLabelVersion(priceSetId, versionNo));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:list')")
|
||||
@GetMapping("/{priceSetId}/products")
|
||||
public AjaxResult products(@PathVariable Long priceSetId,
|
||||
@RequestParam(value = "versionNo", required = false) Integer versionNo)
|
||||
{
|
||||
priceSetService.assertSetCategory(priceSetId, PrcPriceSet.CATEGORY_LABEL);
|
||||
if (versionNo != null)
|
||||
{
|
||||
return success(payProductService.selectLabelProductsByVersion(priceSetId, versionNo));
|
||||
}
|
||||
return success(payProductService.selectLabelProducts(priceSetId));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:query')")
|
||||
@GetMapping("/product/{productId}")
|
||||
public AjaxResult productDetail(@PathVariable Long productId,
|
||||
@RequestParam(value = "termId", required = false) Long termId)
|
||||
{
|
||||
if (termId != null)
|
||||
{
|
||||
return success(payProductService.selectPayProductDetailByTerm(termId));
|
||||
}
|
||||
return success(payProductService.selectPayProductDetail(productId));
|
||||
}
|
||||
|
||||
/** 保存打单产品明细(重量段/附加费等),按当前 term 覆盖子表 */
|
||||
@PreAuthorize("@ss.hasPermi('price:label:edit')")
|
||||
@Log(title = "打单产品明细", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/product")
|
||||
public AjaxResult updateProduct(@RequestBody PayProduct product)
|
||||
{
|
||||
return toAjax(payProductService.updateDetail(product));
|
||||
}
|
||||
|
||||
/** 查询打单产品已绑定渠道(默认应收 AR) */
|
||||
@PreAuthorize("@ss.hasPermi('price:label:query')")
|
||||
@GetMapping("/product/{productId}/channels")
|
||||
public AjaxResult productChannels(@PathVariable Long productId,
|
||||
@RequestParam(value = "payType", required = false) String payType)
|
||||
{
|
||||
String type = (payType == null || payType.trim().isEmpty()) ? "AR" : payType.trim();
|
||||
return success(payProductService.listProductChannelBinds(productId, type));
|
||||
}
|
||||
|
||||
/** 覆盖保存打单产品渠道绑定,写入 srv_channel_product_map */
|
||||
@PreAuthorize("@ss.hasPermi('price:label:edit')")
|
||||
@Log(title = "打单产品绑定渠道", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/product/{productId}/channels")
|
||||
public AjaxResult replaceProductChannels(@PathVariable Long productId, @RequestBody Map<String, Object> body)
|
||||
{
|
||||
String payType = body == null || body.get("payType") == null ? "AR" : String.valueOf(body.get("payType")).trim();
|
||||
List<String> channelIds = new ArrayList<String>();
|
||||
Object rawIds = body == null ? null : body.get("channelIds");
|
||||
if (rawIds instanceof List<?>)
|
||||
{
|
||||
for (Object item : (List<?>) rawIds)
|
||||
{
|
||||
if (item != null && !String.valueOf(item).trim().isEmpty())
|
||||
{
|
||||
channelIds.add(String.valueOf(item).trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
String msg = payProductService.replaceProductChannelBinds(productId, payType, channelIds);
|
||||
return AjaxResult.success(msg);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:remove')")
|
||||
@Log(title = "打单套餐移除产品", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{priceSetId}/products/{productIds}")
|
||||
public AjaxResult removeProducts(@PathVariable Long priceSetId, @PathVariable Long[] productIds)
|
||||
{
|
||||
priceSetService.assertSetCategory(priceSetId, PrcPriceSet.CATEGORY_LABEL);
|
||||
return toAjax(payProductService.removeLabelProducts(priceSetId, productIds));
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:list')")
|
||||
@GetMapping("/customer/list")
|
||||
public TableDataInfo customerList(PrcCustomerPriceSet customer)
|
||||
{
|
||||
if (customer != null && customer.getPriceSetId() != null)
|
||||
{
|
||||
priceSetService.assertSetCategory(customer.getPriceSetId(), PrcPriceSet.CATEGORY_LABEL);
|
||||
}
|
||||
startPage();
|
||||
List<PrcCustomerPriceSet> list = priceSetService.selectCustomerList(customer);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:edit')")
|
||||
@Log(title = "打单套餐客户绑定", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/customer")
|
||||
public AjaxResult addCustomer(@RequestBody PrcCustomerPriceSet customer)
|
||||
{
|
||||
if (customer == null || customer.getPriceSetId() == null)
|
||||
{
|
||||
throw new ServiceException("请选择打单套餐");
|
||||
}
|
||||
priceSetService.assertSetCategory(customer.getPriceSetId(), PrcPriceSet.CATEGORY_LABEL);
|
||||
customer.setCreateBy(getUsername());
|
||||
return toAjax(priceSetService.insertCustomer(customer));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:edit')")
|
||||
@Log(title = "打单套餐客户批量绑定", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/customer/batch")
|
||||
public AjaxResult batchAddCustomer(@RequestBody PrcCustomerPriceSetBatch batch)
|
||||
{
|
||||
if (batch == null || batch.getPriceSetId() == null)
|
||||
{
|
||||
throw new ServiceException("请选择打单套餐");
|
||||
}
|
||||
priceSetService.assertSetCategory(batch.getPriceSetId(), PrcPriceSet.CATEGORY_LABEL);
|
||||
batch.setCreateBy(getUsername());
|
||||
batch.setUpdateBy(getUsername());
|
||||
return success(priceSetService.batchInsertCustomers(batch));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:edit')")
|
||||
@Log(title = "打单套餐客户绑定", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/customer")
|
||||
public AjaxResult editCustomer(@RequestBody PrcCustomerPriceSet customer)
|
||||
{
|
||||
if (customer == null)
|
||||
{
|
||||
throw new ServiceException("客户绑定参数不能为空");
|
||||
}
|
||||
if (customer.getPriceSetId() != null)
|
||||
{
|
||||
priceSetService.assertSetCategory(customer.getPriceSetId(), PrcPriceSet.CATEGORY_LABEL);
|
||||
}
|
||||
customer.setUpdateBy(getUsername());
|
||||
return toAjax(priceSetService.updateCustomer(customer));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:label:edit')")
|
||||
@Log(title = "打单套餐客户绑定", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/customer/{customerPricesetIds}")
|
||||
public AjaxResult removeCustomer(@PathVariable Long[] customerPricesetIds)
|
||||
{
|
||||
return toAjax(priceSetService.deleteCustomerByIds(customerPricesetIds));
|
||||
}
|
||||
|
||||
private Date parseEffectiveTime(Object raw)
|
||||
{
|
||||
if (raw == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (raw instanceof Date)
|
||||
{
|
||||
return (Date) raw;
|
||||
}
|
||||
String text = String.valueOf(raw).trim();
|
||||
if (text.isEmpty() || "null".equalsIgnoreCase(text))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// 兼容 yyyy-MM-dd / yyyy-MM-dd HH:mm / yyyy-MM-dd HH:mm:ss
|
||||
if (text.length() >= 16)
|
||||
{
|
||||
return com.bms.common.utils.DateUtils.parseDate(text);
|
||||
}
|
||||
if (text.length() >= 10)
|
||||
{
|
||||
return com.bms.common.utils.DateUtils.parseDate(text.substring(0, 10));
|
||||
}
|
||||
return com.bms.common.utils.DateUtils.parseDate(text);
|
||||
}
|
||||
}
|
||||
+57
-12
@@ -1,6 +1,8 @@
|
||||
package com.bms.web.controller.price;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Locale;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@@ -20,7 +22,7 @@ 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.exception.ServiceException;
|
||||
import com.bms.common.enums.PaymentMesEnum;
|
||||
import com.bms.common.utils.file.FileUtils;
|
||||
import com.bms.common.utils.poi.ExcelUtil;
|
||||
import com.bms.price.domain.ChannelProductMap;
|
||||
@@ -84,7 +86,8 @@ public class PayProductController extends BaseController
|
||||
@PostMapping("/order/{billId}")
|
||||
public AjaxResult generateOrderCost(@PathVariable String billId)
|
||||
{
|
||||
return success(payOrderCostService.generate(billId));
|
||||
payOrderCostService.generate(billId);
|
||||
return success("操作成功");
|
||||
}
|
||||
|
||||
/** 下载某一版导入时上传的源文件 */
|
||||
@@ -114,17 +117,18 @@ public class PayProductController extends BaseController
|
||||
return success(payProductService.selectTemplateOptions(payType));
|
||||
}
|
||||
|
||||
/** 渠道当前价格副本 */
|
||||
/** 渠道当前价格副本;payType 默认应付 AP */
|
||||
@PreAuthorize("@ss.hasAnyPermi('price:pay:query,data:serviceChannel:query,data:serviceChannel:edit')")
|
||||
@GetMapping("/channel/{channelId}")
|
||||
public AjaxResult channelPrice(@PathVariable String channelId)
|
||||
public AjaxResult channelPrice(@PathVariable String channelId,
|
||||
@RequestParam(value = "payType", required = false) String payType)
|
||||
{
|
||||
return success(payProductService.selectChannelPrice(channelId));
|
||||
return success(payProductService.selectChannelPrice(channelId, payType));
|
||||
}
|
||||
|
||||
/** 渠道只绑定产品编号,以后按有效期取价 */
|
||||
/** 渠道只绑定产品编号,以后按有效期取价;支持应收 AR 与应付 AP */
|
||||
@PreAuthorize("@ss.hasAnyPermi('price:pay:edit,data:serviceChannel:edit')")
|
||||
@Log(title = "渠道应付产品", businessType = BusinessType.UPDATE)
|
||||
@Log(title = "渠道产品绑定", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/channel/{channelId}/bind")
|
||||
public AjaxResult bindChannel(@PathVariable String channelId, @RequestBody ChannelProductMap link)
|
||||
{
|
||||
@@ -134,8 +138,31 @@ public class PayProductController extends BaseController
|
||||
payProductService.unbindChannel(channelId, payType);
|
||||
return success();
|
||||
}
|
||||
payProductService.bindChannel(channelId, link.getProductId(), link.getPriceSetId(), payType);
|
||||
return success(payProductService.selectChannelPrice(channelId));
|
||||
payProductService.bindChannel(channelId, link.getProductId(), payType);
|
||||
return success(payProductService.selectChannelPrice(channelId, payType));
|
||||
}
|
||||
|
||||
/** 批量绑定渠道应收/应付产品 */
|
||||
@PreAuthorize("@ss.hasAnyPermi('price:pay:edit,data:serviceChannel:edit')")
|
||||
@Log(title = "渠道批量绑定产品", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/channel/batchBind")
|
||||
public AjaxResult batchBindChannel(@RequestBody Map<String, Object> body)
|
||||
{
|
||||
List<String> channelIds = new ArrayList<String>();
|
||||
Object rawIds = body == null ? null : body.get("channelIds");
|
||||
if (rawIds instanceof List<?>)
|
||||
{
|
||||
for (Object item : (List<?>) rawIds)
|
||||
{
|
||||
if (item != null && !String.valueOf(item).trim().isEmpty())
|
||||
{
|
||||
channelIds.add(String.valueOf(item).trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
Long apProductId = toLong(body == null ? null : body.get("apProductId"));
|
||||
Long arProductId = toLong(body == null ? null : body.get("arProductId"));
|
||||
return success(payProductService.batchBindChannel(channelIds, apProductId, arProductId));
|
||||
}
|
||||
|
||||
/** 对照两个价格版本 */
|
||||
@@ -234,16 +261,34 @@ public class PayProductController extends BaseController
|
||||
{
|
||||
if (file == null || file.isEmpty())
|
||||
{
|
||||
throw new ServiceException("请选择价卡文件");
|
||||
throw PaymentMesEnum.BMS_PAY_PICK_FILE.exception();
|
||||
}
|
||||
String filename = file.getOriginalFilename();
|
||||
if (filename == null || !filename.toLowerCase(Locale.ROOT).endsWith(".xlsx"))
|
||||
{
|
||||
throw new ServiceException("请上传xlsx价卡");
|
||||
throw PaymentMesEnum.BMS_PAY_XLSX.exception();
|
||||
}
|
||||
if (file.getSize() > 30L * 1024 * 1024)
|
||||
{
|
||||
throw new ServiceException("价卡文件不能超过30MB");
|
||||
throw PaymentMesEnum.BMS_PAY_FILE_30M.exception();
|
||||
}
|
||||
}
|
||||
|
||||
private Long toLong(Object raw)
|
||||
{
|
||||
if (raw == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (raw instanceof Number)
|
||||
{
|
||||
return ((Number) raw).longValue();
|
||||
}
|
||||
String text = String.valueOf(raw).trim();
|
||||
if (text.isEmpty() || "null".equalsIgnoreCase(text))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Long.valueOf(text);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -55,7 +55,7 @@ public class PriceSetController extends BaseController
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(PrcPriceSet set)
|
||||
{
|
||||
// Ordinary package module: never mix exclusive quotes into this list.
|
||||
// 普通套餐模块:该列表不混入专属报价。
|
||||
set.setSetCategory(PrcPriceSet.CATEGORY_PACKAGE);
|
||||
startPage();
|
||||
List<PrcPriceSet> list = priceSetService.selectPrcPriceSetList(set);
|
||||
@@ -83,7 +83,7 @@ public class PriceSetController extends BaseController
|
||||
set.setCreateBy(getUsername());
|
||||
set.setUpdateBy(getUsername());
|
||||
String taskId = priceSetService.submitSaveAsync(set);
|
||||
// Do not pass bare String to success(): overload puts it into msg, not data.
|
||||
// 不要向 success() 直接传裸 String:重载会把它放入 msg 而不是 data。
|
||||
Map<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put("taskId", taskId);
|
||||
return success(payload);
|
||||
@@ -112,7 +112,7 @@ public class PriceSetController extends BaseController
|
||||
return toAjax(priceSetService.updatePrcPriceSet(set));
|
||||
}
|
||||
|
||||
/** Update package name and note only. */
|
||||
/** 仅更新套餐名称和备注。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:set:edit')")
|
||||
@Log(title = "价格套餐基本信息", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/basic")
|
||||
@@ -190,7 +190,7 @@ public class PriceSetController extends BaseController
|
||||
return toAjax(priceSetService.deleteVersion(psvId, getUsername()));
|
||||
}
|
||||
|
||||
/** Import excel attachments for a version (prc_price_excel_file.psv_id). */
|
||||
/** 某个版本的导入 Excel 附件(prc_price_excel_file.psv_id)。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:set:list')")
|
||||
@GetMapping("/version/{psvId}/excelFiles")
|
||||
public AjaxResult versionExcelFiles(@PathVariable Long psvId)
|
||||
@@ -240,7 +240,7 @@ public class PriceSetController extends BaseController
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/** All detail rows for one product (no page); used by edit-price drawer. */
|
||||
/** 某个产品的全部明细行(不分页);供改价抽屉使用。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:set:list')")
|
||||
@GetMapping("/detail/byProduct")
|
||||
public AjaxResult detailByProduct(@RequestParam("psvId") Long psvId,
|
||||
@@ -249,7 +249,7 @@ public class PriceSetController extends BaseController
|
||||
return success(priceSetService.selectDetailListByProduct(psvId, productCode));
|
||||
}
|
||||
|
||||
/** All detail rows for a version (no page); batch prefetch instead of N byProduct calls. */
|
||||
/** 某个版本的全部明细行(不分页);批量预取,避免 N 次 byProduct 调用。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:set:list')")
|
||||
@GetMapping("/detail/byVersion")
|
||||
public AjaxResult detailByVersion(@RequestParam("psvId") Long psvId)
|
||||
@@ -304,7 +304,7 @@ public class PriceSetController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Preview pull product library (no DB write). For create wizard before finish.
|
||||
* 预览从产品库拉取(不写库)。用于新建向导完成前的预览。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:set:edit') or @ss.hasPermi('price:set:forceEdit') or @ss.hasPermi('price:set:add')")
|
||||
@PostMapping("/detail/previewPullFromLibrary")
|
||||
@@ -314,8 +314,8 @@ public class PriceSetController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Preview Excel import (no DB write). Optional detailsJson for updateOnly / build-price merge.
|
||||
* Optional countryCode = single-country dedicated-line Excel (same as product country import).
|
||||
* 预览 Excel 导入(不写库)。可选 detailsJson 用于 updateOnly / 构建价格时的合并。
|
||||
* 可选 countryCode = 单国家专线 Excel(与产品国家导入一致)。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:set:edit') or @ss.hasPermi('price:set:forceEdit') or @ss.hasPermi('price:set:add')")
|
||||
@PostMapping("/detail/previewImportPrices")
|
||||
@@ -345,7 +345,7 @@ public class PriceSetController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Async pull product library into version details. Returns taskId; poll /save/task/{taskId}.
|
||||
* 异步从产品库拉取到版本明细。返回 taskId;轮询 /save/task/{taskId}。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:set:edit') or @ss.hasPermi('price:set:forceEdit')")
|
||||
@Log(title = "套餐拉取产品", businessType = BusinessType.IMPORT)
|
||||
@@ -361,7 +361,7 @@ public class PriceSetController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Async batch write of detail surcharge snapshots. Returns taskId; poll /save/task/{taskId}.
|
||||
* 异步批量写入明细附加费快照。返回 taskId;轮询 /save/task/{taskId}。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:set:edit') or @ss.hasPermi('price:set:forceEdit')")
|
||||
@Log(title = "套餐附加费", businessType = BusinessType.UPDATE)
|
||||
|
||||
+12
-12
@@ -63,8 +63,8 @@ public class ProductController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Product dropdown (non-paged). Slim query, no list-page status refresh.
|
||||
* Used by price-set / billing / customer bind; login required, no extra perm.
|
||||
* 产品下拉(非分页)。精简查询,不做列表页状态刷新。
|
||||
* 供价格套餐 / 计费 / 客户绑定使用;需登录,无额外权限要求。
|
||||
*/
|
||||
@GetMapping("/optionselect")
|
||||
public AjaxResult optionselect(BmsProduct product)
|
||||
@@ -139,7 +139,7 @@ public class ProductController extends BaseController
|
||||
return success();
|
||||
}
|
||||
|
||||
/** Persist create-page preview details into draft/pending/effect version. */
|
||||
/** 将新建页预览明细保存到草稿/待生效/生效版本。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:product:add') or @ss.hasPermi('price:product:edit')")
|
||||
@Log(title = "product preview apply", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/version/applyPreview")
|
||||
@@ -148,7 +148,7 @@ public class ProductController extends BaseController
|
||||
return success(productService.applyCatalogPreview(body));
|
||||
}
|
||||
|
||||
/** Async parallel save of create-page preview. Returns taskId; poll /import/task/{taskId}. */
|
||||
/** 异步并行保存新建页预览。返回 taskId;轮询 /import/task/{taskId}。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:product:add') or @ss.hasPermi('price:product:edit')")
|
||||
@Log(title = "product preview apply", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/version/applyPreviewAsync")
|
||||
@@ -160,7 +160,7 @@ public class ProductController extends BaseController
|
||||
return success(payload);
|
||||
}
|
||||
|
||||
/** Save import Excel attachment onto a draft/pending/effect version (prc_price_excel_file). */
|
||||
/** 将导入 Excel 附件保存到草稿/待生效/生效版本(prc_price_excel_file)。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:product:import')")
|
||||
@Log(title = "product version excel", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/version/{psvId}/excel")
|
||||
@@ -176,7 +176,7 @@ public class ProductController extends BaseController
|
||||
return success(row);
|
||||
}
|
||||
|
||||
/** List Excel attachments of a version. */
|
||||
/** 查询某个版本的 Excel 附件列表。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:product:query')")
|
||||
@GetMapping("/version/{psvId}/excelFiles")
|
||||
public AjaxResult listVersionExcelFiles(@PathVariable Long psvId)
|
||||
@@ -201,8 +201,8 @@ public class ProductController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Async import Weishi B2C workbook. Returns taskId; poll /import/task/{taskId}.
|
||||
* effectiveDate is required: written to product / product_country; previous open rows expire at this date.
|
||||
* 异步导入 Weishi B2C 工作簿。返回 taskId;轮询 /import/task/{taskId}。
|
||||
* effectiveDate 必填:写入 product / product_country;原有未失效记录在该日期失效。
|
||||
*/
|
||||
@Log(title = "产品列表", businessType = BusinessType.IMPORT)
|
||||
@PreAuthorize("@ss.hasPermi('price:product:import')")
|
||||
@@ -217,8 +217,8 @@ public class ProductController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Async single-country cost-sheet import. Returns taskId; poll /import/task/{taskId}.
|
||||
* effectiveDate is required: written to product / product_country; previous open rows expire at this date.
|
||||
* 异步导入单国家成本表。返回 taskId;轮询 /import/task/{taskId}。
|
||||
* effectiveDate 必填:写入 product / product_country;原有未失效记录在该日期失效。
|
||||
*/
|
||||
@Log(title = "产品列表", businessType = BusinessType.IMPORT)
|
||||
@PreAuthorize("@ss.hasPermi('price:product:import')")
|
||||
@@ -233,7 +233,7 @@ public class ProductController extends BaseController
|
||||
return success(payload);
|
||||
}
|
||||
|
||||
/** Async parse-only preview; returns taskId, poll /import/task/{taskId} for result. */
|
||||
/** 仅解析的异步预览;返回 taskId,轮询 /import/task/{taskId} 获取结果。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:product:import')")
|
||||
@PostMapping("/previewImport")
|
||||
public AjaxResult previewImport(
|
||||
@@ -246,7 +246,7 @@ public class ProductController extends BaseController
|
||||
return success(payload);
|
||||
}
|
||||
|
||||
/** Async single-country parse-only preview; returns taskId, poll /import/task/{taskId}. */
|
||||
/** 单国家仅解析的异步预览;返回 taskId,轮询 /import/task/{taskId}。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:product:import')")
|
||||
@PostMapping("/previewImportCountry")
|
||||
public AjaxResult previewImportCountry(
|
||||
|
||||
+10
-10
@@ -139,7 +139,7 @@ public class RedlineProductController extends BaseController
|
||||
return success();
|
||||
}
|
||||
|
||||
/** Persist create-page preview details into draft/pending/effect version. */
|
||||
/** 将新建页预览明细保存到草稿/待生效/生效版本。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:redline:add') or @ss.hasPermi('price:redline:edit')")
|
||||
@Log(title = "redline preview apply", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/version/applyPreview")
|
||||
@@ -148,7 +148,7 @@ public class RedlineProductController extends BaseController
|
||||
return success(productService.applyCatalogPreview(body));
|
||||
}
|
||||
|
||||
/** Async parallel save of create-page preview. Returns taskId; poll /import/task/{taskId}. */
|
||||
/** 异步并行保存新建页预览。返回 taskId;轮询 /import/task/{taskId}。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:redline:add') or @ss.hasPermi('price:redline:edit')")
|
||||
@Log(title = "redline preview apply", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/version/applyPreviewAsync")
|
||||
@@ -160,7 +160,7 @@ public class RedlineProductController extends BaseController
|
||||
return success(payload);
|
||||
}
|
||||
|
||||
/** Save import Excel attachment onto a draft/pending/effect version (prc_price_excel_file). */
|
||||
/** 将导入 Excel 附件保存到草稿/待生效/生效版本(prc_price_excel_file)。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:redline:import')")
|
||||
@Log(title = "redline version excel", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/version/{psvId}/excel")
|
||||
@@ -176,7 +176,7 @@ public class RedlineProductController extends BaseController
|
||||
return success(row);
|
||||
}
|
||||
|
||||
/** List Excel attachments of a version. */
|
||||
/** 查询某个版本的 Excel 附件列表。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:redline:query')")
|
||||
@GetMapping("/version/{psvId}/excelFiles")
|
||||
public AjaxResult listVersionExcelFiles(@PathVariable Long psvId)
|
||||
@@ -201,8 +201,8 @@ public class RedlineProductController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Async import Weishi B2C workbook. Returns taskId; poll /import/task/{taskId}.
|
||||
* effectiveDate is required: written to product / product_country; previous open rows expire at this date.
|
||||
* 异步导入 Weishi B2C 工作簿。返回 taskId;轮询 /import/task/{taskId}。
|
||||
* effectiveDate 必填:写入 product / product_country;原有未失效记录在该日期失效。
|
||||
*/
|
||||
@Log(title = "产品列表", businessType = BusinessType.IMPORT)
|
||||
@PreAuthorize("@ss.hasPermi('price:redline:import')")
|
||||
@@ -217,8 +217,8 @@ public class RedlineProductController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Async single-country cost-sheet import. Returns taskId; poll /import/task/{taskId}.
|
||||
* effectiveDate is required: written to product / product_country; previous open rows expire at this date.
|
||||
* 异步导入单国家成本表。返回 taskId;轮询 /import/task/{taskId}。
|
||||
* effectiveDate 必填:写入 product / product_country;原有未失效记录在该日期失效。
|
||||
*/
|
||||
@Log(title = "产品列表", businessType = BusinessType.IMPORT)
|
||||
@PreAuthorize("@ss.hasPermi('price:redline:import')")
|
||||
@@ -233,7 +233,7 @@ public class RedlineProductController extends BaseController
|
||||
return success(payload);
|
||||
}
|
||||
|
||||
/** Async parse-only preview; returns taskId, poll /import/task/{taskId} for result. */
|
||||
/** 仅解析的异步预览;返回 taskId,轮询 /import/task/{taskId} 获取结果。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:redline:import')")
|
||||
@PostMapping("/previewImport")
|
||||
public AjaxResult previewImport(
|
||||
@@ -246,7 +246,7 @@ public class RedlineProductController extends BaseController
|
||||
return success(payload);
|
||||
}
|
||||
|
||||
/** Async single-country parse-only preview; returns taskId, poll /import/task/{taskId}. */
|
||||
/** 单国家仅解析的异步预览;返回 taskId,轮询 /import/task/{taskId}。 */
|
||||
@PreAuthorize("@ss.hasPermi('price:redline:import')")
|
||||
@PostMapping("/previewImportCountry")
|
||||
public AjaxResult previewImportCountry(
|
||||
|
||||
+58
@@ -16,10 +16,12 @@ 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.StringUtils;
|
||||
import com.bms.price.airbill.domain.SrvBillAirAllocCommand;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFeeLog;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFeeSave;
|
||||
import com.bms.price.domain.vo.SrvBillAirQuery;
|
||||
import com.bms.price.domain.vo.SrvBillAirVo;
|
||||
import com.bms.price.service.ISrvBillAirAllocService;
|
||||
import com.bms.price.service.ISrvBillAirFeeService;
|
||||
import com.bms.price.service.ISrvBillAirService;
|
||||
|
||||
@@ -36,6 +38,9 @@ public class SrvBillAirController extends BaseController
|
||||
@Autowired
|
||||
private ISrvBillAirFeeService srvBillAirFeeService;
|
||||
|
||||
@Autowired
|
||||
private ISrvBillAirAllocService srvBillAirAllocService;
|
||||
|
||||
/**
|
||||
* 提单列表
|
||||
*/
|
||||
@@ -94,4 +99,57 @@ public class SrvBillAirController extends BaseController
|
||||
{
|
||||
return success(srvBillAirFeeService.save(command));
|
||||
}
|
||||
|
||||
/** 已分摊的费用改为已确定 */
|
||||
@PreAuthorize("@ss.hasPermi('price:airBill:feeConfirm')")
|
||||
@Log(title = "提单费用确定", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/fee/confirm")
|
||||
public AjaxResult confirmFee(@RequestParam("billCode") String billCode, @RequestParam("feeId") Long feeId)
|
||||
{
|
||||
return success(srvBillAirFeeService.confirm(billCode, feeId));
|
||||
}
|
||||
|
||||
/** 作废已分摊或已确定的费用,并逻辑删除已分摊到订单的明细 */
|
||||
@PreAuthorize("@ss.hasPermi('price:airBill:feeVoid')")
|
||||
@Log(title = "提单费用作废", businessType = BusinessType.DELETE)
|
||||
@PostMapping("/fee/void")
|
||||
public AjaxResult voidFee(@RequestParam("billCode") String billCode, @RequestParam("feeId") Long feeId)
|
||||
{
|
||||
return success(srvBillAirFeeService.voidFee(billCode, feeId));
|
||||
}
|
||||
|
||||
/** 勾选提单后,查出需要分摊到订单的费用 */
|
||||
@PreAuthorize("@ss.hasPermi('price:airBill:fee')")
|
||||
@GetMapping("/fee/alloc/preview")
|
||||
public AjaxResult allocPreview(@RequestParam("billCodes") String billCodes)
|
||||
{
|
||||
return success(srvBillAirAllocService.preview(splitCodes(billCodes)));
|
||||
}
|
||||
|
||||
/** 按重量或订单数把费用分摊到订单,并把费用状态改为已分摊 */
|
||||
@PreAuthorize("@ss.hasPermi('price:airBill:fee')")
|
||||
@Log(title = "提单费用分摊", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/fee/alloc")
|
||||
public AjaxResult allocate(@RequestBody SrvBillAirAllocCommand command)
|
||||
{
|
||||
List<String> billCodes = command == null ? null : command.getBillCodes();
|
||||
return success(srvBillAirAllocService.allocate(billCodes));
|
||||
}
|
||||
|
||||
private static List<String> splitCodes(String billCodes)
|
||||
{
|
||||
List<String> codes = new ArrayList<>();
|
||||
if (StringUtils.isEmpty(billCodes))
|
||||
{
|
||||
return codes;
|
||||
}
|
||||
for (String part : billCodes.split("[,,\\s]+"))
|
||||
{
|
||||
if (StringUtils.isNotEmpty(part))
|
||||
{
|
||||
codes.add(part.trim());
|
||||
}
|
||||
}
|
||||
return codes;
|
||||
}
|
||||
}
|
||||
|
||||
+16
-1
@@ -20,6 +20,7 @@ import com.bms.common.core.page.TableDataInfo;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.price.domain.PrcSurcharge;
|
||||
import com.bms.price.domain.PrcSurchargeRel;
|
||||
import com.bms.price.service.IPayPostcodeService;
|
||||
import com.bms.price.service.IPrcSurchargeRelService;
|
||||
import com.bms.price.service.IPrcSurchargeService;
|
||||
|
||||
@@ -38,6 +39,9 @@ public class SurchargeController extends BaseController
|
||||
@Autowired
|
||||
private IPrcSurchargeRelService surchargeRelService;
|
||||
|
||||
@Autowired
|
||||
private IPayPostcodeService payPostcodeService;
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:surcharge:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(PrcSurcharge surcharge)
|
||||
@@ -47,6 +51,15 @@ public class SurchargeController extends BaseController
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/** 按国家从应付邮编配置拉取区块/分区选项 */
|
||||
@PreAuthorize("@ss.hasPermi('price:surcharge:list') or @ss.hasPermi('price:surcharge:query') or @ss.hasPermi('price:surcharge:add') or @ss.hasPermi('price:surcharge:edit')")
|
||||
@GetMapping("/blockOptions")
|
||||
public AjaxResult blockOptions(@RequestParam(required = false) String countryCode,
|
||||
@RequestParam(required = false) String countryName)
|
||||
{
|
||||
return success(payPostcodeService.selectBlockTitlesByCountry(countryCode, countryName));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:surcharge:query')")
|
||||
@GetMapping("/{scId}")
|
||||
public AjaxResult getInfo(@PathVariable Long scId)
|
||||
@@ -72,7 +85,9 @@ public class SurchargeController extends BaseController
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:surcharge:list') or @ss.hasPermi('price:surcharge:query')"
|
||||
+ " or @ss.hasPermi('price:set:query') or @ss.hasPermi('price:set:add') or @ss.hasPermi('price:set:edit')"
|
||||
+ " or @ss.hasPermi('price:billing:bill')")
|
||||
+ " or @ss.hasPermi('price:billing:bill')"
|
||||
+ " or @ss.hasPermi('price:pay:query') or @ss.hasPermi('price:pay:edit') or @ss.hasPermi('price:pay:import')"
|
||||
+ " or @ss.hasPermi('price:airBill:fee')")
|
||||
@GetMapping("/options")
|
||||
public AjaxResult listOptions(PrcSurcharge surcharge)
|
||||
{
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package com.bms.web.controller.ar;
|
||||
package com.bms.web.controller.receivable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -7,8 +7,8 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.bms.ar.domain.ArBankAccount;
|
||||
import com.bms.ar.service.IArBillService;
|
||||
import com.bms.receivable.domain.ArBankAccount;
|
||||
import com.bms.receivable.service.IArBillService;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
|
||||
+60
-36
@@ -1,8 +1,9 @@
|
||||
package com.bms.web.controller.ar;
|
||||
package com.bms.web.controller.receivable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@@ -23,21 +24,20 @@ import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.bms.ar.domain.ArBill;
|
||||
import com.bms.ar.domain.ArBankAccount;
|
||||
import com.bms.ar.domain.ArBillDetailFee;
|
||||
import com.bms.ar.domain.ArBillExport;
|
||||
import com.bms.ar.domain.ArBillGenerateTask;
|
||||
import com.bms.ar.domain.ArBillLog;
|
||||
import com.bms.ar.domain.vo.ArBillActionRequest;
|
||||
import com.bms.ar.domain.vo.ArBillGenerateQuery;
|
||||
import com.bms.ar.domain.vo.ArBillGeneratePreviewExportRow;
|
||||
import com.bms.ar.domain.vo.ArBillGenerateRequest;
|
||||
import com.bms.ar.domain.vo.ArBillListExportRow;
|
||||
import com.bms.ar.domain.vo.ArBillQuery;
|
||||
import com.bms.ar.enums.ArBillDateType;
|
||||
import com.bms.ar.service.IArBillService;
|
||||
import com.bms.ar.security.ArBillScopeResolver;
|
||||
import com.bms.receivable.domain.ArBill;
|
||||
import com.bms.receivable.domain.ArBankAccount;
|
||||
import com.bms.receivable.domain.ArBillDetailFee;
|
||||
import com.bms.receivable.domain.ArBillGenerateTask;
|
||||
import com.bms.receivable.domain.ArBillLog;
|
||||
import com.bms.receivable.domain.vo.ArBillActionRequest;
|
||||
import com.bms.receivable.domain.vo.ArBillGenerateQuery;
|
||||
import com.bms.receivable.domain.vo.ArBillGenerateRequest;
|
||||
import com.bms.receivable.domain.vo.ArBillListExportRow;
|
||||
import com.bms.receivable.domain.vo.ArBillQuery;
|
||||
import com.bms.receivable.service.IArBillService;
|
||||
import com.bms.receivable.service.ArBillVoidTaskService;
|
||||
import com.bms.receivable.security.ArBillScopeResolver;
|
||||
import com.bms.receivable.task.ArBillGeneratePreviewExportRunner;
|
||||
import com.bms.common.annotation.Log;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
@@ -62,9 +62,15 @@ public class ArBillController extends BaseController {
|
||||
@Autowired
|
||||
private IArBillService billService;
|
||||
|
||||
@Autowired
|
||||
private ArBillVoidTaskService voidTaskService;
|
||||
|
||||
@Autowired
|
||||
private ArBillScopeResolver billScope;
|
||||
|
||||
@Autowired
|
||||
private ArBillGeneratePreviewExportRunner previewExportRunner;
|
||||
|
||||
/** 应收筛选专用客户选项,与账单使用同一角色规则。 */
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:list')")
|
||||
@GetMapping("/customer-options")
|
||||
@@ -171,30 +177,35 @@ public class ArBillController extends BaseController {
|
||||
AjaxResult result = success();
|
||||
result.put("rows", pageData.getRows());
|
||||
result.put("total", pageData.getTotal());
|
||||
result.put("totalAmount", billService.sumPreviewBill(query));
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 按生成预览使用的相同筛选条件导出全部匹配订单,不受分页参数影响。 */
|
||||
/** 按需汇总生成预览范围内的应收金额。 */
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
|
||||
@PostMapping("/generate/preview/amount")
|
||||
public AjaxResult previewAmount(@RequestBody ArBillGenerateQuery query) {
|
||||
return success(billService.sumPreviewBill(query));
|
||||
}
|
||||
|
||||
/** 按生成预览使用的相同筛选条件提交异步导出任务。 */
|
||||
@Log(title = "应收账单生成订单", businessType = BusinessType.EXPORT)
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
|
||||
@PostMapping("/generate/preview/export")
|
||||
public void exportGeneratePreview(HttpServletResponse response, ArBillGenerateQuery query,
|
||||
HttpServletRequest request) {
|
||||
rejectMultiValueParameter(request, "customerId");
|
||||
ArBillGenerateQuery exportQuery = query == null ? new ArBillGenerateQuery() : query;
|
||||
List<PrcOrderBill> bills = billService.previewBill(exportQuery);
|
||||
List<ArBillGeneratePreviewExportRow> rows = new ArrayList<>(bills.size());
|
||||
for (PrcOrderBill bill : bills) {
|
||||
rows.add(ArBillGeneratePreviewExportRow.from(bill));
|
||||
}
|
||||
ExcelUtil<ArBillGeneratePreviewExportRow> util = new ExcelUtil<>(ArBillGeneratePreviewExportRow.class);
|
||||
if (ArBillDateType.PICKUP_DATE == ArBillDateType.of(exportQuery.getDateType())) {
|
||||
util.hideColumn("bizDate");
|
||||
} else {
|
||||
util.hideColumn("pickupDate");
|
||||
}
|
||||
util.exportExcel(response, rows, "应收账单待生成订单");
|
||||
public AjaxResult exportGeneratePreview(@RequestBody ArBillGenerateQuery query) {
|
||||
return success(Map.of("taskId", previewExportRunner.submit(query, getUserId())));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
|
||||
@GetMapping("/generate/preview/export/task/{taskId}")
|
||||
public AjaxResult generatePreviewExportTask(@PathVariable String taskId) {
|
||||
return success(previewExportRunner.getTask(taskId, getUserId()));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
|
||||
@GetMapping("/generate/preview/export/download/{taskId}")
|
||||
public void downloadGeneratePreviewExport(@PathVariable String taskId, HttpServletResponse response)
|
||||
throws Exception {
|
||||
previewExportRunner.download(taskId, getUserId(), response);
|
||||
}
|
||||
|
||||
/** 查询需要从全选结果中排除的订单;空条件不执行查询,避免返回全部订单。 */
|
||||
@@ -290,7 +301,13 @@ public class ArBillController extends BaseController {
|
||||
public AjaxResult voidBill(@PathVariable Long arBillId, @RequestBody ArBillActionRequest request,
|
||||
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) {
|
||||
String requestId = requestId(request == null ? null : request.getRequestId(), headerRequestId);
|
||||
return success(billService.voidBill(arBillId, request, getUsername(), requestId)).put("request_id", requestId);
|
||||
return success(voidTaskService.submit(arBillId, request == null ? null : request.getRemark(), getUsername(), requestId)).put("request_id", requestId);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:list')")
|
||||
@GetMapping("/void/task/{taskId}")
|
||||
public AjaxResult voidTask(@PathVariable Long taskId) {
|
||||
return success(voidTaskService.get(taskId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,9 +341,16 @@ public class ArBillController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载已成功生成的导出文件;文件内容由服务层从临时文件或配置存储读取。
|
||||
* 获取已成功生成导出文件的下载方式;OSS 文件返回短时签名链接,本地历史文件走原下载接口。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:download')")
|
||||
@GetMapping("/export/{exportId}/download-url")
|
||||
public AjaxResult exportDownloadInfo(@PathVariable Long exportId) {
|
||||
return success(billService.getExportDownloadInfo(exportId));
|
||||
}
|
||||
|
||||
/** 下载本地历史导出文件;OSS 文件由前端通过签名链接直接下载。 */
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:download')")
|
||||
@GetMapping("/export/{exportId}/download")
|
||||
public void download(@PathVariable Long exportId, HttpServletResponse response) throws Exception {
|
||||
billService.downloadExport(exportId, response);
|
||||
+3
-4
@@ -1,4 +1,4 @@
|
||||
package com.bms.web.controller.ar;
|
||||
package com.bms.web.controller.receivable;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
@@ -12,9 +12,8 @@ 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.domain.ArBill;
|
||||
import com.bms.ar.service.IArBalanceWriteoffService;
|
||||
import com.bms.receivable.domain.ArBalanceWriteoff;
|
||||
import com.bms.receivable.service.IArBalanceWriteoffService;
|
||||
import com.bms.common.annotation.Log;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
+10
-10
@@ -6,16 +6,16 @@ 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.ar.profit.ArCommissionRate;
|
||||
import com.bms.ar.profit.ArCommissionService;
|
||||
import com.bms.ar.profit.ArProfitQuery;
|
||||
import com.bms.ar.profit.ArProfitService;
|
||||
import com.bms.ar.profit.ArWriteoffRate;
|
||||
import com.bms.receivable.profit.ArCommissionRate;
|
||||
import com.bms.receivable.profit.ArCommissionService;
|
||||
import com.bms.receivable.profit.ArProfitQuery;
|
||||
import com.bms.receivable.profit.ArProfitService;
|
||||
import com.bms.receivable.profit.ArWriteoffRate;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import com.bms.ar.profit.ArProfitRow;
|
||||
import com.bms.ar.profit.ArCommissionResult;
|
||||
import com.bms.receivable.profit.ArProfitRow;
|
||||
import com.bms.receivable.profit.ArCommissionResult;
|
||||
import com.bms.common.utils.poi.ExcelUtil;
|
||||
import com.bms.price.enums.ProfitAreaRule;
|
||||
import com.bms.common.annotation.Log;
|
||||
@@ -99,7 +99,7 @@ public class ArProfitController extends BaseController
|
||||
return success();
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('report:arProfit:commission')")
|
||||
@PreAuthorize("@ss.hasPermi('report:arProfit:list')")
|
||||
@GetMapping("/commissions")
|
||||
public TableDataInfo commissions(@RequestParam(required = false) Long billId,
|
||||
@RequestParam(defaultValue = "false") boolean includeReversed)
|
||||
@@ -108,7 +108,7 @@ public class ArProfitController extends BaseController
|
||||
return getDataTable(commissions.results(billId, includeReversed));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('report:arProfit:commission')")
|
||||
@PreAuthorize("@ss.hasPermi('report:arProfit:list')")
|
||||
@GetMapping("/commissions/summary")
|
||||
public AjaxResult commissionSummary(@RequestParam(required = false) Long billId) { return success(commissions.summary(billId)); }
|
||||
|
||||
@@ -121,7 +121,7 @@ public class ArProfitController extends BaseController
|
||||
@PostMapping("/commissions/export")
|
||||
public void exportCommissions(HttpServletResponse response, @RequestParam(required = false) Long billId)
|
||||
{
|
||||
new ExcelUtil<>(ArCommissionResult.class).exportExcel(response, commissions.results(billId, false), "应收账单提成明细");
|
||||
new ExcelUtil<>(ArCommissionResult.class).exportExcel(response, commissions.exportResults(billId), "应收账单提成明细");
|
||||
}
|
||||
|
||||
@Log(title = "应收账单毛利分析", businessType = BusinessType.EXPORT)
|
||||
|
||||
+2
-2
@@ -47,7 +47,7 @@ public class SysFileTemplateController extends BaseController
|
||||
return success(fileTemplateService.selectSysFileTemplateById(templateId));
|
||||
}
|
||||
|
||||
/** Meta by file code (authenticated; used by import dialogs). */
|
||||
/** 按文件编码获取元信息(需登录,供导入弹窗使用)。 */
|
||||
@GetMapping("/code/{fileCode}")
|
||||
public AjaxResult getByCode(@PathVariable String fileCode)
|
||||
{
|
||||
@@ -91,7 +91,7 @@ public class SysFileTemplateController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Download by file_code. Login required; no admin perm so import pages can use it.
|
||||
* 按 file_code 下载。需要登录,但不校验管理员权限,便于导入页面调用。
|
||||
*/
|
||||
@GetMapping("/download/{fileCode}")
|
||||
public void download(@PathVariable String fileCode, HttpServletResponse response) throws Exception
|
||||
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
package com.bms.web.controller.tool;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
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.constant.ScheduleConstants;
|
||||
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.exception.ServiceException;
|
||||
import com.bms.common.exception.job.TaskException;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.quartz.domain.SysJob;
|
||||
import com.bms.quartz.service.ISysJobService;
|
||||
import com.bms.quartz.util.CronUtils;
|
||||
import com.bms.system.domain.SysDataMigration;
|
||||
import com.bms.system.domain.SysDataMigrationLog;
|
||||
import com.bms.system.service.ISysDataMigrationService;
|
||||
|
||||
/** 系统工具 - 数据迁移:保存 SQL、手动/定时执行(Quartz) */
|
||||
@RestController
|
||||
@RequestMapping("/tool/migration")
|
||||
public class DataMigrationController extends BaseController
|
||||
{
|
||||
private static final String JOB_GROUP = "DATA_MIGRATION";
|
||||
|
||||
@Autowired
|
||||
private ISysDataMigrationService migrationService;
|
||||
|
||||
@Autowired
|
||||
private ISysJobService jobService;
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('tool:migration:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(SysDataMigration query)
|
||||
{
|
||||
startPage();
|
||||
List<SysDataMigration> list = migrationService.selectSysDataMigrationList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('tool:migration:query')")
|
||||
@GetMapping("/{migrationId}")
|
||||
public AjaxResult getInfo(@PathVariable Long migrationId)
|
||||
{
|
||||
return success(migrationService.selectSysDataMigrationById(migrationId));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('tool:migration:add')")
|
||||
@Log(title = "数据迁移", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@Validated @RequestBody SysDataMigration row) throws SchedulerException, TaskException
|
||||
{
|
||||
row.setCreateBy(getUsername());
|
||||
migrationService.insertSysDataMigration(row);
|
||||
syncJob(row);
|
||||
return success(row);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('tool:migration:edit')")
|
||||
@Log(title = "数据迁移", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@Validated @RequestBody SysDataMigration row) throws SchedulerException, TaskException
|
||||
{
|
||||
row.setUpdateBy(getUsername());
|
||||
migrationService.updateSysDataMigration(row);
|
||||
SysDataMigration fresh = migrationService.selectSysDataMigrationById(row.getMigrationId());
|
||||
syncJob(fresh);
|
||||
return success(fresh);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('tool:migration:remove')")
|
||||
@Log(title = "数据迁移", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{migrationIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] migrationIds) throws SchedulerException
|
||||
{
|
||||
if (migrationIds != null)
|
||||
{
|
||||
for (Long id : migrationIds)
|
||||
{
|
||||
SysDataMigration row = migrationService.selectSysDataMigrationById(id);
|
||||
if (row != null)
|
||||
{
|
||||
removeJob(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
return toAjax(migrationService.deleteSysDataMigrationByIds(migrationIds));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('tool:migration:run')")
|
||||
@Log(title = "数据迁移执行", businessType = BusinessType.OTHER)
|
||||
@PostMapping("/run/{migrationId}")
|
||||
public AjaxResult run(@PathVariable Long migrationId)
|
||||
{
|
||||
Map<String, Object> result = migrationService.executeMigration(migrationId, "MANUAL", getUsername());
|
||||
return success(result);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('tool:migration:log')")
|
||||
@GetMapping("/log/list")
|
||||
public TableDataInfo logList(SysDataMigrationLog query)
|
||||
{
|
||||
startPage();
|
||||
List<SysDataMigrationLog> list = migrationService.selectSysDataMigrationLogList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
private void syncJob(SysDataMigration row) throws SchedulerException, TaskException
|
||||
{
|
||||
if (row == null || row.getMigrationId() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
boolean enable = "0".equals(row.getStatus()) && StringUtils.isNotEmpty(row.getCronExpression());
|
||||
if (enable && !CronUtils.isValid(row.getCronExpression()))
|
||||
{
|
||||
throw new ServiceException("Cron 表达式不正确");
|
||||
}
|
||||
if (!enable)
|
||||
{
|
||||
removeJob(row);
|
||||
return;
|
||||
}
|
||||
|
||||
String invokeTarget = "dataMigrationTask.executeById(" + row.getMigrationId() + "L)";
|
||||
String jobName = "data-migration-" + row.getMigrationId();
|
||||
if (row.getJobId() != null)
|
||||
{
|
||||
SysJob existed = jobService.selectJobById(row.getJobId());
|
||||
if (existed != null)
|
||||
{
|
||||
existed.setJobName(jobName);
|
||||
existed.setJobGroup(JOB_GROUP);
|
||||
existed.setInvokeTarget(invokeTarget);
|
||||
existed.setCronExpression(row.getCronExpression());
|
||||
existed.setMisfirePolicy("3");
|
||||
existed.setConcurrent("1");
|
||||
existed.setUpdateBy(getUsername());
|
||||
existed.setRemark(row.getMigrationName());
|
||||
jobService.updateJob(existed);
|
||||
existed.setStatus(ScheduleConstants.Status.NORMAL.getValue());
|
||||
jobService.changeStatus(existed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SysJob job = new SysJob();
|
||||
job.setJobName(jobName);
|
||||
job.setJobGroup(JOB_GROUP);
|
||||
job.setInvokeTarget(invokeTarget);
|
||||
job.setCronExpression(row.getCronExpression());
|
||||
job.setMisfirePolicy("3");
|
||||
job.setConcurrent("1");
|
||||
job.setCreateBy(getUsername());
|
||||
job.setRemark(row.getMigrationName());
|
||||
jobService.insertJob(job);
|
||||
job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
|
||||
jobService.changeStatus(job);
|
||||
|
||||
SysDataMigration patch = new SysDataMigration();
|
||||
patch.setMigrationId(row.getMigrationId());
|
||||
patch.setJobId(job.getJobId());
|
||||
patch.setUpdateBy(getUsername());
|
||||
migrationService.updateSysDataMigration(patch);
|
||||
row.setJobId(job.getJobId());
|
||||
}
|
||||
|
||||
private void removeJob(SysDataMigration row) throws SchedulerException
|
||||
{
|
||||
if (row == null || row.getJobId() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SysJob job = jobService.selectJobById(row.getJobId());
|
||||
if (job != null)
|
||||
{
|
||||
jobService.deleteJob(job);
|
||||
}
|
||||
SysDataMigration patch = new SysDataMigration();
|
||||
patch.setMigrationId(row.getMigrationId());
|
||||
patch.setJobId(null);
|
||||
// keep job_id clear: update XML only sets jobId when non-null; force via dedicated clear
|
||||
clearJobId(row.getMigrationId());
|
||||
}
|
||||
|
||||
private void clearJobId(Long migrationId)
|
||||
{
|
||||
migrationService.clearJobId(migrationId);
|
||||
}
|
||||
}
|
||||
@@ -42,14 +42,14 @@ bms:
|
||||
order-source-sync-threads: 16
|
||||
# Order-list local rebill workers (2~24). Pair with Druid maxActive >= threads + 8.
|
||||
local-rebill-threads: 16
|
||||
# Night auto-rebill: hours 0-8, pull 500 (all statuses) -> bill -> next 500 until empty / 09:00.
|
||||
# Empty page => no billing.
|
||||
# Auto-rebill: every hour all day, pull 500 (all statuses) -> bill -> next 500 until empty.
|
||||
# Empty page => no billing. Window [start-hour, end-hour).
|
||||
night-retry:
|
||||
enabled: true
|
||||
start-hour: 0
|
||||
end-hour: 9
|
||||
end-hour: 24
|
||||
batch-size: 500
|
||||
cron: "0 0/1 0-8 * * ?"
|
||||
cron: "0 0 * * * ?"
|
||||
oracle:
|
||||
# SLAVE datasource Oracle main-order table (schema.table allowed)
|
||||
order-table: WEISHI_USER.TAB_ORDER
|
||||
@@ -191,7 +191,7 @@ xss:
|
||||
# 过滤开关
|
||||
enabled: true
|
||||
# 排除链接(多个用逗号分隔)
|
||||
excludes: /system/notice
|
||||
excludes: /system/notice,/tool/migration
|
||||
# 匹配链接
|
||||
urlPatterns: /system/*,/monitor/*,/tool/*
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import com.bms.price.domain.PayPostcodeGroup;
|
||||
|
||||
@@ -18,6 +19,7 @@ class PayPostcodeImporterTest
|
||||
private final PayPostcodeImporter importer = new PayPostcodeImporter();
|
||||
|
||||
@Test
|
||||
@Disabled("依赖开发机 D:/应付供应商 下的价卡文件,未随项目提供")
|
||||
void stripsStarAndMergesSameFee() throws Exception
|
||||
{
|
||||
List<PayPostcodeGroup> groups = importer.parse(Files.readAllBytes(file("32、")), "32.xlsx");
|
||||
@@ -88,6 +90,7 @@ class PayPostcodeImporterTest
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("依赖开发机 D:/应付供应商 下的价卡文件,未随项目提供")
|
||||
void reachableUnreachableAndRemoteKeepTheirOwnCountry() throws Exception
|
||||
{
|
||||
List<PayPostcodeGroup> may = importer.parse(Files.readAllBytes(file("20、")), "20.xlsx");
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import com.bms.price.domain.PayOrderCostResult;
|
||||
import com.bms.price.domain.PayPreview;
|
||||
@@ -23,6 +24,7 @@ class PayWorkbookParserTest
|
||||
private final PayWorkbookParser parser = new PayWorkbookParser();
|
||||
|
||||
@Test
|
||||
@Disabled("依赖开发机 D:/应付供应商 下的价卡文件,未随项目提供")
|
||||
void octoberCatalogKeepsTwelveProductSheets() throws Exception
|
||||
{
|
||||
PayPreview preview = parse("9、");
|
||||
@@ -36,6 +38,7 @@ class PayWorkbookParserTest
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("依赖开发机 D:/应付供应商 下的价卡文件,未随项目提供")
|
||||
void mayCardReadsDhlAndDpdWithoutZoneSheets() throws Exception
|
||||
{
|
||||
PayPreview preview = parse("8、");
|
||||
@@ -88,6 +91,7 @@ class PayWorkbookParserTest
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("依赖开发机 D:/应付供应商 下的价卡文件,未随项目提供")
|
||||
void upsZonesStayLeftToRightAndColissimoFuelIsMarked() throws Exception
|
||||
{
|
||||
PayPreview preview = parse("32、");
|
||||
@@ -148,6 +152,7 @@ class PayWorkbookParserTest
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("依赖开发机 D:/应付供应商 下的价卡文件,未随项目提供")
|
||||
void imilePostcodesResolveZoneAndGermanCoverage() throws Exception
|
||||
{
|
||||
PayPreview preview = parse("32、");
|
||||
@@ -168,6 +173,7 @@ class PayWorkbookParserTest
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("依赖开发机 D:/应付供应商 下的价卡文件,未随项目提供")
|
||||
void gweFileOverridesOnlyPanEuroSupplier() throws Exception
|
||||
{
|
||||
PayPreview preview = parse("8.1");
|
||||
@@ -182,6 +188,7 @@ class PayWorkbookParserTest
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("依赖开发机 D:/应付供应商 下的价卡文件,未随项目提供")
|
||||
void wholeWorkbookPreviewSerializes() throws Exception
|
||||
{
|
||||
PayPreview preview = parse("9、");
|
||||
@@ -191,6 +198,7 @@ class PayWorkbookParserTest
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("依赖开发机 D:/应付供应商 下的价卡文件,未随项目提供")
|
||||
void yideCatalogIncludesPostcodeProducts() throws Exception
|
||||
{
|
||||
PayPreview preview = parse("26、");
|
||||
@@ -439,6 +447,7 @@ class PayWorkbookParserTest
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("依赖开发机 D:/payment 下的 CTT 48H.xlsx 价卡文件,未随项目提供")
|
||||
void ctt48hFileKeepsSurchargeTablesOutOfWeightBands() throws Exception
|
||||
{
|
||||
Path file = priceCard("CTT 48H.xlsx");
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.bms.receivable;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.bms.common.core.domain.entity.SysUser;
|
||||
import com.bms.common.core.domain.model.LoginUser;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
import com.bms.receivable.domain.ArBill;
|
||||
import com.bms.receivable.domain.ArBillLog;
|
||||
import com.bms.receivable.mapper.ArBillDetailMapper;
|
||||
import com.bms.receivable.mapper.ArBillExportMapper;
|
||||
import com.bms.receivable.mapper.ArBillMapper;
|
||||
import com.bms.receivable.security.ArBillScopeResolver;
|
||||
import com.bms.receivable.service.IArBillLogService;
|
||||
import com.bms.receivable.service.impl.ArBillServiceImpl;
|
||||
|
||||
class ArBillConfirmTest {
|
||||
private final ArBillMapper billMapper = mock(ArBillMapper.class);
|
||||
private final ArBillDetailMapper detailMapper = mock(ArBillDetailMapper.class);
|
||||
private final ArBillExportMapper exportMapper = mock(ArBillExportMapper.class);
|
||||
private final ArBillScopeResolver scope = mock(ArBillScopeResolver.class);
|
||||
private final IArBillLogService logService = mock(IArBillLogService.class);
|
||||
private final ArBillServiceImpl service = new ArBillServiceImpl();
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(service, "billMapper", billMapper);
|
||||
ReflectionTestUtils.setField(service, "detailMapper", detailMapper);
|
||||
ReflectionTestUtils.setField(service, "exportMapper", exportMapper);
|
||||
ReflectionTestUtils.setField(service, "billScope", scope);
|
||||
ReflectionTestUtils.setField(service, "logService", logService);
|
||||
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
|
||||
new LoginUser(7L, 1L, new SysUser(), Set.of()), null));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void clearSecurity() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
void readyDraftCanBeConfirmedWithoutReadingDetails() {
|
||||
ArBill draft = bill("DRAFT", "READY");
|
||||
draft.setTotalAmount(new BigDecimal("123.45"));
|
||||
ArBill confirmed = bill("CONFIRMED", "READY");
|
||||
when(billMapper.selectArBillByIdForUpdate(11L)).thenReturn(draft);
|
||||
when(billMapper.updateArBillStatus(11L, "CONFIRMED", null, "finance", "DRAFT")).thenReturn(1);
|
||||
when(billMapper.selectArBillByIdScoped(11L, null)).thenReturn(confirmed);
|
||||
when(exportMapper.selectByBillId(11L)).thenReturn(List.of());
|
||||
|
||||
assertSame(confirmed, service.confirmBill(11L, null, "finance", "request-1"));
|
||||
verify(scope).requireBillAccessible(11L);
|
||||
verifyNoInteractions(detailMapper);
|
||||
verify(logService).record(any(ArBillLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unfinishedOrNonDraftBillStillCannotBeConfirmed() {
|
||||
when(billMapper.selectArBillByIdForUpdate(11L)).thenReturn(
|
||||
bill("DRAFT", "GENERATING"), bill("CONFIRMED", "READY"));
|
||||
|
||||
ServiceException unfinished = assertThrows(ServiceException.class,
|
||||
() -> service.confirmBill(11L, null, "finance", "request-1"));
|
||||
assertEquals("账单尚未生成完成,暂不能确认", unfinished.getMessage());
|
||||
ServiceException notDraft = assertThrows(ServiceException.class,
|
||||
() -> service.confirmBill(11L, null, "finance", "request-2"));
|
||||
assertEquals("只有草稿账单可以确认,当前状态为 CONFIRMED", notDraft.getMessage());
|
||||
verify(billMapper, never()).updateArBillStatus(eq(11L), any(), nullable(String.class), any(), any());
|
||||
verifyNoInteractions(detailMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void inaccessibleBillStillCannotBeConfirmed() {
|
||||
when(billMapper.selectArBillByIdForUpdate(11L)).thenReturn(bill("DRAFT", "READY"));
|
||||
doThrow(new ServiceException("账单不存在或无权访问")).when(scope).requireBillAccessible(11L);
|
||||
|
||||
assertThrows(ServiceException.class, () -> service.confirmBill(11L, null, "finance", "request-1"));
|
||||
verify(billMapper, never()).updateArBillStatus(eq(11L), any(), nullable(String.class), any(), any());
|
||||
verifyNoInteractions(detailMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void concurrentStatusChangeStillPreventsConfirmation() {
|
||||
when(billMapper.selectArBillByIdForUpdate(11L)).thenReturn(bill("DRAFT", "READY"));
|
||||
|
||||
ServiceException error = assertThrows(ServiceException.class,
|
||||
() -> service.confirmBill(11L, null, "finance", "request-1"));
|
||||
assertEquals("账单状态已发生变化,请刷新后重试", error.getMessage());
|
||||
verify(logService, never()).record(any(ArBillLog.class));
|
||||
verifyNoInteractions(detailMapper);
|
||||
}
|
||||
|
||||
private static ArBill bill(String status, String generationStatus) {
|
||||
ArBill bill = new ArBill();
|
||||
bill.setArBillId(11L);
|
||||
bill.setStatus(status);
|
||||
bill.setGenerationStatus(generationStatus);
|
||||
return bill;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.bms.receivable;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.bms.common.config.BmsConfig;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.core.redis.RedisCache;
|
||||
import com.bms.price.domain.PrcOrderBill;
|
||||
import com.bms.receivable.domain.vo.ArBillGenerateQuery;
|
||||
import com.bms.receivable.security.ArBillScopeResolver;
|
||||
import com.bms.receivable.service.IArBillService;
|
||||
import com.bms.receivable.task.ArBillGeneratePreviewExportRunner;
|
||||
import com.bms.web.controller.receivable.ArBillController;
|
||||
|
||||
class ArBillGeneratePreviewTest {
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
@Test
|
||||
void previewDoesNotCalculateAmountUntilAmountEndpointIsCalled() {
|
||||
IArBillService service = mock(IArBillService.class);
|
||||
ArBillController controller = new ArBillController();
|
||||
ReflectionTestUtils.setField(controller, "billService", service);
|
||||
ArBillGenerateQuery query = new ArBillGenerateQuery();
|
||||
when(service.previewBill(query)).thenReturn(List.of(new PrcOrderBill()));
|
||||
when(service.sumPreviewBill(query)).thenReturn(new BigDecimal("12.34"));
|
||||
|
||||
try {
|
||||
AjaxResult preview = controller.preview(query);
|
||||
assertFalse(preview.containsKey("totalAmount"));
|
||||
verify(service, never()).sumPreviewBill(query);
|
||||
} finally {
|
||||
PageHelper.clearPage();
|
||||
}
|
||||
|
||||
assertEquals(new BigDecimal("12.34"), controller.previewAmount(query).get("data"));
|
||||
verify(service).sumPreviewBill(query);
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportEndsWithTheSumOfTheExportedAmounts() throws Exception {
|
||||
String previousProfile = BmsConfig.getProfile();
|
||||
try {
|
||||
new BmsConfig().setProfile(tempDir.toString());
|
||||
IArBillService service = mock(IArBillService.class);
|
||||
ArBillGenerateQuery query = new ArBillGenerateQuery();
|
||||
when(service.previewBill(same(query), isNull())).thenReturn(List.of(
|
||||
bill("A", "0.10"), bill("B", "0.20"), bill("C", "-0.05"), bill("D", null)));
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor() {
|
||||
@Override
|
||||
public void execute(Runnable task) {
|
||||
task.run();
|
||||
}
|
||||
};
|
||||
ArBillGeneratePreviewExportRunner runner = new ArBillGeneratePreviewExportRunner(
|
||||
service, mock(RedisCache.class), mock(ArBillScopeResolver.class), executor);
|
||||
for (String dateType : List.of("BIZ_DATE", "PICKUP_DATE")) {
|
||||
query.setDateType(dateType);
|
||||
String taskId = runner.submit(query, 1L);
|
||||
Path file = tempDir.resolve("download/arBillGeneratePreviewExport/" + taskId + ".xlsx");
|
||||
|
||||
try (InputStream in = Files.newInputStream(file); Workbook workbook = WorkbookFactory.create(in)) {
|
||||
Sheet sheet = workbook.getSheetAt(0);
|
||||
Row header = sheet.getRow(0);
|
||||
Row lastRow = sheet.getRow(sheet.getLastRowNum());
|
||||
assertEquals("合计", lastRow.getCell(column(header, "订单号")).getStringCellValue());
|
||||
assertEquals(0.25, lastRow.getCell(column(header, "应收金额")).getNumericCellValue(), 0.000001);
|
||||
assertEquals(5, sheet.getLastRowNum());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
new BmsConfig().setProfile(previousProfile);
|
||||
}
|
||||
}
|
||||
|
||||
private static PrcOrderBill bill(String orderNo, String amount) {
|
||||
PrcOrderBill bill = new PrcOrderBill();
|
||||
bill.setOrderNo(orderNo);
|
||||
bill.setIncomeTotal(amount == null ? null : new BigDecimal(amount));
|
||||
return bill;
|
||||
}
|
||||
|
||||
private static int column(Row header, String name) {
|
||||
for (int i = 0; i < header.getLastCellNum(); i++) {
|
||||
if (name.equals(header.getCell(i).getStringCellValue())) return i;
|
||||
}
|
||||
throw new AssertionError("缺少导出列: " + name);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.bms.report;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.math.BigDecimal;
|
||||
@@ -9,15 +13,130 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import com.bms.ar.domain.ArBalanceWriteoff;
|
||||
import com.bms.ar.domain.ArBill;
|
||||
import com.bms.ar.mapper.ArCommissionMapper;
|
||||
import com.bms.ar.profit.ArCommissionRate;
|
||||
import com.bms.ar.profit.ArCommissionResult;
|
||||
import com.bms.ar.profit.ArCommissionService;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import com.bms.common.core.domain.entity.SysUser;
|
||||
import com.bms.common.core.domain.model.LoginUser;
|
||||
import com.bms.receivable.domain.ArBalanceWriteoff;
|
||||
import com.bms.receivable.domain.ArBill;
|
||||
import com.bms.receivable.mapper.ArBillMapper;
|
||||
import com.bms.receivable.mapper.ArCommissionMapper;
|
||||
import com.bms.receivable.profit.ArCommissionRate;
|
||||
import com.bms.receivable.profit.ArCommissionResult;
|
||||
import com.bms.receivable.profit.ArCommissionService;
|
||||
import com.bms.receivable.profit.ArProfitScope;
|
||||
import com.bms.receivable.profit.ArProfitScopeResolver;
|
||||
import com.bms.receivable.profit.ArWriteoffRate;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
|
||||
class ArCommissionServiceTest
|
||||
{
|
||||
@Test
|
||||
void rateConfirmationRequiresCompleteProfitDataBeforeAnyWrite()
|
||||
{
|
||||
List<String> calls = new ArrayList<>();
|
||||
AtomicReference<Integer> complete = new AtomicReference<>(0);
|
||||
ArCommissionMapper mapper = (ArCommissionMapper) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] { ArCommissionMapper.class }, (proxy, method, args) -> {
|
||||
calls.add(method.getName());
|
||||
return switch (method.getName()) {
|
||||
case "selectBillIdByWriteoffId" -> 25L;
|
||||
case "isBillProfitDataComplete" -> complete.get();
|
||||
case "confirmMissingRate", "reverseResults" -> 1;
|
||||
case "countMissingRates" -> 0;
|
||||
case "selectSettlementInputs" -> List.of();
|
||||
default -> throw new AssertionError(method.getName());
|
||||
};
|
||||
});
|
||||
ArBill bill = new ArBill();
|
||||
bill.setProfitSalesUserId(7L);
|
||||
bill.setGenerationStatus("READY");
|
||||
ArBillMapper billMapper = (ArBillMapper) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] { ArBillMapper.class }, (proxy, method, args) -> {
|
||||
if ("selectArBillByIdForUpdate".equals(method.getName())) return bill;
|
||||
throw new AssertionError(method.getName());
|
||||
});
|
||||
ArProfitScopeResolver resolver = new ArProfitScopeResolver() {
|
||||
@Override public ArProfitScope resolve() { return new ArProfitScope(true, false, false, 1L, 1L); }
|
||||
};
|
||||
ArCommissionService service = new ArCommissionService(mapper, resolver, null, billMapper);
|
||||
ArWriteoffRate input = new ArWriteoffRate();
|
||||
input.setWriteoffId(18L);
|
||||
input.setRate(new BigDecimal("5"));
|
||||
input.setRemark("已核对");
|
||||
for (String status : List.of("CONFIRMED", "PARTIAL_WRITTEN_OFF", "WRITTEN_OFF"))
|
||||
{
|
||||
bill.setStatus(status);
|
||||
calls.clear();
|
||||
ServiceException error = assertThrows(ServiceException.class, () -> service.confirmMissingRate(input));
|
||||
assertTrue(error.getMessage().contains("请先处理后再确认比例"));
|
||||
assertFalse(calls.contains("confirmMissingRate"));
|
||||
assertFalse(calls.contains("reverseResults"));
|
||||
}
|
||||
complete.set(null);
|
||||
assertThrows(ServiceException.class, () -> service.confirmMissingRate(input));
|
||||
complete.set(1);
|
||||
SysUser user = new SysUser();
|
||||
user.setUserName("finance");
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken(new LoginUser(user, java.util.Set.of()), null));
|
||||
try
|
||||
{
|
||||
bill.setStatus("PARTIAL_WRITTEN_OFF");
|
||||
calls.clear();
|
||||
service.confirmMissingRate(input);
|
||||
assertTrue(calls.contains("confirmMissingRate"));
|
||||
assertFalse(calls.contains("reverseResults"));
|
||||
bill.setStatus("WRITTEN_OFF");
|
||||
calls.clear();
|
||||
service.confirmMissingRate(input);
|
||||
assertTrue(calls.indexOf("isBillProfitDataComplete") < calls.indexOf("confirmMissingRate"));
|
||||
assertTrue(calls.contains("reverseResults"));
|
||||
assertTrue(calls.contains("selectSettlementInputs"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void commissionReadsUseServerResolvedScopeAndManagementRemainsRestricted()
|
||||
{
|
||||
AtomicReference<ArProfitScope> actorScope = new AtomicReference<>(new ArProfitScope(false, true, false, 7L, 10L));
|
||||
AtomicReference<Object[]> selected = new AtomicReference<>();
|
||||
ArProfitScopeResolver resolver = new ArProfitScopeResolver() {
|
||||
@Override public ArProfitScope resolve() { return actorScope.get(); }
|
||||
};
|
||||
ArCommissionMapper mapper = (ArCommissionMapper) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] { ArCommissionMapper.class }, (proxy, method, args) -> {
|
||||
if ("selectResults".equals(method.getName()) || "selectCommissionSummary".equals(method.getName())) {
|
||||
selected.set(args.clone());
|
||||
return List.of();
|
||||
}
|
||||
throw new AssertionError(method.getName());
|
||||
});
|
||||
ArCommissionService service = new ArCommissionService(mapper, resolver, null, null);
|
||||
|
||||
service.results(999L, true);
|
||||
assertEquals(999L, selected.get()[0]);
|
||||
assertSame(actorScope.get(), selected.get()[2]);
|
||||
service.summary(999L);
|
||||
assertSame(actorScope.get(), selected.get()[1]);
|
||||
assertThrows(ServiceException.class, service::selectRates);
|
||||
assertThrows(ServiceException.class, service::missingRates);
|
||||
assertThrows(ServiceException.class, () -> service.exportResults(null));
|
||||
|
||||
actorScope.set(new ArProfitScope(false, false, true, 8L, 10L));
|
||||
service.results(null, false);
|
||||
assertSame(actorScope.get(), selected.get()[2]);
|
||||
assertThrows(ServiceException.class, service::selectRates);
|
||||
|
||||
actorScope.set(new ArProfitScope(true, false, false, 9L, 10L));
|
||||
service.exportResults(null);
|
||||
assertSame(actorScope.get(), selected.get()[2]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void snapshotsTheRateOnceAtWriteoffAndPreservesMissingRates()
|
||||
{
|
||||
|
||||
@@ -8,17 +8,89 @@ import java.util.Map;
|
||||
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import com.bms.ar.profit.ArProfitQuery;
|
||||
import com.bms.ar.profit.ArProfitScope;
|
||||
import com.bms.ar.profit.ArCommissionResult;
|
||||
import com.bms.receivable.profit.ArProfitQuery;
|
||||
import com.bms.receivable.profit.ArProfitScope;
|
||||
import com.bms.receivable.profit.ArCommissionResult;
|
||||
|
||||
class ArProfitMapperSqlTest
|
||||
{
|
||||
@Test
|
||||
void snapshotCalculationIsScopedToTheGeneratingBill() throws Exception
|
||||
{
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.getTypeAliasRegistry().registerAlias("ArBill", com.bms.receivable.domain.ArBill.class);
|
||||
for (String resource : new String[] {"mapper/receivable/ArBillScopeMapper.xml", "mapper/receivable/ArBillMapper.xml"})
|
||||
{
|
||||
try (InputStream input = getClass().getClassLoader().getResourceAsStream(resource))
|
||||
{
|
||||
new XMLMapperBuilder(input, configuration, resource, configuration.getSqlFragments()).parse();
|
||||
}
|
||||
}
|
||||
var bound = configuration.getMappedStatement("com.bms.receivable.mapper.ArBillMapper.updateProfitSnapshot")
|
||||
.getBoundSql(Map.of("arBillId", 42L, "shenzhenArea", "C", "guangzhouArea", "GZ"));
|
||||
String sql = bound.getSql();
|
||||
assertTrue(sql.contains("fd.ar_bill_id = ?"));
|
||||
assertTrue(sql.contains("d.ar_bill_id = ?"));
|
||||
assertTrue(sql.contains("b.ar_bill_id = ? and b.generation_status = 'GENERATING'"));
|
||||
assertTrue(sql.contains("f.sales_amount = d.total_amount"));
|
||||
assertTrue(sql.contains("a.detail_total != b.total_amount"));
|
||||
assertTrue(sql.contains("'MISSING_PRICE'"));
|
||||
assertTrue(sql.contains("'CURRENCY_MISMATCH'"));
|
||||
assertTrue(sql.contains("'PRICE_OVERFLOW'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void commissionInReportSummaryUsesBillFiltersAndDataScope() throws Exception
|
||||
{
|
||||
Configuration configuration = new Configuration();
|
||||
String resource = "mapper/receivable/ArProfitMapper.xml";
|
||||
try (InputStream input = getClass().getClassLoader().getResourceAsStream(resource))
|
||||
{
|
||||
new XMLMapperBuilder(input, configuration, resource, configuration.getSqlFragments()).parse();
|
||||
}
|
||||
var statement = configuration.getMappedStatement("com.bms.receivable.mapper.ArProfitMapper.selectSummary");
|
||||
ArProfitQuery query = new ArProfitQuery();
|
||||
query.setCustomerId(5L);
|
||||
query.setBillNo("AR123");
|
||||
query.setStatus("WRITTEN_OFF");
|
||||
query.setSalesUserId(8L);
|
||||
query.setBeginBillDate(new java.util.Date(0));
|
||||
query.setEndBillDate(new java.util.Date());
|
||||
String sql = statement.getBoundSql(Map.of("query", query,
|
||||
"scope", new ArProfitScope(false, true, false, 8L, 10L))).getSql();
|
||||
int commissionStart = sql.indexOf("from ar_bill b join ar_profit_commission_result r");
|
||||
assertTrue(commissionStart >= 0);
|
||||
String commission = sql.substring(commissionStart);
|
||||
assertTrue(commission.contains("r.result_status = 'READY'"));
|
||||
assertTrue(commission.contains("r.currency = b.currency"));
|
||||
assertTrue(commission.contains("b.status = 'WRITTEN_OFF'"));
|
||||
assertTrue(commission.contains("b.profit_sales_user_id = ?"));
|
||||
assertTrue(commission.contains("b.customer_id = ?"));
|
||||
assertTrue(commission.contains("b.bill_no like"));
|
||||
assertTrue(commission.contains("b.status = ?"));
|
||||
assertTrue(commission.contains("b.bill_date >= ?"));
|
||||
assertTrue(commission.contains("b.bill_date <= ?"));
|
||||
assertTrue(commission.contains("group by b.currency"));
|
||||
|
||||
for (ArProfitScope scope : new ArProfitScope[] {
|
||||
new ArProfitScope(false, false, true, 8L, 10L),
|
||||
new ArProfitScope(false, false, false, 8L, 10L),
|
||||
new ArProfitScope(true, false, false, 8L, 10L) })
|
||||
{
|
||||
String scopedSql = statement.getBoundSql(Map.of("query", new ArProfitQuery(), "scope", scope)).getSql();
|
||||
String scopedCommission = scopedSql.substring(scopedSql.indexOf("from ar_bill b join ar_profit_commission_result r"));
|
||||
if (scope.department()) assertTrue(scopedCommission.contains("find_in_set"));
|
||||
else if (scope.all()) assertFalse(scopedCommission.contains("b.profit_sales_user_id = ?"));
|
||||
else assertTrue(scopedCommission.contains("and 1 = 0"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void parsesReportAndCommissionMappersAndScopesEachReportQuery() throws Exception
|
||||
{
|
||||
Configuration configuration = new Configuration();
|
||||
for (String resource : new String[] { "mapper/ar/ArProfitMapper.xml", "mapper/ar/ArCommissionMapper.xml" })
|
||||
// 提成 Mapper 先加载时也必须能解析引用的报表权限片段。
|
||||
for (String resource : new String[] {"mapper/receivable/ArCommissionMapper.xml", "mapper/receivable/ArProfitMapper.xml"})
|
||||
{
|
||||
try (InputStream input = getClass().getClassLoader().getResourceAsStream(resource))
|
||||
{
|
||||
@@ -27,7 +99,7 @@ class ArProfitMapperSqlTest
|
||||
}
|
||||
for (String id : new String[] { "selectBills", "selectSummary", "selectBillDetails", "selectFees" })
|
||||
{
|
||||
String name = "com.bms.ar.mapper.ArProfitMapper." + id;
|
||||
String name = "com.bms.receivable.mapper.ArProfitMapper." + id;
|
||||
String self = configuration.getMappedStatement(name).getBoundSql(Map.of(
|
||||
"query", new ArProfitQuery(), "scope", new ArProfitScope(false, true, false, 8L, 10L))).getSql();
|
||||
String manager = configuration.getMappedStatement(name).getBoundSql(Map.of(
|
||||
@@ -37,18 +109,44 @@ class ArProfitMapperSqlTest
|
||||
assertTrue(manager.contains("find_in_set"));
|
||||
assertTrue(manager.contains("b.status in"));
|
||||
}
|
||||
ArProfitQuery orderFilter = new ArProfitQuery();
|
||||
orderFilter.setOrderNo("ORDER-1");
|
||||
String bills = configuration.getMappedStatement("com.bms.ar.mapper.ArProfitMapper.selectBills")
|
||||
.getBoundSql(Map.of("query", orderFilter, "scope", new ArProfitScope(true, false, false, 8L, 10L))).getSql();
|
||||
String bills = configuration.getMappedStatement("com.bms.receivable.mapper.ArProfitMapper.selectBills")
|
||||
.getBoundSql(Map.of("query", new ArProfitQuery(), "scope", new ArProfitScope(true, false, false, 8L, 10L))).getSql();
|
||||
assertTrue(bills.contains("from ar_bill b"));
|
||||
assertTrue(bills.contains("exists (select 1 from ar_bill_detail matched"));
|
||||
assertFalse(bills.contains("and d.order_no like"));
|
||||
String billCommission = configuration.getMappedStatement("com.bms.ar.mapper.ArCommissionMapper.selectCommissionSummary")
|
||||
.getBoundSql(Map.of("billId", 9L)).getSql();
|
||||
assertTrue(bills.contains("b.bill_name"));
|
||||
assertTrue(bills.contains("b.profit_calc_status = 'READY'"));
|
||||
assertTrue(bills.contains("b.product_amount"));
|
||||
assertTrue(bills.contains("b.redline_amount"));
|
||||
assertFalse(bills.contains("ar_bill_detail"));
|
||||
assertFalse(bills.contains("order_no"));
|
||||
String summary = configuration.getMappedStatement("com.bms.receivable.mapper.ArProfitMapper.selectSummary")
|
||||
.getBoundSql(Map.of("query", new ArProfitQuery(), "scope", new ArProfitScope(true, false, false, 8L, 10L))).getSql();
|
||||
assertFalse(summary.contains("ar_bill_detail"));
|
||||
assertTrue(summary.contains("ar_profit_commission_result"));
|
||||
String billCommission = configuration.getMappedStatement("com.bms.receivable.mapper.ArCommissionMapper.selectCommissionSummary")
|
||||
.getBoundSql(Map.of("billId", 9L, "scope", new ArProfitScope(true, false, false, 8L, 10L))).getSql();
|
||||
assertTrue(billCommission.contains("r.ar_bill_id = ?"));
|
||||
assertTrue(billCommission.contains("then null"));
|
||||
var salesSql = configuration.getMappedStatement("com.bms.ar.mapper.ArProfitMapper.selectSalesOptions")
|
||||
for (String id : new String[] { "selectResults", "selectCommissionSummary" })
|
||||
{
|
||||
var statement = configuration.getMappedStatement("com.bms.receivable.mapper.ArCommissionMapper." + id);
|
||||
String self = statement.getBoundSql(Map.of("billId", 9L, "includeReversed", true,
|
||||
"scope", new ArProfitScope(false, true, false, 8L, 10L))).getSql();
|
||||
String department = statement.getBoundSql(Map.of("includeReversed", false,
|
||||
"scope", new ArProfitScope(false, false, true, 8L, 10L))).getSql();
|
||||
String denied = statement.getBoundSql(Map.of("includeReversed", false,
|
||||
"scope", new ArProfitScope(false, false, false, 8L, 10L))).getSql();
|
||||
String all = statement.getBoundSql(Map.of("includeReversed", false,
|
||||
"scope", new ArProfitScope(true, false, false, 8L, 10L))).getSql();
|
||||
assertTrue(self.contains("b.profit_sales_user_id = ?"));
|
||||
assertTrue(self.contains("r.ar_bill_id = ?"));
|
||||
assertFalse(self.contains("find_in_set"));
|
||||
assertTrue(department.contains("u.user_id = b.profit_sales_user_id"));
|
||||
assertTrue(department.contains("find_in_set"));
|
||||
assertTrue(denied.contains("and 1 = 0"));
|
||||
assertFalse(all.contains("b.profit_sales_user_id = ?"));
|
||||
assertFalse(all.contains("find_in_set"));
|
||||
}
|
||||
var salesSql = configuration.getMappedStatement("com.bms.receivable.mapper.ArProfitMapper.selectSalesOptions")
|
||||
.getBoundSql(Map.of("keyword", "sales"));
|
||||
assertTrue(salesSql.getSql().contains("from sys_user u"));
|
||||
assertTrue(salesSql.getSql().contains("u.nick_name like"));
|
||||
@@ -56,11 +154,22 @@ class ArProfitMapperSqlTest
|
||||
assertFalse(salesSql.getSql().contains("ar_bill"));
|
||||
assertFalse(salesSql.getSql().contains("sys_role"));
|
||||
assertTrue(salesSql.getSql().contains("limit 50"));
|
||||
String settlement = configuration.getMappedStatement("com.bms.ar.mapper.ArCommissionMapper.selectSettlementInputs")
|
||||
String settlement = configuration.getMappedStatement("com.bms.receivable.mapper.ArCommissionMapper.selectSettlementInputs")
|
||||
.getBoundSql(Map.of("billId", 1L)).getSql();
|
||||
assertTrue(settlement.contains("w.status = 'EFFECTIVE'"));
|
||||
assertTrue(settlement.contains("b.status = 'WRITTEN_OFF'"));
|
||||
var insert = configuration.getMappedStatement("com.bms.ar.mapper.ArCommissionMapper.insertResults")
|
||||
String validation = configuration.getMappedStatement("com.bms.receivable.mapper.ArCommissionMapper.isBillProfitDataComplete")
|
||||
.getBoundSql(Map.of("billId", 1L)).getSql();
|
||||
assertTrue(validation.contains("detail.ar_bill_id = ?"));
|
||||
assertTrue(validation.contains("d.ar_bill_id = ?"));
|
||||
assertTrue(validation.contains("a.detail_total = b.total_amount"));
|
||||
assertTrue(validation.contains("f.sales_amount = d.total_amount"));
|
||||
assertTrue(validation.contains("f.fee_currency = d.currency"));
|
||||
assertTrue(validation.contains("f.product_complete = 1"));
|
||||
assertTrue(validation.contains("f.redline_complete = 1"));
|
||||
assertFalse(validation.contains("b.status = 'WRITTEN_OFF'"));
|
||||
assertFalse(validation.contains("profit_calc_status"));
|
||||
var insert = configuration.getMappedStatement("com.bms.receivable.mapper.ArCommissionMapper.insertResults")
|
||||
.getBoundSql(Map.of("items", java.util.List.of(new ArCommissionResult(), new ArCommissionResult()), "operator", "test"));
|
||||
assertTrue(insert.getSql().contains("ar_profit_commission_result"));
|
||||
assertTrue(insert.getParameterMappings().size() == 16);
|
||||
|
||||
@@ -4,10 +4,9 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import com.bms.ar.profit.ArProfitScopeResolver;
|
||||
import com.bms.receivable.profit.ArProfitScopeResolver;
|
||||
import com.bms.common.core.domain.entity.SysRole;
|
||||
import com.bms.common.core.domain.entity.SysUser;
|
||||
import com.bms.common.core.domain.model.LoginUser;
|
||||
|
||||
@@ -6,9 +6,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import com.bms.ar.profit.CommissionCalculator;
|
||||
import com.bms.ar.profit.ArCommissionResult;
|
||||
import com.bms.ar.profit.CommissionCalculator.Writeoff;
|
||||
import com.bms.receivable.profit.CommissionCalculator;
|
||||
import com.bms.receivable.profit.ArCommissionResult;
|
||||
import com.bms.receivable.profit.CommissionCalculator.Writeoff;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class CommissionCalculatorTest
|
||||
|
||||
@@ -164,10 +164,11 @@ public class Constants
|
||||
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
|
||||
* com.bms.customer 覆盖客户/供应商资料同步、客户授信到期等业务任务包;
|
||||
* com.bms.masterdata.task 为渠道产品主数据同步任务;com.bms.notify.task 为客户超额通知;
|
||||
* com.bms.price.task 为价格/产品状态刷新任务
|
||||
* com.bms.price.task 为价格/产品状态刷新任务;com.bms.system.migration 为数据迁移任务
|
||||
*/
|
||||
public static final String[] JOB_WHITELIST_STR = { "com.bms.quartz.task", "com.bms.customer",
|
||||
"com.bms.masterdata.task", "com.bms.notify.task", "com.bms.price.task" };
|
||||
"com.bms.masterdata.task", "com.bms.notify.task", "com.bms.price.task",
|
||||
"com.bms.system.migration" };
|
||||
|
||||
/**
|
||||
* 定时任务违规的字符
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
package com.bms.common.enums;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
|
||||
/**
|
||||
* 应付报错。只收没有拼接变量的固定提示。
|
||||
* code 以 BMS_PAY 开头,name 是这条提示用在哪一步,cnMes 是弹出的中文,enMes 先留空。
|
||||
*/
|
||||
public enum PaymentMesEnum
|
||||
{
|
||||
BMS_PAY_PICK_ORDER("BMS_PAY_PICK_ORDER", "生成应付校验订单", "请选择订单"),
|
||||
BMS_PAY_ORDER_MISS("BMS_PAY_ORDER_MISS", "生成应付校验订单", "订单不存在"),
|
||||
BMS_PAY_NO_COST("BMS_PAY_NO_COST", "生成应付计算结果", "没有算出应付费用"),
|
||||
BMS_PAY_NO_ZONE("BMS_PAY_NO_ZONE", "生成应付匹配邮编", "邮编没有对上价卡地区"),
|
||||
BMS_PAY_NO_CHANNEL("BMS_PAY_NO_CHANNEL", "生成应付匹配渠道", "订单没有渠道服务商"),
|
||||
BMS_PAY_CH_SYNC("BMS_PAY_CH_SYNC", "生成应付匹配渠道", "订单没有渠道服务商,或渠道尚未同步"),
|
||||
BMS_PAY_NO_PRODUCT("BMS_PAY_NO_PRODUCT", "应付计费匹配产品", "没有应付产品"),
|
||||
BMS_PAY_NO_WEIGHT("BMS_PAY_NO_WEIGHT", "订单判断重量", "订单没有计费重"),
|
||||
|
||||
BMS_PAY_PICK_PROD("BMS_PAY_PICK_PROD", "应付产品保存", "请先勾选要保存的产品"),
|
||||
BMS_PAY_PROD_LIMIT("BMS_PAY_PROD_LIMIT", "应付产品保存", "一次最多保存80个产品"),
|
||||
BMS_PAY_PROD_FIELD("BMS_PAY_PROD_FIELD", "应付产品保存", "产品名称、工作表和文件摘要不能为空"),
|
||||
BMS_PAY_PICK_EDIT("BMS_PAY_PICK_EDIT", "应付产品修改", "请选择要修改的产品"),
|
||||
BMS_PAY_PROD_MISS("BMS_PAY_PROD_MISS", "应付产品修改", "产品不存在或已删除"),
|
||||
BMS_PAY_TPL_ONLY("BMS_PAY_TPL_ONLY", "应付产品修改", "渠道价格请在渠道页面保存,这里只改公共模板"),
|
||||
BMS_PAY_SAVE_CH("BMS_PAY_SAVE_CH", "渠道应付价格", "请先保存渠道,再维护价格"),
|
||||
BMS_PAY_NO_PRICE("BMS_PAY_NO_PRICE", "渠道应付价格", "没有可保存的价格"),
|
||||
BMS_PAY_REIMPORT("BMS_PAY_REIMPORT", "应付价卡导入", "重新导入时请使用单个产品文件,或先选定渠道上的应付产品"),
|
||||
BMS_PAY_PICK_IMP("BMS_PAY_PICK_IMP", "应付价卡导入", "请先选择一个已导入的应付产品"),
|
||||
BMS_PAY_DICT_EMPTY("BMS_PAY_DICT_EMPTY", "应付产品字典", "应付产品字典还没有数据"),
|
||||
BMS_PAY_TERM_START("BMS_PAY_TERM_START", "应付有效期", "请填写生效开始"),
|
||||
BMS_PAY_TERM_END("BMS_PAY_TERM_END", "应付有效期", "生效结束不能早于生效开始"),
|
||||
BMS_PAY_PICK_VER("BMS_PAY_PICK_VER", "应付有效期", "请选择要修改的版本"),
|
||||
BMS_PAY_TERM_MISS("BMS_PAY_TERM_MISS", "应付有效期", "有效期不存在"),
|
||||
BMS_PAY_TERM_STAT("BMS_PAY_TERM_STAT", "应付有效期", "状态只能是待生效、生效或已失效"),
|
||||
BMS_PAY_FILE_DOWN("BMS_PAY_FILE_DOWN", "应付价卡源文件", "源文件下载失败"),
|
||||
BMS_PAY_NO_SOURCE("BMS_PAY_NO_SOURCE", "应付价卡源文件", "这一版没有可下载的源文件"),
|
||||
BMS_PAY_FILE_UP("BMS_PAY_FILE_UP", "应付价卡源文件", "价卡文件上传失败"),
|
||||
BMS_PAY_FILE_WAIT("BMS_PAY_FILE_WAIT", "应付价卡源文件", "源文件还没有上传成功,请重新选择价卡后再保存"),
|
||||
BMS_PAY_CH_FIRST("BMS_PAY_CH_FIRST", "渠道绑定产品", "请先保存渠道,再选择产品"),
|
||||
BMS_PAY_CH_MISS("BMS_PAY_CH_MISS", "渠道绑定产品", "渠道不存在或渠道编码为空"),
|
||||
BMS_PAY_PICK_CH("BMS_PAY_PICK_CH", "渠道绑定产品", "请先勾选渠道"),
|
||||
BMS_PAY_PICK_SIDE("BMS_PAY_PICK_SIDE", "渠道绑定产品", "请至少选择应付或应收产品"),
|
||||
BMS_PAY_BIND_NONE("BMS_PAY_BIND_NONE", "渠道绑定产品", "没有可绑定的渠道"),
|
||||
BMS_PAY_CH_PROD("BMS_PAY_CH_PROD", "渠道应付产品", "渠道还没有选择应付产品"),
|
||||
BMS_PAY_OUT_TERM("BMS_PAY_OUT_TERM", "应付价格有效期", "订单时间没有落在价格有效期内"),
|
||||
BMS_PAY_TWO_VER("BMS_PAY_TWO_VER", "应付价格对比", "请选择两个价格版本"),
|
||||
BMS_PAY_AR_AP("BMS_PAY_AR_AP", "收付类型", "收付类型只能是应收AR或应付AP"),
|
||||
BMS_PAY_AP_MISS("BMS_PAY_AP_MISS", "应付产品查询", "应付产品不存在或已删除"),
|
||||
|
||||
BMS_PAY_PICK_FILE("BMS_PAY_PICK_FILE", "应付价卡文件", "请选择价卡文件"),
|
||||
BMS_PAY_XLSX("BMS_PAY_XLSX", "应付价卡文件", "请上传xlsx价卡"),
|
||||
BMS_PAY_FILE_30M("BMS_PAY_FILE_30M", "应付价卡文件", "价卡文件不能超过30MB"),
|
||||
BMS_PAY_NO_SHEET("BMS_PAY_NO_SHEET", "应付价卡解析", "文件里没有工作表"),
|
||||
BMS_PAY_NO_QUOTE("BMS_PAY_NO_QUOTE", "应付价卡解析", "目录里没有找到产品报价表"),
|
||||
BMS_PAY_BAD_XLS("BMS_PAY_BAD_XLS", "应付价卡解析", "价卡无法读取,请确认是 Excel 报价文件"),
|
||||
|
||||
BMS_PAY_PICK_XLS("BMS_PAY_PICK_XLS", "应付邮编导入", "请选择 Excel 文件"),
|
||||
BMS_PAY_XLS_30M("BMS_PAY_XLS_30M", "应付邮编导入", "文件不能超过30MB"),
|
||||
BMS_PAY_ZIP_READ("BMS_PAY_ZIP_READ", "应付邮编导入", "邮编表无法读取,请确认是 Excel 文件"),
|
||||
BMS_PAY_XLSX_ONLY("BMS_PAY_XLSX_ONLY", "应付邮编导入", "只支持 xlsx 文件"),
|
||||
BMS_PAY_ZIP_EDIT("BMS_PAY_ZIP_EDIT", "应付邮编配置", "请选择要修改的邮编配置"),
|
||||
BMS_PAY_ZIP_MISS("BMS_PAY_ZIP_MISS", "应付邮编配置", "邮编配置不存在或已删除"),
|
||||
BMS_PAY_ZIP_TYPE("BMS_PAY_ZIP_TYPE", "应付邮编配置", "类型只能是附加费或邮编分区"),
|
||||
BMS_PAY_ZIP_HEAD("BMS_PAY_ZIP_HEAD", "应付邮编导入", "没有识别到邮编或附加费邮编,请确认表头里有邮编、ZIP 或附加费"),
|
||||
BMS_PAY_ZIP_ATTR("BMS_PAY_ZIP_ATTR", "应付邮编配置", "邮编属性只能是可达、不可达或偏远"),
|
||||
|
||||
BMS_PAY_BILL_MISS("BMS_PAY_BILL_MISS", "应付账单查询", "应付账单不存在"),
|
||||
BMS_PAY_PICK_SUP("BMS_PAY_PICK_SUP", "应付账单生成", "请选择供应商"),
|
||||
BMS_PAY_SUP_MISS("BMS_PAY_SUP_MISS", "应付账单生成", "供应商不存在"),
|
||||
BMS_PAY_NO_GEN("BMS_PAY_NO_GEN", "应付账单生成", "没有可生成的订单"),
|
||||
BMS_PAY_BILL_FEE("BMS_PAY_BILL_FEE", "应付账单生成", "订单没有应付费用"),
|
||||
BMS_PAY_CURRENCY("BMS_PAY_CURRENCY", "应付账单生成", "所选订单币种不一致,请按币种分开生成"),
|
||||
BMS_PAY_DRAFT("BMS_PAY_DRAFT", "应付账单确认", "只有草稿可以确认"),
|
||||
BMS_PAY_CONFIRM("BMS_PAY_CONFIRM", "应付账单付款", "请先确认账单,再登记付款"),
|
||||
BMS_PAY_PICK_BILL("BMS_PAY_PICK_BILL", "应付账单作废", "请选择应付账单"),
|
||||
BMS_PAY_VOID_WHY("BMS_PAY_VOID_WHY", "应付账单作废", "请填写作废原因"),
|
||||
BMS_PAY_VOIDED("BMS_PAY_VOIDED", "应付账单作废", "账单已经作废"),
|
||||
BMS_PAY_PAID("BMS_PAY_PAID", "应付账单作废", "已付款的账单不能直接作废,请先冲回付款"),
|
||||
BMS_PAY_ORDER_NO("BMS_PAY_ORDER_NO", "应付账单查询", "请填写订单号"),
|
||||
BMS_PAY_BIZ_DATE("BMS_PAY_BIZ_DATE", "应付账单查询", "请选择计费日起止"),
|
||||
BMS_PAY_LADING_NO("BMS_PAY_LADING_NO", "应付账单查询", "请填写提单号"),
|
||||
BMS_PAY_QUERY("BMS_PAY_QUERY", "应付账单查询", "请选择查询方式"),
|
||||
|
||||
BMS_PAY_NO_ALLOC("BMS_PAY_NO_ALLOC", "提单费用分摊", "所选提单没有需要分摊的费用"),
|
||||
BMS_PAY_ALLOC_ST("BMS_PAY_ALLOC_ST", "提单费用分摊", "费用状态已变化,请重新打开后再分摊"),
|
||||
BMS_PAY_PICK_LAD("BMS_PAY_PICK_LAD", "提单费用分摊", "请勾选提单"),
|
||||
BMS_PAY_LAD_LIMIT("BMS_PAY_LAD_LIMIT", "提单费用分摊", "单次最多处理5条提单"),
|
||||
BMS_PAY_NO_CHANGE("BMS_PAY_NO_CHANGE", "提单费用保存", "没有需要保存的变更"),
|
||||
BMS_PAY_IMP_EMPTY("BMS_PAY_IMP_EMPTY", "提单费用导入", "导入文件没有费用数据"),
|
||||
BMS_PAY_IMP_DEMO("BMS_PAY_IMP_DEMO", "提单费用导入", "请删除示例行后再导入有效费用"),
|
||||
BMS_PAY_FEE_BODY("BMS_PAY_FEE_BODY", "提单费用保存", "请提交要保存的费用"),
|
||||
BMS_PAY_DEL_ID("BMS_PAY_DEL_ID", "提单费用删除", "要删除的费用缺少编号"),
|
||||
BMS_PAY_FEE_DUP("BMS_PAY_FEE_DUP", "提单费用保存", "同一条费用不能重复提交"),
|
||||
BMS_PAY_FEE_GONE("BMS_PAY_FEE_GONE", "提单费用保存", "费用已删除或不属于该提单"),
|
||||
BMS_PAY_FEE_LOCK("BMS_PAY_FEE_LOCK", "提单费用删除", "已分摊或已确定的费用不能删除,请使用作废"),
|
||||
BMS_PAY_EDIT_ID("BMS_PAY_EDIT_ID", "提单费用修改", "要修改的费用缺少编号"),
|
||||
BMS_PAY_FEE_EDIT("BMS_PAY_FEE_EDIT", "提单费用修改", "已分摊或已确定的费用不能修改"),
|
||||
BMS_PAY_PICK_OK("BMS_PAY_PICK_OK", "提单费用确定", "请选择要确定的费用"),
|
||||
BMS_PAY_OK_STAT("BMS_PAY_OK_STAT", "提单费用确定", "只有已分摊的费用可以确定"),
|
||||
BMS_PAY_OK_CHG("BMS_PAY_OK_CHG", "提单费用确定", "费用状态已变化,请刷新后再确定"),
|
||||
BMS_PAY_PICK_VOID("BMS_PAY_PICK_VOID", "提单费用作废", "请选择要作废的费用"),
|
||||
BMS_PAY_VOID_STAT("BMS_PAY_VOID_STAT", "提单费用作废", "只有已分摊或已确定的费用可以作废"),
|
||||
BMS_PAY_VOID_CHG("BMS_PAY_VOID_CHG", "提单费用作废", "费用状态已变化,请刷新后再作废"),
|
||||
BMS_PAY_NO_PERM("BMS_PAY_NO_PERM", "提单费用权限", "没有权限修改该费用"),
|
||||
BMS_PAY_FEE_ENTRY("BMS_PAY_FEE_ENTRY", "提单费用保存", "费用入口不正确"),
|
||||
BMS_PAY_FORBIDDEN("BMS_PAY_FORBIDDEN", "提单费用权限", "没有权限"),
|
||||
BMS_PAY_PICK_ITEM("BMS_PAY_PICK_ITEM", "提单费用校验", "请选择费用项"),
|
||||
BMS_PAY_ITEM_MISS("BMS_PAY_ITEM_MISS", "提单费用校验", "费用项不存在"),
|
||||
BMS_PAY_LEVEL("BMS_PAY_LEVEL", "提单费用校验", "费用阶段不在字典中"),
|
||||
BMS_PAY_PRICE("BMS_PAY_PRICE", "提单费用校验价格", "请填写价格"),
|
||||
BMS_PAY_PRICE_NEG("BMS_PAY_PRICE_NEG", "提单费用校验价格", "价格不能小于0"),
|
||||
BMS_PAY_PRICE_LEN("BMS_PAY_PRICE_LEN", "提单费用校验价格", "价格整数部分不能超过16位"),
|
||||
BMS_PAY_CUR_PICK("BMS_PAY_CUR_PICK", "提单费用校验币种", "请选择收费币种"),
|
||||
BMS_PAY_CUR_DICT("BMS_PAY_CUR_DICT", "提单费用校验币种", "收费币种不在币种字典中"),
|
||||
BMS_PAY_REMARK("BMS_PAY_REMARK", "提单费用校验备注", "备注不能超过500字"),
|
||||
BMS_PAY_LAD_BLANK("BMS_PAY_LAD_BLANK", "提单费用校验提单", "提单号不能为空"),
|
||||
BMS_PAY_LAD_MISS("BMS_PAY_LAD_MISS", "提单费用校验提单", "提单不存在");
|
||||
|
||||
private static final Map<String, PaymentMesEnum> BY_CODE = new HashMap<>();
|
||||
|
||||
static
|
||||
{
|
||||
for (PaymentMesEnum item : values())
|
||||
{
|
||||
BY_CODE.put(item.code, item);
|
||||
}
|
||||
}
|
||||
|
||||
private final String code;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String cnMes;
|
||||
|
||||
private final String enMes;
|
||||
|
||||
PaymentMesEnum(String code, String name, String cnMes)
|
||||
{
|
||||
this(code, name, cnMes, "");
|
||||
}
|
||||
|
||||
PaymentMesEnum(String code, String name, String cnMes, String enMes)
|
||||
{
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.cnMes = cnMes;
|
||||
this.enMes = enMes == null ? "" : enMes;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getCnMes()
|
||||
{
|
||||
return cnMes;
|
||||
}
|
||||
|
||||
public String getEnMes()
|
||||
{
|
||||
return enMes;
|
||||
}
|
||||
|
||||
/** 按编码取出提示。没有对应编码时返回 null。 */
|
||||
public static PaymentMesEnum of(String code)
|
||||
{
|
||||
if (code == null || code.isBlank())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return BY_CODE.get(code.trim());
|
||||
}
|
||||
|
||||
/** 弹出给用户的中文提示。英文未配置时仍返回中文。 */
|
||||
public String text()
|
||||
{
|
||||
return cnMes == null || cnMes.isBlank() ? enMes : cnMes;
|
||||
}
|
||||
|
||||
/** 转成业务异常,页面只看到中文提示。 */
|
||||
public ServiceException exception()
|
||||
{
|
||||
return new ServiceException(text()).setDetailMessage(code);
|
||||
}
|
||||
}
|
||||
@@ -169,9 +169,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize destination postcode for zone match / persist.
|
||||
* Removes all Unicode whitespace (incl. NBSP / U+2005) and ASCII spaces;
|
||||
* strips Excel-style trailing ".0" on pure digits.
|
||||
* 规范化目的地邮编,用于分区匹配/持久化。
|
||||
* 移除所有 Unicode 空白字符(包括 NBSP / U+2005)和 ASCII 空格;
|
||||
* 去除纯数字末尾 Excel 风格的 ".0"。
|
||||
*/
|
||||
public static String normalizePostcode(String postcode)
|
||||
{
|
||||
@@ -681,12 +681,12 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares given {@code string} to a CharSequences vararg of {@code searchStrings},
|
||||
* returning {@code true} if the {@code string} is equal to any of the {@code searchStrings}, ignoring case.
|
||||
* 将给定的 {@code string} 与 {@code searchStrings} 可变参数中的字符序列进行比较,
|
||||
* 如果 {@code string} 与其中任意一个 {@code searchStrings} 相等(忽略大小写),则返回 {@code true}。
|
||||
*
|
||||
* @param string to compare, may be {@code null}.
|
||||
* @param searchStrings a vararg of strings, may be {@code null}.
|
||||
* @return {@code true} if the string is equal (case-insensitive) to any other element of {@code searchStrings};
|
||||
* @param string 要比较的字符串,可能为 {@code null}。
|
||||
* @param searchStrings 字符串可变参数,可能为 {@code null}。
|
||||
* @return 如果字符串与 {@code searchStrings} 中的任意其他元素相等(不区分大小写),则返回 {@code true};
|
||||
*/
|
||||
public static boolean equalsAnyIgnoreCase(final CharSequence string, final CharSequence... searchStrings)
|
||||
{
|
||||
|
||||
@@ -29,13 +29,13 @@ public class MimeTypeUtils
|
||||
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
|
||||
// 图片
|
||||
"bmp", "gif", "jpg", "jpeg", "png",
|
||||
// word excel powerpoint
|
||||
// Word、Excel、PowerPoint 文档
|
||||
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt",
|
||||
// 压缩文件
|
||||
"rar", "zip", "gz", "bz2",
|
||||
// 视频格式
|
||||
"mp4", "avi", "rmvb",
|
||||
// pdf
|
||||
// pdf 文档
|
||||
"pdf" };
|
||||
|
||||
public static String getExtension(String prefix)
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
package com.bms.common.utils.file;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Date;
|
||||
|
||||
import com.aliyun.oss.ClientBuilderConfiguration;
|
||||
import com.aliyun.oss.HttpMethod;
|
||||
import com.aliyun.oss.OSS;
|
||||
import com.aliyun.oss.OSSClientBuilder;
|
||||
import com.aliyun.oss.model.CannedAccessControlList;
|
||||
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
|
||||
import com.aliyun.oss.model.OSSObject;
|
||||
import com.aliyun.oss.model.ObjectMetadata;
|
||||
import com.aliyun.oss.model.PutObjectRequest;
|
||||
import com.aliyun.oss.model.ResponseHeaderOverrides;
|
||||
import com.bms.common.config.OssProperties;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -67,6 +74,28 @@ public class OssStorage {
|
||||
}
|
||||
}
|
||||
|
||||
/** 为私有 OSS 对象生成带附件文件名的限时下载链接。 */
|
||||
public String generatePresignedDownloadUrl(String storageKey, String fileName, Date expiresAt) {
|
||||
String objectKey = resolveObjectKey(storageKey);
|
||||
OSS client = createClient();
|
||||
try {
|
||||
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(
|
||||
properties.getBucket(), objectKey, HttpMethod.GET);
|
||||
request.setExpiration(expiresAt);
|
||||
|
||||
String downloadName = StringUtils.hasText(fileName) ? fileName : "账单导出.xlsx";
|
||||
String encodedFileName = URLEncoder.encode(downloadName, StandardCharsets.UTF_8).replace("+", "%20");
|
||||
ResponseHeaderOverrides responseHeaders = new ResponseHeaderOverrides();
|
||||
responseHeaders.setContentDisposition("attachment; filename=" + encodedFileName
|
||||
+ ";filename*=utf-8''" + encodedFileName);
|
||||
request.setResponseHeaders(responseHeaders);
|
||||
URL signedUrl = client.generatePresignedUrl(request);
|
||||
return signedUrl.toExternalForm();
|
||||
} finally {
|
||||
client.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(String storageKey) {
|
||||
String objectKey = resolveObjectKey(storageKey);
|
||||
OSS client = createClient();
|
||||
|
||||
@@ -20,7 +20,7 @@ public class EscapeUtil
|
||||
TEXT[i] = new char[] { (char) i };
|
||||
}
|
||||
|
||||
// special HTML characters
|
||||
// 特殊的 HTML 字符
|
||||
TEXT['\''] = "'".toCharArray(); // 单引号
|
||||
TEXT['"'] = """.toCharArray(); // 双引号
|
||||
TEXT['&'] = "&".toCharArray(); // &符
|
||||
|
||||
@@ -18,7 +18,7 @@ import java.util.regex.Pattern;
|
||||
public final class HTMLFilter
|
||||
{
|
||||
/**
|
||||
* regex flag union representing /si modifiers in php
|
||||
* 表示 php 中 /si 修饰符的正则表达式标志组合
|
||||
**/
|
||||
private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
|
||||
private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
|
||||
@@ -45,60 +45,60 @@ public final class HTMLFilter
|
||||
private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
|
||||
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
|
||||
|
||||
// @xxx could grow large... maybe use sesat's ReferenceMap
|
||||
// @xxx 可能会变得很大……也许可以使用 sesat 的 ReferenceMap
|
||||
private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* set of allowed html elements, along with allowed attributes for each element
|
||||
* 允许的 html 元素集合,以及每个元素允许的属性
|
||||
**/
|
||||
private final Map<String, List<String>> vAllowed;
|
||||
/**
|
||||
* counts of open tags for each (allowable) html element
|
||||
* 每个(允许的)html 元素当前打开的标签计数
|
||||
**/
|
||||
private final Map<String, Integer> vTagCounts = new HashMap<>();
|
||||
|
||||
/**
|
||||
* html elements which must always be self-closing (e.g. "<img />")
|
||||
* 必须始终自闭合的 html 元素(例如 "<img />")
|
||||
**/
|
||||
private final String[] vSelfClosingTags;
|
||||
/**
|
||||
* html elements which must always have separate opening and closing tags (e.g. "<b></b>")
|
||||
* 必须始终具有独立开始和结束标签的 html 元素(例如 "<b></b>")
|
||||
**/
|
||||
private final String[] vNeedClosingTags;
|
||||
/**
|
||||
* set of disallowed html elements
|
||||
* 不允许的 html 元素集合
|
||||
**/
|
||||
private final String[] vDisallowed;
|
||||
/**
|
||||
* attributes which should be checked for valid protocols
|
||||
* 需要校验协议是否有效的属性
|
||||
**/
|
||||
private final String[] vProtocolAtts;
|
||||
/**
|
||||
* allowed protocols
|
||||
* 允许的协议
|
||||
**/
|
||||
private final String[] vAllowedProtocols;
|
||||
/**
|
||||
* tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
|
||||
* 内容为空时应移除的标签(例如 "<b></b>" 或 "<b />")
|
||||
**/
|
||||
private final String[] vRemoveBlanks;
|
||||
/**
|
||||
* entities allowed within html markup
|
||||
* html 标记中允许的实体
|
||||
**/
|
||||
private final String[] vAllowedEntities;
|
||||
/**
|
||||
* flag determining whether comments are allowed in input String.
|
||||
* 用于标识输入字符串中是否允许注释的标志。
|
||||
*/
|
||||
private final boolean stripComment;
|
||||
private final boolean encodeQuotes;
|
||||
/**
|
||||
* flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>"
|
||||
* becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped.
|
||||
* 用于标识在遇到“不配对”的尖括号时是否尝试构造标签的标志(例如 "<b text </b>"
|
||||
* 会变成 "<b> text </b>")。如果设置为 false,不配对的尖括号将被 html 转义。
|
||||
*/
|
||||
private final boolean alwaysMakeTags;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
* 默认构造方法。
|
||||
*/
|
||||
public HTMLFilter()
|
||||
{
|
||||
@@ -125,7 +125,7 @@ public final class HTMLFilter
|
||||
vSelfClosingTags = new String[] { "img" };
|
||||
vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" };
|
||||
vDisallowed = new String[] {};
|
||||
vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp.
|
||||
vAllowedProtocols = new String[] { "http", "mailto", "https" }; // 不支持 ftp。
|
||||
vProtocolAtts = new String[] { "src", "href" };
|
||||
vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" };
|
||||
vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" };
|
||||
@@ -135,9 +135,9 @@ public final class HTMLFilter
|
||||
}
|
||||
|
||||
/**
|
||||
* Map-parameter configurable constructor.
|
||||
* 可通过 Map 参数配置的构造方法。
|
||||
*
|
||||
* @param conf map containing configuration. keys match field names.
|
||||
* @param conf 包含配置的 map。键与字段名一致。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public HTMLFilter(final Map<String, Object> conf)
|
||||
@@ -171,7 +171,7 @@ public final class HTMLFilter
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// my versions of some PHP library functions
|
||||
// 我实现的某些 PHP 库函数版本
|
||||
public static String chr(final int decimal)
|
||||
{
|
||||
return String.valueOf((char) decimal);
|
||||
@@ -190,10 +190,10 @@ public final class HTMLFilter
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* given a user submitted input String, filter out any invalid or restricted html.
|
||||
* 对用户提交的输入字符串,过滤掉任何无效或受限的 html。
|
||||
*
|
||||
* @param input text (i.e. submitted by a user) than may contain html
|
||||
* @return "clean" version of input, with only valid, whitelisted html elements allowed
|
||||
* @param input 可能包含 html 的文本(即用户提交的内容)
|
||||
* @return 输入的“干净”版本,仅允许有效且在 html 白名单中的元素
|
||||
*/
|
||||
public String filter(final String input)
|
||||
{
|
||||
@@ -242,7 +242,7 @@ public final class HTMLFilter
|
||||
if (alwaysMakeTags)
|
||||
{
|
||||
//
|
||||
// try and form html
|
||||
// 尝试构造 html
|
||||
//
|
||||
s = regexReplace(P_END_ARROW, "", s);
|
||||
// 不追加结束标签
|
||||
@@ -253,15 +253,15 @@ public final class HTMLFilter
|
||||
else
|
||||
{
|
||||
//
|
||||
// escape stray brackets
|
||||
// 转义多余的括号
|
||||
//
|
||||
s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s);
|
||||
s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s);
|
||||
|
||||
//
|
||||
// the last regexp causes '<>' entities to appear
|
||||
// (we need to do a lookahead assertion so that the last bracket can
|
||||
// be used in the next pass of the regexp)
|
||||
// 上一个正则表达式会导致出现 "<>" 实体
|
||||
// (我们需要使用先行断言,以便最后一个括号
|
||||
// 可以在正则表达式的下一次匹配中使用)
|
||||
//
|
||||
s = regexReplace(P_BOTH_ARROWS, "", s);
|
||||
}
|
||||
@@ -282,8 +282,8 @@ public final class HTMLFilter
|
||||
}
|
||||
m.appendTail(buf);
|
||||
|
||||
// these get tallied in processTag
|
||||
// (remember to reset before subsequent calls to filter method)
|
||||
// 这些计数在 processTag 中进行累加
|
||||
// (记住在后续调用 filter 方法之前重置计数)
|
||||
final StringBuilder sBuilder = new StringBuilder(buf.toString());
|
||||
for (String key : vTagCounts.keySet())
|
||||
{
|
||||
@@ -325,7 +325,7 @@ public final class HTMLFilter
|
||||
|
||||
private String processTag(final String s)
|
||||
{
|
||||
// ending tags
|
||||
// 结束标签
|
||||
Matcher m = P_END_TAG.matcher(s);
|
||||
if (m.find())
|
||||
{
|
||||
@@ -343,7 +343,7 @@ public final class HTMLFilter
|
||||
}
|
||||
}
|
||||
|
||||
// starting tags
|
||||
// 开始标签
|
||||
m = P_START_TAG.matcher(s);
|
||||
if (m.find())
|
||||
{
|
||||
@@ -424,7 +424,7 @@ public final class HTMLFilter
|
||||
}
|
||||
}
|
||||
|
||||
// comments
|
||||
// 注释
|
||||
m = P_COMMENT.matcher(s);
|
||||
if (!stripComment && m.find())
|
||||
{
|
||||
@@ -443,7 +443,7 @@ public final class HTMLFilter
|
||||
final String protocol = m.group(1);
|
||||
if (!inArray(protocol, vAllowedProtocols))
|
||||
{
|
||||
// bad protocol, turn into local anchor link instead
|
||||
// 协议不合法,改为本地锚点链接
|
||||
s = "#" + s.substring(protocol.length() + 1);
|
||||
if (s.startsWith("#//"))
|
||||
{
|
||||
@@ -499,7 +499,7 @@ public final class HTMLFilter
|
||||
{
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
// validate entities throughout the string
|
||||
// 校验整个字符串中的实体
|
||||
Matcher m = P_VALID_ENTITIES.matcher(s);
|
||||
while (m.find())
|
||||
{
|
||||
|
||||
@@ -569,12 +569,12 @@ public class ExcelUtil<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* Common one-liner export for controllers.
|
||||
* 供控制器使用的通用一行式导出方法。
|
||||
*
|
||||
* @param response HTTP response
|
||||
* @param list export rows
|
||||
* @param clazz row class with @Excel fields
|
||||
* @param sheetName sheet / file title
|
||||
* @param response HTTP 响应
|
||||
* @param list 导出的数据行
|
||||
* @param clazz 带有 @Excel 注解字段的数据行类
|
||||
* @param sheetName sheet / 文件标题
|
||||
*/
|
||||
public static <E> void export(HttpServletResponse response, List<E> list, Class<E> clazz, String sheetName)
|
||||
{
|
||||
|
||||
@@ -75,10 +75,10 @@ public final class Base64
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes hex octects into Base64
|
||||
* 将十六进制字节编码为 Base64
|
||||
*
|
||||
* @param binaryData Array containing binaryData
|
||||
* @return Encoded Base64 array
|
||||
* @param binaryData 包含二进制数据的数组
|
||||
* @return 编码后的 Base64 数组
|
||||
*/
|
||||
public static String encode(byte[] binaryData)
|
||||
{
|
||||
@@ -124,7 +124,7 @@ public final class Base64
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
|
||||
}
|
||||
|
||||
// form integral number of 6-bit groups
|
||||
// 组成完整的 6 位组
|
||||
if (fewerThan24bits == EIGHTBIT)
|
||||
{
|
||||
b1 = binaryData[dataIndex];
|
||||
@@ -154,10 +154,10 @@ public final class Base64
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes Base64 data into octects
|
||||
* 将 Base64 数据解码为字节
|
||||
*
|
||||
* @param encoded string containing Base64 data
|
||||
* @return Array containind decoded data.
|
||||
* @param encoded 包含 Base64 数据的字符串
|
||||
* @return 包含解码后数据的数组。
|
||||
*/
|
||||
public static byte[] decode(String encoded)
|
||||
{
|
||||
@@ -167,12 +167,12 @@ public final class Base64
|
||||
}
|
||||
|
||||
char[] base64Data = encoded.toCharArray();
|
||||
// remove white spaces
|
||||
// 去除空白字符
|
||||
int len = removeWhiteSpace(base64Data);
|
||||
|
||||
if (len % FOURBYTE != 0)
|
||||
{
|
||||
return null;// should be divisible by four
|
||||
return null;// 应为 4 的倍数
|
||||
}
|
||||
|
||||
int numberQuadruple = (len / FOURBYTE);
|
||||
@@ -198,7 +198,7 @@ public final class Base64
|
||||
|| !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++])))
|
||||
{
|
||||
return null;
|
||||
} // if found "no data" just return null
|
||||
} // 如果未找到数据,直接返回 null
|
||||
|
||||
b1 = base64Alphabet[d1];
|
||||
b2 = base64Alphabet[d2];
|
||||
@@ -212,7 +212,7 @@ public final class Base64
|
||||
|
||||
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])))
|
||||
{
|
||||
return null;// if found "no data" just return null
|
||||
return null;// 如果未找到数据,直接返回 null
|
||||
}
|
||||
|
||||
b1 = base64Alphabet[d1];
|
||||
@@ -221,10 +221,10 @@ public final class Base64
|
||||
d3 = base64Data[dataIndex++];
|
||||
d4 = base64Data[dataIndex++];
|
||||
if (!isData((d3)) || !isData((d4)))
|
||||
{// Check if they are PAD characters
|
||||
{// 检查它们是否为 PAD 字符
|
||||
if (isPad(d3) && isPad(d4))
|
||||
{
|
||||
if ((b2 & 0xf) != 0)// last 4 bits should be zero
|
||||
if ((b2 & 0xf) != 0)// 最后 4 位应为零
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -236,7 +236,7 @@ public final class Base64
|
||||
else if (!isPad(d3) && isPad(d4))
|
||||
{
|
||||
b3 = base64Alphabet[d3];
|
||||
if ((b3 & 0x3) != 0)// last 2 bits should be zero
|
||||
if ((b3 & 0x3) != 0)// 最后 2 位应为零
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -252,7 +252,7 @@ public final class Base64
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // No PAD e.g 3cQl
|
||||
{ // 没有 PAD,例如 3cQl
|
||||
b3 = base64Alphabet[d3];
|
||||
b4 = base64Alphabet[d4];
|
||||
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
@@ -264,10 +264,10 @@ public final class Base64
|
||||
}
|
||||
|
||||
/**
|
||||
* remove WhiteSpace from MIME containing encoded Base64 data.
|
||||
* 从包含已编码 Base64 数据的 MIME 中移除空白字符。
|
||||
*
|
||||
* @param data the byte array of base64 data (with WS)
|
||||
* @return the new length
|
||||
* @param data Base64 数据的字节数组(含空白字符)
|
||||
* @return 新的长度
|
||||
*/
|
||||
private static int removeWhiteSpace(char[] data)
|
||||
{
|
||||
@@ -276,7 +276,7 @@ public final class Base64
|
||||
return 0;
|
||||
}
|
||||
|
||||
// count characters that's not whitespace
|
||||
// 统计非空白字符的数量
|
||||
int newSize = 0;
|
||||
int len = data.length;
|
||||
for (int i = 0; i < len; i++)
|
||||
|
||||
@@ -97,10 +97,10 @@ public final class UUID implements java.io.Serializable, Comparable<UUID>
|
||||
|
||||
byte[] randomBytes = new byte[16];
|
||||
ng.nextBytes(randomBytes);
|
||||
randomBytes[6] &= 0x0f; /* clear version */
|
||||
randomBytes[6] |= 0x40; /* set to version 4 */
|
||||
randomBytes[8] &= 0x3f; /* clear variant */
|
||||
randomBytes[8] |= 0x80; /* set to IETF variant */
|
||||
randomBytes[6] &= 0x0f; /* 清除版本位 */
|
||||
randomBytes[6] |= 0x40; /* 设置为版本 4 */
|
||||
randomBytes[8] &= 0x3f; /* 清除变体位 */
|
||||
randomBytes[8] |= 0x80; /* 设置为 IETF 变体 */
|
||||
return new UUID(randomBytes);
|
||||
}
|
||||
|
||||
@@ -123,10 +123,10 @@ public final class UUID implements java.io.Serializable, Comparable<UUID>
|
||||
throw new InternalError("MD5 not supported");
|
||||
}
|
||||
byte[] md5Bytes = md.digest(name);
|
||||
md5Bytes[6] &= 0x0f; /* clear version */
|
||||
md5Bytes[6] |= 0x30; /* set to version 3 */
|
||||
md5Bytes[8] &= 0x3f; /* clear variant */
|
||||
md5Bytes[8] |= 0x80; /* set to IETF variant */
|
||||
md5Bytes[6] &= 0x0f; /* 清除版本位 */
|
||||
md5Bytes[6] |= 0x30; /* 设置为版本 3 */
|
||||
md5Bytes[8] &= 0x3f; /* 清除变体位 */
|
||||
md5Bytes[8] |= 0x80; /* 设置为 IETF 变体 */
|
||||
return new UUID(md5Bytes);
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ public final class UUID implements java.io.Serializable, Comparable<UUID>
|
||||
*/
|
||||
public int version()
|
||||
{
|
||||
// Version is bits masked by 0x000000000000F000 in MS long
|
||||
// 版本号是最高有效长整型中被 0x000000000000F000 掩码的位
|
||||
return (int) ((mostSigBits >> 12) & 0x0f);
|
||||
}
|
||||
|
||||
@@ -217,11 +217,11 @@ public final class UUID implements java.io.Serializable, Comparable<UUID>
|
||||
*/
|
||||
public int variant()
|
||||
{
|
||||
// This field is composed of a varying number of bits.
|
||||
// 0 - - Reserved for NCS backward compatibility
|
||||
// 1 0 - The IETF aka Leach-Salz variant (used by this class)
|
||||
// 1 1 0 Reserved, Microsoft backward compatibility
|
||||
// 1 1 1 Reserved for future definition.
|
||||
// 该字段由数量可变的位组成。
|
||||
// 0 - - 为 NCS 向后兼容保留
|
||||
// 1 0 - IETF 即 Leach-Salz 变体(本类使用)
|
||||
// 1 1 0 保留,微软向后兼容
|
||||
// 1 1 1 保留供以后定义使用。
|
||||
return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ public final class UUID implements java.io.Serializable, Comparable<UUID>
|
||||
return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
|
||||
}
|
||||
|
||||
// Comparison Operations
|
||||
// 比较操作
|
||||
|
||||
/**
|
||||
* 将此 UUID 与指定的 UUID 比较。
|
||||
@@ -419,8 +419,8 @@ public final class UUID implements java.io.Serializable, Comparable<UUID>
|
||||
@Override
|
||||
public int compareTo(UUID val)
|
||||
{
|
||||
// The ordering is intentionally set up so that the UUIDs
|
||||
// can simply be numerically compared as two numbers
|
||||
// 此处有意这样设置比较顺序,使得 UUID
|
||||
// 可以简单地作为两个数字进行数值比较
|
||||
return (this.mostSigBits < val.mostSigBits ? -1 : //
|
||||
(this.mostSigBits > val.mostSigBits ? 1 : //
|
||||
(this.leastSigBits < val.leastSigBits ? -1 : //
|
||||
@@ -429,7 +429,7 @@ public final class UUID implements java.io.Serializable, Comparable<UUID>
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
// Private method start
|
||||
// 私有方法开始
|
||||
/**
|
||||
* 返回指定数字对应的hex值
|
||||
*
|
||||
|
||||
@@ -106,7 +106,7 @@ public class MyBatisConfig
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// ignore
|
||||
// 忽略
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import org.springframework.stereotype.Component;
|
||||
public class TmsApiProperties
|
||||
{
|
||||
/**
|
||||
* Agreed API identification code (header X-Api-Code or body apiCode).
|
||||
* 约定的 API 标识码(请求头 X-Api-Code 或请求体 apiCode)。
|
||||
*/
|
||||
private String apiCode = "BMS.TMS.ZYaOvlQzpcfau8TUaCFfO3Hp8ieHIqGw";
|
||||
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ public class BmsCustomerPriceSet extends BaseEntity
|
||||
/** 计价类型 FIXED=固定价 DISCOUNT=折扣 */
|
||||
private String setType;
|
||||
|
||||
/** Package status D=pending-enable A=audit E=effective R=reject S=stopped */
|
||||
/** 套餐状态 D=待启用 A=审核 E=生效 R=拒绝 S=停用 */
|
||||
private String priceSetStatus;
|
||||
|
||||
/** 套餐生效日期 */
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ public class BmsPriceSetOption
|
||||
/** 计价类型 FIXED=固定价 DISCOUNT=折扣 */
|
||||
private String setType;
|
||||
|
||||
/** Package status D=pending-enable A=audit E=effective R=reject S=stopped */
|
||||
/** 套餐状态 D=待启用 A=审核 E=生效 R=拒绝 S=停用 */
|
||||
private String status;
|
||||
|
||||
/** 生效日期 */
|
||||
|
||||
+2
-2
@@ -94,7 +94,7 @@ public class BmsCustomerPriceSetServiceImpl implements IBmsCustomerPriceSetServi
|
||||
if (existing != null)
|
||||
{
|
||||
bind.setCustomerPricesetId(existing.getCustomerPricesetId());
|
||||
// UI no longer collects bind dates: keep existing when not passed.
|
||||
// UI 不再收集绑定日期:未传时保留原值。
|
||||
if (bind.getPrBegindate() == null)
|
||||
{
|
||||
bind.setPrBegindate(existing.getPrBegindate());
|
||||
@@ -107,7 +107,7 @@ public class BmsCustomerPriceSetServiceImpl implements IBmsCustomerPriceSetServi
|
||||
}
|
||||
else
|
||||
{
|
||||
// pr_begindate NOT NULL; UI omits it — default to now (same as price-set bind).
|
||||
// pr_begindate NOT NULL,UI 未传时默认取当前时间(与价格套餐绑定一致)。
|
||||
if (bind.getPrBegindate() == null)
|
||||
{
|
||||
bind.setPrBegindate(DateUtils.getNowDate());
|
||||
|
||||
+3
-3
@@ -525,7 +525,7 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
// Concurrent first-create: peer may have inserted; re-lock existing row.
|
||||
// 并发首次创建:其他节点可能已插入;重新锁定已存在的行。
|
||||
if (isAccountInitRace(e))
|
||||
{
|
||||
account = customerMoneyMapper.selectAccountForUpdate(customerId, BASE_CURRENCY);
|
||||
@@ -539,7 +539,7 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
||||
}
|
||||
}
|
||||
|
||||
/** Deadlock / duplicate key while inserting ctr_customer_account. */
|
||||
/** 插入 ctr_customer_account 时出现死锁 / 唯一键冲突。 */
|
||||
private static boolean isAccountInitRace(Throwable e)
|
||||
{
|
||||
Throwable t = e;
|
||||
@@ -607,7 +607,7 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
||||
flow.setEffectiveDate(effectiveDate);
|
||||
flow.setExpireDate(expireDate);
|
||||
flow.setReason("账户初始化");
|
||||
// No login (open API / MQ / job): operatorId null, same as occupyCredit
|
||||
// 无登录态(开放 API / MQ / 定时任务):operatorId 为 null,与 occupyCredit 一致
|
||||
flow.setOperatorId(currentUserId());
|
||||
flow.setCreateBy(username);
|
||||
flow.setRemark(remark);
|
||||
|
||||
+2
-2
@@ -15,8 +15,8 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
import com.bms.common.utils.SecurityUtils;
|
||||
import com.bms.ar.domain.ArBankAccount;
|
||||
import com.bms.ar.mapper.ArBankAccountMapper;
|
||||
import com.bms.receivable.domain.ArBankAccount;
|
||||
import com.bms.receivable.mapper.ArBankAccountMapper;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomer;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerAttachment;
|
||||
import com.bms.customer.customerinfo.mapper.BmsCustomerMapper;
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.bms.price.airbill.domain;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
/** 提单费用分摊请求。一次最多 5 个提单号。 */
|
||||
@Data
|
||||
public class SrvBillAirAllocCommand
|
||||
{
|
||||
private List<String> billCodes;
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.bms.price.airbill.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
|
||||
/** 待分摊的一条提单费用,供费用处理弹框展示。 */
|
||||
@Data
|
||||
public class SrvBillAirAllocFee
|
||||
{
|
||||
private Long id;
|
||||
|
||||
private String billCode;
|
||||
|
||||
/** 提单主键 srv_bill_air.guid,写入订单费用的 source_id */
|
||||
private String ladingId;
|
||||
|
||||
/** 费用阶段,字典 bms_fee_level */
|
||||
private String feeLevel;
|
||||
|
||||
/** 费用状态,字典 bill_fee_status */
|
||||
private String billFeeStatus;
|
||||
|
||||
private String feeName;
|
||||
|
||||
private BigDecimal price;
|
||||
|
||||
private String currency;
|
||||
|
||||
/** 分摊维度:1订单 2重量 */
|
||||
private String allocDim;
|
||||
|
||||
private Integer orderCount;
|
||||
|
||||
/** 不能分摊时的原因。空表示可以分摊 */
|
||||
private String blockReason;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.bms.price.airbill.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
|
||||
/** 提单下参与分摊的一票订单。 */
|
||||
@Data
|
||||
public class SrvBillAirAllocOrder
|
||||
{
|
||||
private String billCode;
|
||||
|
||||
private String orderNo;
|
||||
|
||||
/** 计费订单 ID,对应 prc_order_bill.bill_id */
|
||||
private String billId;
|
||||
|
||||
/** 计费重,没有计费重时依次取实际重量、收货重量 */
|
||||
private BigDecimal chargeWeight;
|
||||
}
|
||||
@@ -15,9 +15,27 @@ public class SrvBillAirFee
|
||||
|
||||
private String billCode;
|
||||
|
||||
/** 字典 bms_fee_type */
|
||||
/** 附加费编号 prc_surcharge.sc_id */
|
||||
private String feeType;
|
||||
|
||||
/** 附加费名称,仅展示,不入库 */
|
||||
private String feeName;
|
||||
|
||||
/** 费用阶段,字典 bms_fee_level。空运为 2(b段),报关和清关为 3(c段) */
|
||||
private String feeLevel;
|
||||
|
||||
/** 费用入口:AIR 空运,DECLARE 报关,CLEAR 清关。页面不展示 */
|
||||
private String feeScene;
|
||||
|
||||
/** 提单费用状态,字典 bill_fee_status。只随分摊、作废操作变化 */
|
||||
private String billFeeStatus;
|
||||
|
||||
/** 是否分摊,字典 bill_fee_alloc:1是 0否 */
|
||||
private String allocFlag;
|
||||
|
||||
/** 分摊维度(票),字典 bill_fee_alloc_dim:1订单 2重量。是否分摊为否时为空 */
|
||||
private String allocDim;
|
||||
|
||||
private BigDecimal price;
|
||||
|
||||
/** 字典 bms_currency */
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.bms.price.airbill.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import com.bms.common.annotation.Excel;
|
||||
import lombok.Data;
|
||||
|
||||
/** 空运、报关、清关费用导入行。 */
|
||||
@Data
|
||||
public class SrvBillAirFeeImportRow
|
||||
{
|
||||
@Excel(name = "费用名称")
|
||||
private String feeName;
|
||||
|
||||
@Excel(name = "价格")
|
||||
private BigDecimal price;
|
||||
|
||||
@Excel(name = "币种")
|
||||
private String currency;
|
||||
|
||||
@Excel(name = "备注")
|
||||
private String remark;
|
||||
}
|
||||
@@ -17,6 +17,12 @@ public class SrvBillAirFeeLog
|
||||
|
||||
public static final String DELETE = "3";
|
||||
|
||||
/** 已分摊费用确定。日志不记录字段变化。 */
|
||||
public static final String CONFIRM = "4";
|
||||
|
||||
/** 已分摊或已确定费用作废。 */
|
||||
public static final String VOID = "5";
|
||||
|
||||
private Long logId;
|
||||
|
||||
private String billCode;
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.bms.price.airbill.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.bms.common.core.domain.BaseEntity;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 空运、报关、清关页面保存费用。
|
||||
* scene:air 空运,declare 报关,clear 清关。数据权限写在 params.dataScope。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SrvBillAirRoleFeeCommand extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String scene;
|
||||
|
||||
/** 入库入口,仅服务端填写 */
|
||||
@JsonIgnore
|
||||
private String feeScene;
|
||||
|
||||
private List<SrvBillAirFeeSave> bills = new ArrayList<>();
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.bms.price.airbill.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.bms.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 空运、报关、清关费用查询。数据权限写在 params.dataScope。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SrvBillAirRoleFeeQuery extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** air 空运,declare 报关,clear 清关 */
|
||||
private String scene;
|
||||
|
||||
/** 入库入口:AIR、DECLARE、CLEAR */
|
||||
private String feeScene;
|
||||
|
||||
private List<String> billCodes = new ArrayList<>();
|
||||
}
|
||||
@@ -10,22 +10,22 @@ import org.springframework.stereotype.Component;
|
||||
@ConfigurationProperties(prefix = "bms.billing")
|
||||
public class BillingProperties
|
||||
{
|
||||
/** local = Spring event; redis = Redis list queue */
|
||||
/** local = Spring 事件;redis = Redis 列表队列 */
|
||||
private String mqMode = "redis";
|
||||
private String mqQueueKey = "bms:billing:task";
|
||||
private boolean consumerEnabled = true;
|
||||
/** Messages popped per scheduled tick (redis mode) */
|
||||
/** 每个调度周期取出的消息数量(redis 模式) */
|
||||
private int consumerBatchSize = 32;
|
||||
/** Concurrent processTask threads (redis mode) */
|
||||
/** 并发执行 processTask 的线程数(redis 模式) */
|
||||
private int consumerThreads = 8;
|
||||
/** Concurrent threads for monitor order-source sync */
|
||||
/** 监控订单数源同步的并发线程数 */
|
||||
private int orderSourceSyncThreads = 8;
|
||||
/**
|
||||
* Concurrent threads for order-list local rebill (2~24).
|
||||
* Keep below Druid maxActive with headroom for other traffic.
|
||||
* 订单列表本地重计费的并发线程数(2~24)。
|
||||
* 需低于 Druid maxActive,并给其他流量留出余量。
|
||||
*/
|
||||
private int localRebillThreads = 16;
|
||||
/** Night window auto-retry of prc_billing_task (all statuses) */
|
||||
/** 每小时自动重试 prc_billing_task(所有状态) */
|
||||
private NightRetry nightRetry = new NightRetry();
|
||||
private Oracle oracle = new Oracle();
|
||||
|
||||
@@ -120,18 +120,18 @@ public class BillingProperties
|
||||
}
|
||||
|
||||
/**
|
||||
* Overnight rebill window: pull prc_billing_task (all success_status) and retryTask.
|
||||
* Default hours [0, 9) local time; empty result skips billing.
|
||||
* 自动重计费时间窗:拉取 prc_billing_task(所有 success_status)并执行 retryTask。
|
||||
* 默认本地时间 [0, 24) 点(全天);结果为空则跳过计费。
|
||||
*/
|
||||
public static class NightRetry
|
||||
{
|
||||
/** Master switch */
|
||||
/** 总开关 */
|
||||
private boolean enabled = true;
|
||||
/** Inclusive start hour (0 = 00:00) */
|
||||
/** 起始小时,含(0 = 00:00) */
|
||||
private int startHour = 0;
|
||||
/** Exclusive end hour (9 = stop at 09:00) */
|
||||
private int endHour = 9;
|
||||
/** Page size: bill this many then pull next page (default 500) */
|
||||
/** 结束小时,不含(24 = 全天) */
|
||||
private int endHour = 24;
|
||||
/** 分页大小:计费这么多条后再拉下一页(默认 500) */
|
||||
private int batchSize = 500;
|
||||
|
||||
public boolean isEnabled()
|
||||
@@ -177,9 +177,9 @@ public class BillingProperties
|
||||
|
||||
public static class Oracle
|
||||
{
|
||||
/** Upstream main-order table name on SLAVE/Oracle DS */
|
||||
/** SLAVE/Oracle 数据源上的上游主订单表名 */
|
||||
private String orderTable = "WEISHI_USER.TAB_ORDER";
|
||||
/** Optional default product when Oracle row has no PRODUCT_CODE */
|
||||
/** Oracle 行中没有 PRODUCT_CODE 时使用的可选默认产品 */
|
||||
private String defaultProductCode = "";
|
||||
|
||||
public String getOrderTable()
|
||||
|
||||
@@ -14,7 +14,7 @@ public class BmsProduct extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** maps to prc_price_set_product.psp_id */
|
||||
/** 对应 prc_price_set_product.psp_id */
|
||||
private Long productId;
|
||||
private Long psvId;
|
||||
private String priceType;
|
||||
@@ -30,7 +30,7 @@ public class BmsProduct extends BaseEntity
|
||||
private Date effectiveDate;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireDate;
|
||||
/** 0 pending, 1 active, 2 expired */
|
||||
/** 0 待生效,1 生效,2 已失效 */
|
||||
public static final String STATUS_PENDING = "0";
|
||||
public static final String STATUS_ACTIVE = "1";
|
||||
public static final String STATUS_EXPIRED = "2";
|
||||
@@ -45,8 +45,8 @@ public class BmsProduct extends BaseEntity
|
||||
private Integer countryCount;
|
||||
private Integer surchargeCount;
|
||||
private List<BmsProductCountry> countries;
|
||||
/** query alias of status (0/1/2) */
|
||||
/** status 的查询别名(0/1/2) */
|
||||
private String effectStatus;
|
||||
/** query: active = status in (0,1); expired = status 2 */
|
||||
/** 查询:active = status in (0,1);expired = status 2 */
|
||||
private String statusGroup;
|
||||
}
|
||||
|
||||
@@ -12,23 +12,23 @@ public class BmsProductCountry implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** maps to prc_price_set_detail.psd_id */
|
||||
/** 对应 prc_price_set_detail.psd_id */
|
||||
private Long detailId;
|
||||
private Long psvId;
|
||||
private Long pspId;
|
||||
private String priceType;
|
||||
/** FK to product.product_id */
|
||||
/** 外键,指向 product.product_id */
|
||||
private Long productId;
|
||||
private String productCode;
|
||||
/** Parse/import helper only (not a DB column). */
|
||||
/** 仅用于解析/导入辅助(非数据库字段)。 */
|
||||
private String productName;
|
||||
private String countryName;
|
||||
private String countryCode;
|
||||
/** prc_zone.zone_id; null when product country has no zone */
|
||||
/** prc_zone.zone_id;产品国家无分区时为 null */
|
||||
private Long zoneId;
|
||||
/** e.g. Z1; null when no zone */
|
||||
/** 例如 Z1;无分区时为 null */
|
||||
private String zoneCode;
|
||||
/** e.g. Australia(Z1); null when no zone */
|
||||
/** 例如 Australia(Z1);无分区时为 null */
|
||||
private String zoneName;
|
||||
private BigDecimal weightMin;
|
||||
private BigDecimal weightMax;
|
||||
@@ -39,10 +39,10 @@ public class BmsProductCountry implements Serializable
|
||||
private String leadTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date effectiveDate;
|
||||
/** null=open; closed to next import effective_date */
|
||||
/** null=未关闭;关闭到下次导入的 effective_date */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireDate;
|
||||
/** 0 pending, 1 active, 2 expired */
|
||||
/** 0 待生效,1 生效,2 已失效 */
|
||||
private String status;
|
||||
/** 0未删除 1已删除 */
|
||||
public static final String IS_DEL_NO = "0";
|
||||
@@ -52,8 +52,8 @@ public class BmsProductCountry implements Serializable
|
||||
private Date createTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
/** query only: true = expire_date is null */
|
||||
/** 仅查询用:true = expire_date 为 null */
|
||||
private Boolean openOnly;
|
||||
/** query alias of status (0/1/2) */
|
||||
/** status 的查询别名(0/1/2) */
|
||||
private String effectStatus;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public class BmsProductImportTask implements Serializable
|
||||
private int percent;
|
||||
private String message;
|
||||
private String error;
|
||||
/** Preview parse payload (products / details); not persisted to DB. */
|
||||
/** 预览解析结果(products / details);不落库。 */
|
||||
private Object result;
|
||||
|
||||
public String getTaskId()
|
||||
|
||||
@@ -29,7 +29,7 @@ public class BmsRedlineProduct extends BaseEntity
|
||||
private Date effectiveDate;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireDate;
|
||||
/** 0 pending, 1 active, 2 expired */
|
||||
/** 0 待生效,1 生效,2 已失效 */
|
||||
public static final String STATUS_PENDING = "0";
|
||||
public static final String STATUS_ACTIVE = "1";
|
||||
public static final String STATUS_EXPIRED = "2";
|
||||
@@ -44,8 +44,8 @@ public class BmsRedlineProduct extends BaseEntity
|
||||
private Integer countryCount;
|
||||
private Integer surchargeCount;
|
||||
private List<BmsRedlineProductCountry> countries;
|
||||
/** query alias of status (0/1/2) */
|
||||
/** status 的查询别名(0/1/2) */
|
||||
private String effectStatus;
|
||||
/** query: active = status in (0,1); expired = status 2 */
|
||||
/** 查询:active = status in (0,1);expired = status 2 */
|
||||
private String statusGroup;
|
||||
}
|
||||
|
||||
+10
-10
@@ -12,23 +12,23 @@ public class BmsRedlineProductCountry implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** maps to prc_price_set_detail.psd_id */
|
||||
/** 对应 prc_price_set_detail.psd_id */
|
||||
private Long detailId;
|
||||
private Long psvId;
|
||||
private Long pspId;
|
||||
private String priceType;
|
||||
/** FK to product.product_id / psp_id */
|
||||
/** 外键,指向 product.product_id / psp_id */
|
||||
private Long productId;
|
||||
private String productCode;
|
||||
/** Parse/import helper only (not a DB column). */
|
||||
/** 仅用于解析/导入辅助(非数据库字段)。 */
|
||||
private String productName;
|
||||
private String countryName;
|
||||
private String countryCode;
|
||||
/** prc_zone.zone_id; null when product country has no zone */
|
||||
/** prc_zone.zone_id;产品国家无分区时为 null */
|
||||
private Long zoneId;
|
||||
/** e.g. Z1; null when no zone */
|
||||
/** 例如 Z1;无分区时为 null */
|
||||
private String zoneCode;
|
||||
/** e.g. Australia(Z1); null when no zone */
|
||||
/** 例如 Australia(Z1);无分区时为 null */
|
||||
private String zoneName;
|
||||
private BigDecimal weightMin;
|
||||
private BigDecimal weightMax;
|
||||
@@ -39,10 +39,10 @@ public class BmsRedlineProductCountry implements Serializable
|
||||
private String leadTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date effectiveDate;
|
||||
/** null=open; closed to next import effective_date */
|
||||
/** null=未关闭;关闭到下次导入的 effective_date */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireDate;
|
||||
/** 0 pending, 1 active, 2 expired */
|
||||
/** 0 待生效,1 生效,2 已失效 */
|
||||
private String status;
|
||||
/** 0未删除 1已删除 */
|
||||
public static final String IS_DEL_NO = "0";
|
||||
@@ -52,8 +52,8 @@ public class BmsRedlineProductCountry implements Serializable
|
||||
private Date createTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
/** query only: true = expire_date is null */
|
||||
/** 仅查询用:true = expire_date 为 null */
|
||||
private Boolean openOnly;
|
||||
/** query alias of status (0/1/2) */
|
||||
/** status 的查询别名(0/1/2) */
|
||||
private String effectStatus;
|
||||
}
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ public class BmsRedlineProductImportTask implements Serializable
|
||||
private int percent;
|
||||
private String message;
|
||||
private String error;
|
||||
/** Preview parse payload (products / details); not persisted to DB. */
|
||||
/** 预览解析结果(products / details);不落库。 */
|
||||
private Object result;
|
||||
|
||||
public String getTaskId()
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ public class CatalogPreviewApplyRequest implements Serializable
|
||||
|
||||
private Long psvId;
|
||||
private List<BmsProduct> products;
|
||||
/** key = upper-case productCode */
|
||||
/** key = 大写后的 productCode */
|
||||
private Map<String, List<BmsProductCountry>> detailsByProductCode;
|
||||
|
||||
public Long getPsvId()
|
||||
|
||||
@@ -15,10 +15,16 @@ public class ChannelProductMap
|
||||
/** 渠道表主键 srv_service_channel.id */
|
||||
private String channelId;
|
||||
|
||||
/** 渠道编码,对应 srv_service_channel.channel_code */
|
||||
private String channelCode;
|
||||
|
||||
private Long productId;
|
||||
|
||||
/** 应收套餐 prc_price_set.price_set_id */
|
||||
private Long priceSetId;
|
||||
/** 渠道名称(列表回显) */
|
||||
private String channelName;
|
||||
|
||||
/** 产品名称(列表回显) */
|
||||
private String productName;
|
||||
|
||||
/** AR 应收,AP 应付 */
|
||||
private String payType;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 打单套餐价卡版本(按 pay_product_term.version_no 聚合,不走价格套餐 prc_price_set_version)。
|
||||
*/
|
||||
@Data
|
||||
public class LabelPackageVersion
|
||||
{
|
||||
private Long priceSetId;
|
||||
|
||||
private Integer versionNo;
|
||||
|
||||
/** 展示用,如 V1 */
|
||||
private String versionLabel;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date effectiveTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date expireTime;
|
||||
|
||||
/** 与 pay_product_term.status 一致:0待生效 1生效 2已失效 */
|
||||
private String status;
|
||||
|
||||
private Integer productCount;
|
||||
|
||||
/** status=1 生效条数 */
|
||||
private Integer activeCount;
|
||||
|
||||
/** status=0 待生效条数 */
|
||||
private Integer pendingCount;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -12,6 +12,9 @@ public class PayExtraLine
|
||||
|
||||
private Long productId;
|
||||
|
||||
/** 价卡周期,对应 pay_product_term.term_id;与 productId 组成明细维度 */
|
||||
private Long termId;
|
||||
|
||||
/** 邮编 或 配置 */
|
||||
private String lineType;
|
||||
|
||||
|
||||
@@ -11,7 +11,13 @@ public class PayPriceCompare
|
||||
|
||||
private PayProduct newer;
|
||||
|
||||
/** 有效期、来源文件、币种 */
|
||||
private List<PayPriceDiff> versionDiffs;
|
||||
|
||||
private List<PayPriceDiff> weightDiffs;
|
||||
|
||||
private List<PayPriceDiff> surchargeDiffs;
|
||||
|
||||
/** 邮编等不能归进重量段和附加费的原文 */
|
||||
private List<PayPriceDiff> extraDiffs;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import lombok.Data;
|
||||
@Data
|
||||
public class PayPriceDiff
|
||||
{
|
||||
/** 版本、重量段、附加费、其他配置 */
|
||||
private String kind;
|
||||
|
||||
private String name;
|
||||
|
||||
private String zoneName;
|
||||
|
||||
@@ -120,6 +120,12 @@ public class PayProduct extends BaseEntity
|
||||
@Excel(name = "附加费数", width = 12)
|
||||
private Integer surchargeCount;
|
||||
|
||||
/** 已绑定渠道展示文案(非表字段,列表回显) */
|
||||
private String boundChannelText;
|
||||
|
||||
/** 已绑定渠道数量(非表字段) */
|
||||
private Integer boundChannelCount;
|
||||
|
||||
@Excel(name = "其他配置数", width = 12)
|
||||
private Integer extraCount;
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ public class PayProductTerm extends BaseEntity
|
||||
|
||||
private Long productId;
|
||||
|
||||
/** 打单套餐ID;应付公共模板为空 */
|
||||
private Long priceSetId;
|
||||
|
||||
private Integer versionNo;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
@@ -30,7 +33,7 @@ public class PayProductTerm extends BaseEntity
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date effectiveEnd;
|
||||
|
||||
/** 0 生效,1 失效 */
|
||||
/** 0待生效 1生效 2已失效 */
|
||||
private String status;
|
||||
|
||||
private String productName;
|
||||
|
||||
@@ -13,5 +13,17 @@ public class PaySaveCommand
|
||||
/** 为 true 时,同文件同表同名的旧产品先删后插 */
|
||||
private boolean overwrite;
|
||||
|
||||
/** 打单套餐ID;有值时走 LABEL 导入(稳定 product_id + term.price_set_id) */
|
||||
private Long priceSetId;
|
||||
|
||||
/** 打单套餐名称;priceSetId 为空时新建套餐壳 */
|
||||
private String priceSetName;
|
||||
|
||||
/** 打单套餐备注 */
|
||||
private String priceSetNote;
|
||||
|
||||
/** 编辑待生效版本时传入,覆盖该 version_no,不新建版本号 */
|
||||
private Integer versionNo;
|
||||
|
||||
private List<PayProduct> products = new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -15,13 +15,19 @@ public class PaySurcharge
|
||||
|
||||
private Long productId;
|
||||
|
||||
/** 所属价卡版本,跟产品版本一起变 */
|
||||
/** 价卡周期,对应 pay_product_term.term_id;与 productId 组成明细维度 */
|
||||
private Long termId;
|
||||
|
||||
/** 所属价卡版本,跟产品版本一起变(展示兼容) */
|
||||
private Integer versionNo;
|
||||
|
||||
private String sectionName;
|
||||
|
||||
private String feeName;
|
||||
|
||||
/** 附加费配置ID,对应 prc_surcharge.sc_id */
|
||||
private Long scId;
|
||||
|
||||
private String zoneName;
|
||||
|
||||
/** 收费标准原文 */
|
||||
|
||||
@@ -13,7 +13,10 @@ public class PayWeightBand
|
||||
|
||||
private Long productId;
|
||||
|
||||
/** 所属价卡版本,跟产品版本一起变 */
|
||||
/** 价卡周期,对应 pay_product_term.term_id;与 productId 组成明细维度 */
|
||||
private Long termId;
|
||||
|
||||
/** 所属价卡版本,跟产品版本一起变(展示兼容) */
|
||||
private Integer versionNo;
|
||||
|
||||
/** 报价区块,例如派送费、偏远附加费 */
|
||||
|
||||
@@ -17,17 +17,17 @@ public class PrcBillingTask extends BaseEntity
|
||||
private String transferNo;
|
||||
private String trackingNo;
|
||||
private String orderNo;
|
||||
/** Customer code snapshot for filter / rebill */
|
||||
/** 客户编码快照,用于筛选 / 重计费 */
|
||||
private String customerCode;
|
||||
/** PENDING / REBILL / REFUND */
|
||||
private String billType;
|
||||
/** WAITING / SUCCESS / FAILED */
|
||||
private String successStatus;
|
||||
/** Billing time from mobile (not Oracle order time; not overwritten on writeback) */
|
||||
/** 移动端传入的计费时间(非 Oracle 订单时间;回写时不会被覆盖) */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date billTime;
|
||||
private String billId;
|
||||
/** Stable fail code for search, e.g. NO_CUSTOMER_PACKAGE */
|
||||
/** 用于查询的稳定失败码,例如 NO_CUSTOMER_PACKAGE */
|
||||
private String failCode;
|
||||
private String errorMessage;
|
||||
}
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* Price-set detail batch bind customers.
|
||||
* 价格套餐明细批量绑定客户。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@@ -23,8 +23,8 @@ public class PrcCustomerPriceSetBatch extends BaseEntity
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date prEnddate;
|
||||
private String priceSetNote;
|
||||
/** Customers to bind (need customerCode; id/name optional). */
|
||||
/** 要绑定的客户(需要 customerCode;id/name 可选)。 */
|
||||
private List<PrcCustomerPriceSet> customers;
|
||||
/** true = move already-bound customers onto this set; false = skip them. */
|
||||
/** true = 把已绑定的客户迁移到本套餐;false = 跳过这些客户。 */
|
||||
private Boolean overwriteExisting;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public class PrcOrderBill extends BaseEntity
|
||||
private String trackingNo;
|
||||
/** 批量查询:多个运单号精确匹配(仅查询用,非表字段) */
|
||||
private List<String> trackingNos;
|
||||
/** transfer order no */
|
||||
/** 转单号 */
|
||||
private String transferNo;
|
||||
/** 批量查询:多个转单号精确匹配(仅查询用,非表字段) */
|
||||
private List<String> transferNos;
|
||||
@@ -50,7 +50,7 @@ public class PrcOrderBill extends BaseEntity
|
||||
private List<String> customerCodes;
|
||||
private Integer pieces;
|
||||
private BigDecimal actualWeight;
|
||||
/** Receive weight from upstream TAB_ORDER.WEIGHT */
|
||||
/** 收货重量,来自上游 TAB_ORDER.WEIGHT */
|
||||
private BigDecimal receiveWeight;
|
||||
private BigDecimal volume;
|
||||
private BigDecimal volumeWeight;
|
||||
@@ -84,9 +84,10 @@ public class PrcOrderBill extends BaseEntity
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date bizDate;
|
||||
private Long tmsId;
|
||||
|
||||
private String matchSource;
|
||||
private String matchMessage;
|
||||
/** 渠道编码(上游 TAB_ORDER.PRODUCT_FLOOR_CODE) */
|
||||
private String productFloorCode;
|
||||
/** 渠道名称(上游 TAB_ORDER.PRODUCT_FLOOR) */
|
||||
private String productFloor;
|
||||
private Long priceSetId;
|
||||
private String priceSetCode;
|
||||
private String priceSetName;
|
||||
@@ -109,9 +110,9 @@ public class PrcOrderBill extends BaseEntity
|
||||
private BigDecimal registrationFee;
|
||||
private BigDecimal freightAmount;
|
||||
private BigDecimal surchargeAmount;
|
||||
/** receivable total */
|
||||
/** 应收合计 */
|
||||
private BigDecimal incomeTotal;
|
||||
/** payable total */
|
||||
/** 应付合计 */
|
||||
private BigDecimal payTotal;
|
||||
private String currency;
|
||||
/** 应收账单状态:UNBILLED未出账/PARTIAL部分出账/BILLED已出账。 */
|
||||
@@ -122,7 +123,7 @@ public class PrcOrderBill extends BaseEntity
|
||||
private String creditArrearsStatus;
|
||||
/** NONE / REFUNDED */
|
||||
private String refundStatus;
|
||||
/** Billing fail: 0 ok; 1/2/3 sales miss; 23/24/25 catalog miss; soft-fail amount=0, no fee rows */
|
||||
/** 计费失败:0 正常,写入应收账单+费用;非 0 金额为 0,不写入应收账单/费用 */
|
||||
private Integer billingFailStatus;
|
||||
/** 失败原因 */
|
||||
private String errorMessage;
|
||||
@@ -133,6 +134,6 @@ public class PrcOrderBill extends BaseEntity
|
||||
private List<PrcIncomeBillFee> incomeFees;
|
||||
private List<PrcPayBillFee> payFees;
|
||||
|
||||
/** Excel export fields (Java props; export only). */
|
||||
/** Excel 导出字段(Java 属性名;仅导出用)。 */
|
||||
private List<String> exportFields;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public class PrcOrderBillExportTask implements Serializable
|
||||
private String error;
|
||||
private String exportType;
|
||||
private String fileName;
|
||||
/** Relative path under download root, or empty until success. */
|
||||
/** 下载根目录下的相对路径,成功前为空。 */
|
||||
private String storageKey;
|
||||
private int rowCount;
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ public class PrcPayBillFee implements Serializable
|
||||
private String feeType;
|
||||
private String feeName;
|
||||
private Long scId;
|
||||
|
||||
/** 费用所属阶段:1 a段,2 b段,3 c段,4 d段 */
|
||||
private String feeLevel;
|
||||
private BigDecimal amount;
|
||||
private String currency;
|
||||
private String roundMode;
|
||||
@@ -26,6 +29,12 @@ public class PrcPayBillFee implements Serializable
|
||||
|
||||
/** CALC 计算,LADING 提单分摊,MANUAL 人工 */
|
||||
private String feeSource;
|
||||
|
||||
/** 来源数据id */
|
||||
private String sourceId;
|
||||
|
||||
/** 是否删除:0未删除 1已删除 */
|
||||
private String isDeleted;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public class PrcPriceExcelFile extends BaseEntity
|
||||
private Long pefId;
|
||||
/** PRODUCT / REDLINE / SALES */
|
||||
private String excelType;
|
||||
/** FK prc_price_set_version.psv_id */
|
||||
/** 外键 prc_price_set_version.psv_id */
|
||||
private Long psvId;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date effectiveTime;
|
||||
@@ -32,6 +32,6 @@ public class PrcPriceExcelFile extends BaseEntity
|
||||
private String contentType;
|
||||
private String fileExtension;
|
||||
private String ossId;
|
||||
/** Comma-separated sheet names (optional) */
|
||||
/** 逗号分隔的 Sheet 名称(可选) */
|
||||
private String sheetNames;
|
||||
}
|
||||
|
||||
@@ -15,10 +15,12 @@ public class PrcPriceSet extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Ordinary package (default). */
|
||||
/** 普通套餐(默认)。 */
|
||||
public static final String CATEGORY_PACKAGE = "PACKAGE";
|
||||
/** Boss exclusive quote. */
|
||||
/** 老板专属报价。 */
|
||||
public static final String CATEGORY_EXCLUSIVE = "EXCLUSIVE";
|
||||
/** Label / 打单套餐(应收价卡套餐). */
|
||||
public static final String CATEGORY_LABEL = "LABEL";
|
||||
|
||||
private Long priceSetId;
|
||||
private String priceSetCode;
|
||||
@@ -26,7 +28,7 @@ public class PrcPriceSet extends BaseEntity
|
||||
private String priceSetNote;
|
||||
/** FIXED / DISCOUNT */
|
||||
private String setType;
|
||||
/** PACKAGE=ordinary set / EXCLUSIVE=boss exclusive quote */
|
||||
/** PACKAGE=普通套餐 / EXCLUSIVE=老板专属报价 / LABEL=打单套餐 */
|
||||
private String setCategory;
|
||||
private BigDecimal defaultDiscount;
|
||||
private String defaultType;
|
||||
@@ -41,27 +43,27 @@ public class PrcPriceSet extends BaseEntity
|
||||
|
||||
private Integer productCount;
|
||||
private Integer customerCount;
|
||||
/** List display: bound customer code/name summary (not a DB column). */
|
||||
/** 列表展示:已绑定客户编码/名称摘要(非数据库字段)。 */
|
||||
private String boundCustomers;
|
||||
private Boolean expired;
|
||||
private String keyword;
|
||||
/** Query only: filter packages by bound customer id. */
|
||||
/** 仅查询用:按绑定客户ID筛选套餐。 */
|
||||
private Long customerId;
|
||||
private String submitFlag;
|
||||
/** current version on create/edit save */
|
||||
/** 新建/编辑保存时的当前版本 */
|
||||
private PrcPriceSetVersion version;
|
||||
private List<PrcPriceSetDetail> details;
|
||||
private List<PrcPriceSetVersion> versions;
|
||||
private List<PrcCustomerPriceSet> customers;
|
||||
/** async save task id (not persisted) */
|
||||
/** 异步保存任务ID(不落库) */
|
||||
private String saveTaskId;
|
||||
/** Y: ignored on save; library pull only via /detail/pullFromLibrary */
|
||||
/** Y:保存时忽略;只能通过 /detail/pullFromLibrary 从库拉取 */
|
||||
private String pullFromLibrary;
|
||||
/** Y: even if version already has details, delete+re-pull from library */
|
||||
/** Y:即使版本已有明细,也删除后重新从库拉取 */
|
||||
private String forcePullLibrary;
|
||||
/**
|
||||
* Request-only: bind this Excel attachment (pef_id) to the saved version.psv_id
|
||||
* (not a column on prc_price_set).
|
||||
* 仅请求用:把这个 Excel 附件(pef_id)绑定到保存的版本 psv_id
|
||||
* (不是 prc_price_set 的字段)。
|
||||
*/
|
||||
private Long pefId;
|
||||
/** 请求专用:多个 Excel 附件 pef_id,保存时绑到版本(非表字段) */
|
||||
|
||||
@@ -30,14 +30,14 @@ public class PrcPriceSetDetail implements Serializable
|
||||
private String weightLabel;
|
||||
private BigDecimal weightMin;
|
||||
private BigDecimal weightMax;
|
||||
/** legacy migrate source id */
|
||||
/** 历史迁移来源ID */
|
||||
private Long productCountryId;
|
||||
private BigDecimal fvValue;
|
||||
private BigDecimal fvRegistrationFee;
|
||||
private String sizeLimit;
|
||||
private String lastMile;
|
||||
private String leadTime;
|
||||
/** display-only base from PRODUCT type when comparing discounts */
|
||||
/** 比较折扣时仅用于展示的 PRODUCT 类型基价 */
|
||||
private BigDecimal baseFreightPrice;
|
||||
private BigDecimal baseRegistrationFee;
|
||||
private BigDecimal discountValue;
|
||||
@@ -47,7 +47,7 @@ public class PrcPriceSetDetail implements Serializable
|
||||
private Date effectiveDate;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date expireDate;
|
||||
/** 0 not deleted / 1 deleted */
|
||||
/** 0 未删除 / 1 已删除 */
|
||||
public static final String IS_DEL_NO = "0";
|
||||
public static final String IS_DEL_YES = "1";
|
||||
private String isDel;
|
||||
|
||||
@@ -29,12 +29,12 @@ public class PrcPriceSetProduct implements Serializable
|
||||
private Date effectiveTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireTime;
|
||||
/** 0 draft / 3 pending / 1 effect / 2 expired */
|
||||
/** 0 草稿 / 3 待生效 / 1 生效 / 2 已失效 */
|
||||
private String status;
|
||||
/** legacy migrate source id */
|
||||
/** 历史迁移来源ID */
|
||||
private Long sourceProductId;
|
||||
private String sourceSheet;
|
||||
/** 0 not deleted / 1 deleted */
|
||||
/** 0 未删除 / 1 已删除 */
|
||||
public static final String IS_DEL_NO = "0";
|
||||
public static final String IS_DEL_YES = "1";
|
||||
private String isDel;
|
||||
|
||||
@@ -23,27 +23,27 @@ public class PrcPriceSetVersion extends BaseEntity
|
||||
private Integer versionSeq;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date effectiveTime;
|
||||
/** null = open; set to next version effective_time when that version starts */
|
||||
/** null = 未关闭;当下一版本开始时设置为该版本的 effective_time */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireTime;
|
||||
/** 0 draft / 3 pending / 1 active / 2 expired */
|
||||
/** 0 草稿 / 3 待生效 / 1 生效 / 2 已失效 */
|
||||
private String status;
|
||||
/**
|
||||
* Create-price mode for this version (column: price_build_mode).
|
||||
* SALES: PULL = product library; IMPORT = Excel + prior version; IMPORT_FRESH = Excel blank.
|
||||
* PRODUCT/REDLINE: IMPORT = copy from effect version; IMPORT_FRESH = rebuild (Excel).
|
||||
* Legacy catalog: COPY → IMPORT, old IMPORT → IMPORT_FRESH (see migrate SQL).
|
||||
* 该版本的建价模式(字段:price_build_mode)。
|
||||
* SALES:PULL = 产品库;IMPORT = Excel + 上一个版本;IMPORT_FRESH = Excel 空白。
|
||||
* PRODUCT/REDLINE:IMPORT = 从生效版本复制;IMPORT_FRESH = 重建(Excel)。
|
||||
* 历史目录:COPY → IMPORT,旧的 IMPORT → IMPORT_FRESH(见迁移 SQL)。
|
||||
*/
|
||||
private String priceBuildMode;
|
||||
/** 0 normal / 1 soft-deleted */
|
||||
/** 0 正常 / 1 软删除 */
|
||||
private String isDel;
|
||||
|
||||
private List<PrcPriceSetDetail> details;
|
||||
private List<PrcPriceSetProduct> products;
|
||||
/** query display: active product rows */
|
||||
/** 查询展示:生效的产品行数 */
|
||||
private Integer productCount;
|
||||
/** query display: active detail rows */
|
||||
/** 查询展示:生效的明细行数 */
|
||||
private Integer detailCount;
|
||||
/** query only: pending / expired / effect */
|
||||
/** 仅查询用:待生效 / 已失效 / 生效 */
|
||||
private String statusGroup;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class PrcQuoteFee implements Serializable
|
||||
/** bms_fee_type: BASIC_FREIGHT / REGISTRATION_FEE / SURCHARGE */
|
||||
private String feeType;
|
||||
private String feeName;
|
||||
/** prc_surcharge.sc_id, 0 for freight/registration */
|
||||
/** prc_surcharge.sc_id,运费/挂号费为 0 */
|
||||
private Long scId;
|
||||
private BigDecimal amount;
|
||||
private String currency;
|
||||
|
||||
@@ -16,19 +16,23 @@ public class PrcSurcharge extends BaseEntity
|
||||
private String scName;
|
||||
/** 字典 bms_fee_type */
|
||||
private String feeType;
|
||||
/** FIXED / WEIGHT / RATE */
|
||||
/** 计费方式:ALWAYS/每票都收、POSTCODE/命中邮编、ZONE/命中分区、EXTRA_KG/超重续收、WEIGHT/超重、SIZE/超尺寸、RETURN/退件、MANUAL/暂不计费 */
|
||||
private String chargeType;
|
||||
/** 渠道产品(字典 payment_product_type.dict_value) */
|
||||
private String dictValue;
|
||||
private BigDecimal chargeValue;
|
||||
private String currency;
|
||||
/** 字典 bms_round_mode:UP / DOWN / HALF_UP */
|
||||
private String roundMode;
|
||||
/** on / of */
|
||||
/** 开 / 关 */
|
||||
private String ssCode;
|
||||
/** 0未删除 1已删除 */
|
||||
public static final String IS_DEL_NO = "0";
|
||||
public static final String IS_DEL_YES = "1";
|
||||
private String isDel;
|
||||
private String countryCode;
|
||||
/** 分区/区块(对应 pay_postcode_group.block_title) */
|
||||
private String blockTitle;
|
||||
private String scNote;
|
||||
private Long tmsId;
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ public class PrcSurchargeRel extends BaseEntity
|
||||
|
||||
private Long relId;
|
||||
private Long scId;
|
||||
/** psp_id for PRODUCT/REDLINE/SALES */
|
||||
/** PRODUCT/REDLINE/SALES 时为 psp_id */
|
||||
private Long productId;
|
||||
private String productCode;
|
||||
/** psd_id for PRODUCT/REDLINE/SALES (null=all) */
|
||||
/** PRODUCT/REDLINE/SALES 时为 psd_id(null=全部) */
|
||||
private Long detailId;
|
||||
/** PRODUCT / SALES / REDLINE */
|
||||
private String priceType;
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ public class PrcWeightSectionTemplate extends BaseEntity
|
||||
|
||||
private Long wstId;
|
||||
private String wstName;
|
||||
/** on / off */
|
||||
/** 开 / 关 */
|
||||
private String ssCode;
|
||||
private String pgCode;
|
||||
private Long snId;
|
||||
|
||||
@@ -13,7 +13,7 @@ public class PrcZone implements Serializable
|
||||
|
||||
private Long zoneId;
|
||||
private Long regionId;
|
||||
/** ISO2 country code stored on prc_zone */
|
||||
/** 存储在 prc_zone 上的 ISO2 国家代码 */
|
||||
private String countryCode;
|
||||
private String zoneCode;
|
||||
private String zoneName;
|
||||
@@ -23,8 +23,8 @@ public class PrcZone implements Serializable
|
||||
private Date createTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
/** list display, not a table column */
|
||||
/** 列表展示,非表字段 */
|
||||
private Integer detailCount;
|
||||
/** list display from region */
|
||||
/** 列表展示,来源于 region */
|
||||
private String countryNameCn;
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ public class RedlineCatalogPreviewApplyRequest implements Serializable
|
||||
|
||||
private Long psvId;
|
||||
private List<BmsRedlineProduct> products;
|
||||
/** key = upper-case productCode */
|
||||
/** key = 大写后的 productCode */
|
||||
private Map<String, List<BmsRedlineProductCountry>> detailsByProductCode;
|
||||
|
||||
public Long getPsvId()
|
||||
|
||||
+4
-4
@@ -15,17 +15,17 @@ public class AddIncomeBillFeeRequest implements Serializable
|
||||
@NotBlank(message = "billId required")
|
||||
private String billId;
|
||||
|
||||
/** Surcharge config id (prc_surcharge.sc_id). */
|
||||
/** 附加费配置ID(prc_surcharge.sc_id)。 */
|
||||
@NotNull(message = "scId required")
|
||||
private Long scId;
|
||||
|
||||
/** Dict bms_fee_type; empty = use surcharge.feeType. */
|
||||
/** 字典 bms_fee_type;为空则使用 surcharge.feeType。 */
|
||||
private String feeType;
|
||||
|
||||
/** Empty = calculate from surcharge chargeType. */
|
||||
/** 为空则按附加费的 chargeType 计算。 */
|
||||
private BigDecimal amount;
|
||||
|
||||
/** Default CNY. */
|
||||
/** 默认 CNY。 */
|
||||
private String currency;
|
||||
|
||||
private String remark;
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ public class BatchAddIncomeBillFeePreview implements Serializable
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int matchCount;
|
||||
/** First N rows for UI preview. */
|
||||
/** 供界面预览的前 N 行。 */
|
||||
private List<PrcOrderBill> samples = new ArrayList<PrcOrderBill>();
|
||||
|
||||
public int getMatchCount()
|
||||
|
||||
+12
-12
@@ -15,13 +15,13 @@ public class BatchAddIncomeBillFeeRequest implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** ALL = list filters; SELECTED = billIds only. */
|
||||
/** ALL = 列表筛选条件;SELECTED = 仅 billIds。 */
|
||||
private String scopeMode;
|
||||
|
||||
/** Selected bill ids when scopeMode=SELECTED. */
|
||||
/** scopeMode=SELECTED 时选中的账单ID。 */
|
||||
private List<String> billIds;
|
||||
|
||||
/** Same filters as billing list search. */
|
||||
/** 与计费列表搜索相同的筛选条件。 */
|
||||
private List<String> orderNos;
|
||||
private List<String> trackingNos;
|
||||
private List<String> transferNos;
|
||||
@@ -31,33 +31,33 @@ public class BatchAddIncomeBillFeeRequest implements Serializable
|
||||
private List<String> productCodes;
|
||||
private String countryCode;
|
||||
private String creditArrearsStatus;
|
||||
/** Same as list filter order_status IN (...). */
|
||||
/** 与列表筛选 order_status IN (...) 相同。 */
|
||||
private List<Integer> orderStatuses;
|
||||
/** Same as list filter billing_fail_status IN (...). */
|
||||
/** 与列表筛选 billing_fail_status IN (...) 相同。 */
|
||||
private List<Integer> billingFailStatuses;
|
||||
|
||||
/** Billing time range (biz_date), inclusive; format yyyy-MM-dd. */
|
||||
/** 计费时间范围(biz_date),含边界;格式 yyyy-MM-dd。 */
|
||||
private String beginBizDate;
|
||||
private String endBizDate;
|
||||
|
||||
/** Receive/pickup time range (pickup_date), inclusive. */
|
||||
/** 收货/提货时间范围(pickup_date),含边界。 */
|
||||
private String beginPickupDate;
|
||||
private String endPickupDate;
|
||||
|
||||
/** Surcharge config id (prc_surcharge.sc_id). */
|
||||
/** 附加费配置ID(prc_surcharge.sc_id)。 */
|
||||
@NotNull(message = "scId required")
|
||||
private Long scId;
|
||||
|
||||
/** Dict bms_fee_type; empty = use surcharge.feeType. */
|
||||
/** 字典 bms_fee_type;为空则使用 surcharge.feeType。 */
|
||||
private String feeType;
|
||||
|
||||
/**
|
||||
* Fixed amount for every matched bill.
|
||||
* Empty = calculate per bill from surcharge chargeType / weight / freight.
|
||||
* 每个匹配账单的固定金额。
|
||||
* 为空则按附加费的 chargeType / 重量 / 运费逐票计算。
|
||||
*/
|
||||
private BigDecimal amount;
|
||||
|
||||
/** Default CNY. */
|
||||
/** 默认 CNY。 */
|
||||
private String currency;
|
||||
|
||||
private String remark;
|
||||
|
||||
+5
-5
@@ -12,14 +12,14 @@ public class BillingTaskRetryByFilterRequest implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Customer code (exact match) */
|
||||
/** 客户编码(精确匹配) */
|
||||
private String customerCode;
|
||||
/** Bill time start yyyy-MM-dd */
|
||||
/** 计费时间起 yyyy-MM-dd */
|
||||
private String beginTime;
|
||||
/** Bill time end yyyy-MM-dd */
|
||||
/** 计费时间止 yyyy-MM-dd */
|
||||
private String endTime;
|
||||
/** Optional: FAILED / WAITING / SUCCESS / ALL; default ALL */
|
||||
/** 可选:FAILED / WAITING / SUCCESS / ALL;默认 ALL */
|
||||
private String successStatus;
|
||||
/** Optional batch size; server clamps to 20~200, default 50 */
|
||||
/** 可选批大小;服务端限制在 20~200,默认 50 */
|
||||
private Integer batchSize;
|
||||
}
|
||||
|
||||
+4
-4
@@ -18,13 +18,13 @@ public class BillingTaskSubmitRequest implements Serializable
|
||||
private String transferNo;
|
||||
private String trackingNo;
|
||||
private String orderNo;
|
||||
/** Optional customer code; also filled from Oracle when task runs */
|
||||
/** 可选客户编码;任务执行时也会从 Oracle 补全 */
|
||||
private String customerCode;
|
||||
/** Billing time from mobile; persisted to prc_billing_task.bill_time */
|
||||
/** 移动端传入的计费时间;写入 prc_billing_task.bill_time */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date billTime;
|
||||
/** Optional: PENDING / REBILL / REFUND. Empty = auto PENDING or REBILL. */
|
||||
/** 可选:PENDING / REBILL / REFUND。为空则自动取 PENDING 或 REBILL。 */
|
||||
private String billType;
|
||||
/** Optional body apiCode when header X-Api-Code missing */
|
||||
/** 可选请求体 apiCode,请求头缺少 X-Api-Code 时使用 */
|
||||
private String apiCode;
|
||||
}
|
||||
|
||||
+3
-3
@@ -10,7 +10,7 @@ public class ChargeWeightTemplateRequest implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** true = fill orderNo/customer from current list filters */
|
||||
/** true = 按当前列表筛选条件填充 orderNo/customer */
|
||||
private boolean fillByFilter;
|
||||
|
||||
private List<String> orderNos;
|
||||
@@ -21,11 +21,11 @@ public class ChargeWeightTemplateRequest implements Serializable
|
||||
private List<String> customerCodes;
|
||||
private List<String> productCodes;
|
||||
private String creditArrearsStatus;
|
||||
/** Same as list filter order_status IN (...). */
|
||||
/** 与列表筛选 order_status IN (...) 相同。 */
|
||||
private List<Integer> orderStatuses;
|
||||
private String beginBizDate;
|
||||
private String endBizDate;
|
||||
/** Receive/pickup time range (pickup_date), inclusive. */
|
||||
/** 收货/提货时间范围(pickup_date),含边界。 */
|
||||
private String beginPickupDate;
|
||||
private String endPickupDate;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.bms.price.domain.vo;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 订单列表计费失败原因弹窗数据。
|
||||
*/
|
||||
@Data
|
||||
public class OrderBillFailReasonVo implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String billId;
|
||||
private String orderNo;
|
||||
private String transferNo;
|
||||
private String trackingNo;
|
||||
|
||||
private String customerCode;
|
||||
private String customerName;
|
||||
private String productCode;
|
||||
private String productName;
|
||||
private String countryCode;
|
||||
private String countryName;
|
||||
private String zoneCode;
|
||||
private String zoneName;
|
||||
private String destPostcode;
|
||||
|
||||
private Long priceSetId;
|
||||
private String priceSetCode;
|
||||
private String priceSetName;
|
||||
private String setType;
|
||||
private Long psvId;
|
||||
private String versionNo;
|
||||
/** 价卡周期的版本生效时间。 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date effectiveTime;
|
||||
/** 价卡周期的版本失效时间;null = 未关闭。 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireTime;
|
||||
private Long psdId;
|
||||
|
||||
private BigDecimal weight;
|
||||
private BigDecimal chargeWeight;
|
||||
private BigDecimal weightMin;
|
||||
private BigDecimal weightMax;
|
||||
private String weightLabel;
|
||||
private BigDecimal freightRate;
|
||||
private BigDecimal freightAmount;
|
||||
private BigDecimal incomeTotal;
|
||||
private String currency;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date bizDate;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date pickupDate;
|
||||
|
||||
private Integer billingFailStatus;
|
||||
private String billingFailStatusLabel;
|
||||
/** 原始落库的失败文本。 */
|
||||
private String errorMessage;
|
||||
/** 模板化后的可读失败原因。 */
|
||||
private String failReasonDetail;
|
||||
}
|
||||
+4
-2
@@ -43,8 +43,10 @@ public class PrcBillingBillListExportRow implements Serializable
|
||||
private BigDecimal incomeTotal;
|
||||
@Excel(name = "应付总额", scale = 2, width = 14)
|
||||
private BigDecimal payTotal;
|
||||
@Excel(name = "计价来源", width = 16)
|
||||
private String matchSourceLabel;
|
||||
@Excel(name = "渠道编码", width = 16)
|
||||
private String productFloorCode;
|
||||
@Excel(name = "渠道名称", width = 20)
|
||||
private String productFloor;
|
||||
@Excel(name = "订单状态", width = 12)
|
||||
private String orderStatusLabel;
|
||||
@Excel(name = "计费状态", width = 14)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user