统一修改ruoyi->bms

This commit is contained in:
caojia
2026-09-28 10:02:38 +08:00
parent 341afc4111
commit 0408b6c35e
639 changed files with 2776 additions and 2807 deletions
@@ -0,0 +1,34 @@
package com.bms;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 启动程序
*
* @author bms
*/
@EnableAsync
@EnableScheduling
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class BmsApplication
{
public static void main(String[] args)
{
// System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(BmsApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 启动成功 ლ(´ڡ`ლ)゙ \n" +
" .-------. ____ __ \n" +
" | _ _ \\ \\ \\ / / \n" +
" | ( ' ) | \\ _. / ' \n" +
" |(_ o _) / _( )_ .' \n" +
" | (_,_).' __ ___(_ o _)' \n" +
" | |\\ \\ | || |(_,_)' \n" +
" | | \\ `' /| `-' / \n" +
" | | \\ / \\ / \n" +
" ''-' `'-' `-..-' ");
}
}
@@ -0,0 +1,18 @@
package com.bms;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* web容器中进行部署
*
* @author bms
*/
public class BmsServletInitializer extends SpringBootServletInitializer
{
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
{
return application.sources(BmsApplication.class);
}
}
@@ -0,0 +1,87 @@
package com.bms.web.controller.api;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.exception.ServiceException;
import com.bms.common.utils.StringUtils;
import com.bms.price.search.EsBizLogSupport;
import com.bms.price.search.IBillSearchStore;
import com.bms.web.controller.price.BillingController;
/**
* Persist quote/bill API failures to Elasticsearch (validation, JSON parse, business, unexpected).
* Uses {@link EsBizLogSupport#saveApiFail} so service/controller can also call without duplicate docs.
*/
@Order(0)
@RestControllerAdvice(assignableTypes = { TmsPriceController.class, BillingController.class })
public class PriceSyncEsLogAdvice
{
@Autowired
private IBillSearchStore billSearchStore;
@ExceptionHandler(MethodArgumentNotValidException.class)
public AjaxResult handleValid(MethodArgumentNotValidException e, HttpServletRequest request)
{
String message = e.getBindingResult().getFieldError() == null
? e.getMessage()
: e.getBindingResult().getFieldError().getDefaultMessage();
saveFail(request, e.getBindingResult().getTarget(), message);
return AjaxResult.error(message);
}
@ExceptionHandler(BindException.class)
public AjaxResult handleBind(BindException e, HttpServletRequest request)
{
String message = e.getAllErrors().isEmpty() ? e.getMessage() : e.getAllErrors().get(0).getDefaultMessage();
saveFail(request, e.getTarget(), message);
return AjaxResult.error(message);
}
@ExceptionHandler(HttpMessageNotReadableException.class)
public AjaxResult handleNotReadable(HttpMessageNotReadableException e, HttpServletRequest request)
{
String message = EsBizLogSupport.errorText(e);
saveFail(request, null, message);
return AjaxResult.error(message);
}
@ExceptionHandler(ServiceException.class)
public AjaxResult handleService(ServiceException e, HttpServletRequest request)
{
saveFail(request, null, e);
Integer code = e.getCode();
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
}
@ExceptionHandler(Exception.class)
public AjaxResult handleAny(Exception e, HttpServletRequest request)
{
saveFail(request, null, e);
return AjaxResult.error(EsBizLogSupport.errorText(e));
}
private void saveFail(HttpServletRequest request, Object boundTarget, Throwable e)
{
if (!EsBizLogSupport.isPriceSyncWriteApi(request))
{
return;
}
EsBizLogSupport.saveApiFail(billSearchStore, request, boundTarget, e);
}
private void saveFail(HttpServletRequest request, Object boundTarget, String message)
{
if (!EsBizLogSupport.isPriceSyncWriteApi(request))
{
return;
}
EsBizLogSupport.saveApiFail(billSearchStore, request, boundTarget, message);
}
}
@@ -0,0 +1,59 @@
package com.bms.web.controller.api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
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.common.annotation.Anonymous;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.exception.ServiceException;
import com.bms.common.utils.StringUtils;
import com.bms.framework.config.properties.TmsApiProperties;
import com.bms.price.domain.vo.TmsOrderBillStatusResult;
import com.bms.price.service.IPrcOrderBillService;
/**
* TMS 订单客户状态查询接口。
*
* <p>该接口不要求登录 Token,但必须携带 X-Api-Code。
* identifier 会按订单号、运单号、转单号进行精确匹配。</p>
*/
@Anonymous
@RestController
@RequestMapping("/api/tms/order-bill")
public class TmsOrderBillStatusController extends BaseController {
private static final String HEADER_API_CODE = "X-Api-Code";
@Autowired
private IPrcOrderBillService prcOrderBillService;
@Autowired
private TmsApiProperties tmsApiProperties;
/**
* 查询订单对应客户是否正常、额度是否欠费。
*
* <p>接口只返回查询事实和状态字段,不判断上游是否继续处理。</p>
*/
@GetMapping("/status")
public AjaxResult status(
@RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode,
@RequestParam(value = "identifier", required = false) String identifier) {
assertApiCode(headerApiCode);
TmsOrderBillStatusResult result = prcOrderBillService.queryTmsOrderBillStatus(identifier);
return success(result);
}
private void assertApiCode(String headerApiCode) {
String expected = tmsApiProperties.getApiCode();
if (StringUtils.isEmpty(expected)) {
throw new ServiceException("API_CODE_NOT_CONFIGURED: bms.tms.api-code is empty");
}
if (StringUtils.isEmpty(headerApiCode) || !expected.equals(headerApiCode.trim())) {
throw new ServiceException("API_CODE_INVALID: missing or invalid X-Api-Code");
}
}
}
@@ -0,0 +1,171 @@
package com.bms.web.controller.api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
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.RequestBody;
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.common.annotation.Anonymous;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.exception.ServiceException;
import com.bms.common.utils.StringUtils;
import com.bms.framework.config.properties.TmsApiProperties;
import com.bms.price.domain.vo.BillingTaskSubmitRequest;
import com.bms.price.domain.vo.TmsPriceBillRequest;
import com.bms.price.domain.vo.TmsPriceQuoteRequest;
import com.bms.price.domain.vo.TmsPriceQuoteResult;
import com.bms.price.domain.PrcBillingTask;
import com.bms.price.search.EsBizLogSupport;
import com.bms.price.search.EsLogType;
import com.bms.price.search.IBillSearchStore;
import com.bms.price.service.IPrcBillingTaskService;
import com.bms.price.service.IPrcOrderBillService;
import com.bms.price.service.ITmsPriceQuoteService;
/**
* TMS open API: freight quote.
*
* POST /api/tms/price/quote
* Header X-Api-Code must equal bms.tms.api-code
*
* @author bms
*/
@Anonymous
@RestController
@RequestMapping("/api/tms/price")
public class TmsPriceController extends BaseController
{
public static final String HEADER_API_CODE = "X-Api-Code";
@Autowired
private ITmsPriceQuoteService tmsPriceQuoteService;
@Autowired
private IPrcOrderBillService prcOrderBillService;
@Autowired
private IPrcBillingTaskService prcBillingTaskService;
@Autowired
private TmsApiProperties tmsApiProperties;
@Autowired
private IBillSearchStore billSearchStore;
/**
* Quote by customer + product + country + weight (+ destPostcode when country has zones).
* Zone resolve: destPostcode -> prc_zone_detail/prc_zone -> bms_product_country.zone_code.
* Match: customer package FIXED &gt; DISCOUNT &gt; product default.
*/
@PostMapping("/quote")
public AjaxResult quote(
@RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode,
@Validated @RequestBody TmsPriceQuoteRequest request)
{
try
{
assertApiCode(headerApiCode, request == null ? null : request.getApiCode());
}
catch (ServiceException e)
{
EsBizLogSupport.saveFail(billSearchStore, EsLogType.QUOTE, EsBizLogSupport.quoteFailBizId(request),
request, e);
throw e;
}
TmsPriceQuoteResult result = tmsPriceQuoteService.quote(request);
return success(result);
}
/**
* 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.
*/
@PostMapping("/bill")
public AjaxResult bill(
@RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode,
@Validated @RequestBody TmsPriceBillRequest request)
{
try
{
assertApiCode(headerApiCode, request == null ? null : request.getApiCode());
}
catch (ServiceException e)
{
EsBizLogSupport.saveFail(billSearchStore, EsLogType.BILL, EsBizLogSupport.billFailBizId(request), request, e);
throw e;
}
TmsPriceQuoteResult result = prcOrderBillService.bill(request);
return success(result);
}
/**
* Warehouse inbound: create billing task (PENDING/REBILL/REFUND) and publish MQ.
* Consumer queries Oracle by transferNo/orderNo then inserts prc_order_bill.
* POST /api/tms/price/billing-task
*/
@PostMapping("/billing-task")
public AjaxResult submitBillingTask(
@RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode,
@RequestBody BillingTaskSubmitRequest request)
{
assertApiCode(headerApiCode, request == null ? null : request.getApiCode());
PrcBillingTask task = prcBillingTaskService.submitInbound(request);
return success(task);
}
/**
* Query billing task writeback status.
* GET /api/tms/price/billing-task/{taskId}
*/
@GetMapping("/billing-task/{taskId}")
public AjaxResult getBillingTask(
@RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode,
@PathVariable("taskId") Long taskId,
@RequestParam(value = "apiCode", required = false) String apiCode)
{
assertApiCode(headerApiCode, apiCode);
return success(prcBillingTaskService.getById(taskId));
}
/**
* Query order bill by orderNo after sync.
*/
@GetMapping("/bill/by-order/{orderNo}")
public AjaxResult getBillByOrder(
@RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode,
@PathVariable("orderNo") String orderNo,
@RequestParam(value = "apiCode", required = false) String apiCode)
{
assertApiCode(headerApiCode, apiCode);
return success(prcOrderBillService.getBillByOrderNo(orderNo));
}
private void assertApiCode(String headerApiCode, String bodyApiCode)
{
String expected = tmsApiProperties.getApiCode();
if (StringUtils.isEmpty(expected))
{
throw new ServiceException("API_CODE_NOT_CONFIGURED: bms.tms.api-code is empty");
}
String actual = StringUtils.isNotEmpty(headerApiCode) ? headerApiCode.trim() : null;
if (StringUtils.isEmpty(actual) && StringUtils.isNotEmpty(bodyApiCode))
{
actual = bodyApiCode.trim();
}
if (StringUtils.isEmpty(actual))
{
throw new ServiceException("API_CODE_REQUIRED: missing header X-Api-Code (or body apiCode)");
}
if (!expected.equals(actual))
{
throw new ServiceException("API_CODE_INVALID: api identification code mismatch");
}
}
}
@@ -0,0 +1,33 @@
package com.bms.web.controller.ar;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
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.mapper.ArBankAccountMapper;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
/**
* 公司收款银行账户(下拉选项,供登记收款等场景选择)。
*/
@RestController
@RequestMapping("/ar/bank-account")
public class ArBankAccountController extends BaseController {
@Autowired
private ArBankAccountMapper arBankAccountMapper;
/**
* 启用账户列表;可按币种过滤,默认账户优先。
*/
@GetMapping("/options")
public AjaxResult options(@RequestParam(value = "currency", required = false) String currency) {
List<ArBankAccount> list = arBankAccountMapper.selectEnabledList(currency);
return AjaxResult.success(list);
}
}
@@ -0,0 +1,286 @@
package com.bms.web.controller.ar;
import java.util.List;
import java.util.Date;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.validation.annotation.Validated;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.web.bind.annotation.InitBinder;
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.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.ArBillGenerateRequest;
import com.bms.ar.domain.vo.ArBillQuery;
import com.bms.ar.service.IArBillService;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.exception.ServiceException;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.StringUtils;
import com.bms.common.utils.uuid.IdUtils;
import com.bms.price.domain.PrcOrderBill;
/**
* 应收账单 HTTP 接口层。
*
* <p>负责账单列表、账单生成、确认/作废、异步导出、日志和详情分页接口;
* 具体业务规则统一下沉到 {@code IArBillService}。</p>
*/
@RestController
@RequestMapping("/ar/bill")
@Validated
public class ArBillController extends BaseController {
@Autowired
private IArBillService billService;
/**
* 查询参数中的日期必须严格按 yyyy-MM-dd 解析,禁止非法日期自动滚动到其他日期。
*/
@InitBinder
public void initDateBinder(WebDataBinder binder) {
java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd");
formatter.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(formatter, true));
}
/**
* 查询账单列表;分页参数由 BaseController.startPage 交给 MyBatis PageHelper 处理。
*/
@PreAuthorize("@ss.hasPermi('ar:bill:list')")
@GetMapping("/list")
public TableDataInfo list(ArBillQuery query, HttpServletRequest request) {
rejectMultiValueParameter(request, "customerId");
startPage();
List<ArBill> list = billService.selectArBillList(query);
return getDataTable(list);
}
/**
* 查询账单头信息;账单明细通过独立分页接口按需加载。
*/
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
@GetMapping("/{arBillId}")
public AjaxResult getInfo(@PathVariable Long arBillId) {
return success(billService.selectArBillById(arBillId));
}
/**
* 分页查询账单明细,避免详情页一次性加载全部订单。
*/
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
@GetMapping("/{arBillId}/details")
public TableDataInfo details(@PathVariable Long arBillId) {
startPage();
return getDataTable(billService.selectArBillDetails(arBillId));
}
/**
* 查询单条账单明细的费用明细,按展开行懒加载。
*/
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
@GetMapping("/{arBillId}/details/{detailId}/fees")
public AjaxResult detailFees(@PathVariable Long arBillId, @PathVariable Long detailId) {
List<ArBillDetailFee> fees = billService.selectArBillDetailFees(arBillId, detailId);
return success(fees);
}
/**
* 查询指定币种的公司默认收款账户,账号返回脱敏值。
*/
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
@GetMapping("/default-bank-account")
public AjaxResult defaultBankAccount(@RequestParam(required = false, defaultValue = "CNY") String currency) {
ArBankAccount account = billService.getDefaultBankAccount(currency);
return success(account);
}
/**
* 查询生成账单前的订单计费结果。
*/
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
@GetMapping("/generate/preview")
public AjaxResult preview(ArBillGenerateQuery query, HttpServletRequest request) {
rejectMultiValueParameter(request, "customerId");
startPage();
List<PrcOrderBill> list = billService.previewBill(query);
TableDataInfo pageData = getDataTable(list);
AjaxResult result = success();
result.put("rows", pageData.getRows());
result.put("total", pageData.getTotal());
result.put("totalAmount", billService.sumPreviewBill(query));
return result;
}
/**
* 创建账单头和异步生成任务,接口不等待全部订单明细处理完成。
*/
@Log(title = "应收账单", businessType = BusinessType.INSERT)
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
@PostMapping("/generate")
public AjaxResult generate(@Valid @RequestBody ArBillGenerateRequest request,
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) {
String requestId = requestId(request.getRequestId(), headerRequestId);
ArBillGenerateTask task = billService.generateBill(request, getUsername(), requestId);
return success(task).put("request_id", requestId);
}
/**
* 查询单个应收账单异步生成任务。
*/
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
@GetMapping("/generate/task/{taskId}")
public AjaxResult generateTaskInfo(@PathVariable Long taskId) {
return success(billService.selectGenerateTask(taskId));
}
/**
* 查询当前用户仍在处理中的应收账单生成任务。
*/
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
@GetMapping("/generate/tasks/active")
public AjaxResult activeGenerateTasks() {
return success(billService.selectActiveGenerateTasks(getUsername()));
}
/**
* 请求取消应收账单异步生成任务。
*/
@Log(title = "应收账单", businessType = BusinessType.UPDATE)
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
@PostMapping("/generate/task/{taskId}/cancel")
public AjaxResult cancelGenerateTask(@PathVariable Long taskId,
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) {
String requestId = requestId(null, headerRequestId);
return success(billService.cancelGenerateTask(taskId, getUsername(), requestId)).put("request_id", requestId);
}
/**
* 整单重新发起应收账单异步生成任务。
*/
@Log(title = "应收账单", businessType = BusinessType.INSERT)
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
@PostMapping("/generate/task/{taskId}/retry")
public AjaxResult retryGenerateTask(@PathVariable Long taskId,
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) {
String requestId = requestId(null, headerRequestId);
return success(billService.retryGenerateTask(taskId, getUsername(), requestId)).put("request_id", requestId);
}
/**
* 确认已生成完成的账单,使其进入可核销状态。
*/
@Log(title = "应收账单", businessType = BusinessType.UPDATE)
@PreAuthorize("@ss.hasPermi('ar:bill:confirm')")
@PutMapping("/{arBillId}/confirm")
public AjaxResult confirm(@PathVariable Long arBillId, @RequestBody(required = false) ArBillActionRequest request,
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) {
String requestId = requestId(request == null ? null : request.getRequestId(), headerRequestId);
return success(billService.confirmBill(arBillId, request, getUsername(), requestId)).put("request_id", requestId);
}
/**
* 作废尚未发生核销的账单;账单明细保留作为历史快照。
*/
@Log(title = "应收账单", businessType = BusinessType.UPDATE)
@PreAuthorize("@ss.hasPermi('ar:bill:void')")
@PutMapping("/{arBillId}/void")
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);
}
/**
* 仅提交导出任务;前端通过导出任务状态接口轮询并在完成后下载。
*/
@Log(title = "应收账单导出", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('ar:bill:export')")
@PostMapping("/{arBillId}/export")
public AjaxResult export(@PathVariable Long arBillId,
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) {
String requestId = requestId(null, headerRequestId);
return success(billService.exportBill(arBillId, getUsername(), requestId)).put("request_id", requestId);
}
/**
* 查询当前账单的导出历史和任务状态。
*/
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
@GetMapping("/{arBillId}/exports")
public AjaxResult exports(@PathVariable Long arBillId) {
return success(billService.selectExports(arBillId));
}
/**
* 查询单个账单导出任务状态,供前端轮询。
*/
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
@GetMapping("/export/{exportId}")
public AjaxResult exportInfo(@PathVariable Long exportId) {
return success(billService.selectExport(exportId));
}
/**
* 下载已成功生成的导出文件;文件内容由服务层从临时文件或配置存储读取。
*/
@PreAuthorize("@ss.hasPermi('ar:bill:export')")
@GetMapping("/export/{exportId}/download")
public void download(@PathVariable Long exportId, HttpServletResponse response) throws Exception {
billService.downloadExport(exportId, response);
}
/**
* 分页查询账单操作日志。
*/
@PreAuthorize("@ss.hasPermi('ar:log:list')")
@GetMapping("/log/list")
public TableDataInfo logs(ArBillLog query) {
startPage();
return getDataTable(billService.selectLogList(query));
}
/**
* 解析接口幂等号:优先使用请求体,再使用请求头,最后生成新的 UUID。
* 生成账单、确认、作废和导出都依赖该值避免重复提交。
*/
private String requestId(String bodyRequestId, String headerRequestId) {
if (StringUtils.isNotEmpty(bodyRequestId)) return bodyRequestId;
if (StringUtils.isNotEmpty(headerRequestId)) return headerRequestId;
return IdUtils.fastSimpleUUID();
}
/**
* 禁止把 customerId 以数组参数或重复参数提交,避免参数绑定失败后悄悄退化为无客户条件查询。
*/
private void rejectMultiValueParameter(HttpServletRequest request, String parameterName) {
if (request.getParameterMap().containsKey(parameterName + "[]")) {
throw new ServiceException("查询参数[" + parameterName + "]格式不正确,请传入单个客户ID");
}
String[] values = request.getParameterValues(parameterName);
if (values != null && values.length > 1) {
throw new ServiceException("查询参数[" + parameterName + "]只能传入一个值");
}
}
}
@@ -0,0 +1,94 @@
package com.bms.web.controller.common;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import jakarta.annotation.Resource;
import javax.imageio.ImageIO;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.code.kaptcha.Producer;
import com.bms.common.config.BmsConfig;
import com.bms.common.constant.CacheConstants;
import com.bms.common.constant.Constants;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.redis.RedisCache;
import com.bms.common.utils.sign.Base64;
import com.bms.common.utils.uuid.IdUtils;
import com.bms.system.service.ISysConfigService;
/**
* 验证码操作处理
*
* @author bms
*/
@RestController
public class CaptchaController
{
@Resource(name = "captchaProducer")
private Producer captchaProducer;
@Resource(name = "captchaProducerMath")
private Producer captchaProducerMath;
@Autowired
private RedisCache redisCache;
@Autowired
private ISysConfigService configService;
/**
* 生成验证码
*/
@GetMapping("/captchaImage")
public AjaxResult getCode(HttpServletResponse response) throws IOException
{
AjaxResult ajax = AjaxResult.success();
boolean captchaEnabled = configService.selectCaptchaEnabled();
ajax.put("captchaEnabled", captchaEnabled);
if (!captchaEnabled)
{
return ajax;
}
// 保存验证码信息
String uuid = IdUtils.simpleUUID();
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
String capStr = null, code = null;
BufferedImage image = null;
// 生成验证码
String captchaType = BmsConfig.getCaptchaType();
if ("math".equals(captchaType))
{
String capText = captchaProducerMath.createText();
capStr = capText.substring(0, capText.lastIndexOf("@"));
code = capText.substring(capText.lastIndexOf("@") + 1);
image = captchaProducerMath.createImage(capStr);
}
else if ("char".equals(captchaType))
{
capStr = code = captchaProducer.createText();
image = captchaProducer.createImage(capStr);
}
redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
// 转换流信息写出
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
try
{
ImageIO.write(image, "jpg", os);
}
catch (IOException e)
{
return AjaxResult.error(e.getMessage());
}
ajax.put("uuid", uuid);
ajax.put("img", Base64.encode(os.toByteArray()));
return ajax;
}
}
@@ -0,0 +1,162 @@
package com.bms.web.controller.common;
import java.util.ArrayList;
import java.util.List;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.bms.common.config.BmsConfig;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.utils.StringUtils;
import com.bms.common.utils.file.FileUploadUtils;
import com.bms.common.utils.file.FileUtils;
import com.bms.framework.config.ServerConfig;
/**
* 通用请求处理
*
* @author bms
*/
@RestController
@RequestMapping("/common")
public class CommonController
{
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
@Autowired
private ServerConfig serverConfig;
private static final String FILE_DELIMITER = ",";
/**
* 通用下载请求
*
* @param fileName 文件名称
* @param delete 是否删除
*/
@GetMapping("/download")
public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
{
try
{
if (!FileUtils.checkAllowDownload(fileName))
{
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
}
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
String filePath = BmsConfig.getDownloadPath() + fileName;
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, realFileName);
FileUtils.writeBytes(filePath, response.getOutputStream());
if (delete)
{
FileUtils.deleteFile(filePath);
}
}
catch (Exception e)
{
log.error("下载文件失败", e);
}
}
/**
* 通用上传请求(单个)
*/
@PostMapping("/upload")
public AjaxResult uploadFile(MultipartFile file) throws Exception
{
try
{
// 上传文件路径
String filePath = BmsConfig.getUploadPath();
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
AjaxResult ajax = AjaxResult.success();
ajax.put("url", url);
ajax.put("fileName", fileName);
ajax.put("newFileName", FileUtils.getName(fileName));
ajax.put("originalFilename", file.getOriginalFilename());
return ajax;
}
catch (Exception e)
{
return AjaxResult.error(e.getMessage());
}
}
/**
* 通用上传请求(多个)
*/
@PostMapping("/uploads")
public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception
{
try
{
// 上传文件路径
String filePath = BmsConfig.getUploadPath();
List<String> urls = new ArrayList<String>();
List<String> fileNames = new ArrayList<String>();
List<String> newFileNames = new ArrayList<String>();
List<String> originalFilenames = new ArrayList<String>();
for (MultipartFile file : files)
{
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
urls.add(url);
fileNames.add(fileName);
newFileNames.add(FileUtils.getName(fileName));
originalFilenames.add(file.getOriginalFilename());
}
AjaxResult ajax = AjaxResult.success();
ajax.put("urls", StringUtils.join(urls, FILE_DELIMITER));
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMITER));
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMITER));
ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMITER));
return ajax;
}
catch (Exception e)
{
return AjaxResult.error(e.getMessage());
}
}
/**
* 本地资源通用下载
*/
@GetMapping("/download/resource")
public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
throws Exception
{
try
{
if (!FileUtils.checkAllowDownload(resource))
{
throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
}
// 本地资源路径
String localPath = BmsConfig.getProfile();
// 数据库资源地址
String downloadPath = localPath + FileUtils.stripPrefix(resource);
// 下载名称
String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, downloadName);
FileUtils.writeBytes(downloadPath, response.getOutputStream());
}
catch (Exception e)
{
log.error("下载文件失败", e);
}
}
}
@@ -0,0 +1,225 @@
package com.bms.web.controller.customer.customerinfo;
import java.util.List;
import com.bms.customer.customerinfo.domain.BmsCustomerAttachment;
import com.bms.customer.customerinfo.domain.BmsCustomerCompany;
import com.bms.customer.customerinfo.domain.BmsCustomerContact;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.enums.BusinessType;
import com.bms.customer.customerinfo.domain.BmsCustomer;
import com.bms.customer.customerinfo.service.IBmsCustomerService;
import com.bms.common.utils.poi.ExcelUtil;
import com.bms.common.core.page.TableDataInfo;
/**
* 客户管理Controller
*
* @author bms
* @date 2026-09-08
*/
@RestController
@RequestMapping("/customersupplier/customerinfo")
public class BmsCustomerController extends BaseController
{
@Autowired
private IBmsCustomerService bmsCustomerService;
/**
* 查询客户信息列表
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/list")
public TableDataInfo list(BmsCustomer bmsCustomer)
{
startPage();
List<BmsCustomer> list = bmsCustomerService.selectBmsCustomerList(bmsCustomer);
return getDataTable(list);
}
/**
* 导出客户信息列表
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:export')")
@Log(title = "客户信息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, BmsCustomer bmsCustomer)
{
List<BmsCustomer> list = bmsCustomerService.selectBmsCustomerList(bmsCustomer);
ExcelUtil<BmsCustomer> util = new ExcelUtil<>(BmsCustomer.class);
util.exportExcel(response, list, "客户主数据");
}
/**
* 获取客户信息详细信息
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:query')")
@GetMapping(value = "/{customerId}")
public AjaxResult getInfo(@PathVariable("customerId") Long customerId)
{
return success(bmsCustomerService.selectBmsCustomerByCustomerId(customerId));
}
/**
* 新增客户信息
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:add')")
@Log(title = "客户信息", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BmsCustomer bmsCustomer)
{
bmsCustomerService.insertBmsCustomer(bmsCustomer);
// 返回新主键,供前端新增成功后停留在编辑页继续维护子表/绑定产品
AjaxResult ajax = AjaxResult.success("新增成功");
ajax.put("customerId", bmsCustomer.getCustomerId());
return ajax;
}
/**
* 修改客户信息
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:edit')")
@Log(title = "客户信息", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BmsCustomer bmsCustomer)
{
return toAjax(bmsCustomerService.updateBmsCustomer(bmsCustomer));
}
/**
* 删除客户信息
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:remove')")
@Log(title = "客户信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{customerIds}")
public AjaxResult remove(@PathVariable Long[] customerIds)
{
return toAjax(bmsCustomerService.deleteBmsCustomerByCustomerIds(customerIds));
}
/**
* 查看附件列表数据
*/
// @PreAuthorize("")
@GetMapping("/attachment/list")
public TableDataInfo showattachmentlist(BmsCustomerAttachment bmsCustomerAttachment)
{
startPage();
List<BmsCustomerAttachment> list = bmsCustomerService.selectBmsCustomerAttachmentList(bmsCustomerAttachment);
return getDataTable(list);
}
/**
* 查询客户公司主体列表
*/
@GetMapping("/company/list")
public TableDataInfo companyList(BmsCustomerCompany bmsCustomerCompany)
{
startPage();
List<BmsCustomerCompany> list = bmsCustomerService.selectBmsCustomerCompanyList(bmsCustomerCompany);
return getDataTable(list);
}
/**
* 获取客户公司主体详细信息
*/
@GetMapping("/company/{companyId}")
public AjaxResult getCompanyInfo(@PathVariable("companyId") Long companyId)
{
return success(bmsCustomerService.selectBmsCustomerCompanyByCompanyId(companyId));
}
/**
* 新增客户公司主体(可关联客户主表,可不关联)
*/
@Log(title = "客户公司主体", businessType = BusinessType.INSERT)
@PostMapping("/company")
public AjaxResult addCompany(@RequestBody BmsCustomerCompany bmsCustomerCompany)
{
return toAjax(bmsCustomerService.insertBmsCustomerCompany(bmsCustomerCompany));
}
/**
* 修改客户公司主体
*/
@Log(title = "客户公司主体", businessType = BusinessType.UPDATE)
@PutMapping("/company")
public AjaxResult editCompany(@RequestBody BmsCustomerCompany bmsCustomerCompany)
{
return toAjax(bmsCustomerService.updateBmsCustomerCompany(bmsCustomerCompany));
}
/**
* 删除客户公司主体
*/
@Log(title = "客户公司主体", businessType = BusinessType.DELETE)
@DeleteMapping("/company/{companyIds}")
public AjaxResult removeCompany(@PathVariable Long[] companyIds)
{
return toAjax(bmsCustomerService.deleteBmsCustomerCompanyByCompanyIds(companyIds));
}
/**
* 查询客户联系人列表
*/
@GetMapping("/contact/list")
public TableDataInfo contactList(BmsCustomerContact bmsCustomerContact)
{
startPage();
List<BmsCustomerContact> list = bmsCustomerService.selectBmsCustomerContactList(bmsCustomerContact);
return getDataTable(list);
}
/**
* 获取客户联系人详细信息
*/
@GetMapping("/contact/{contactId}")
public AjaxResult getContactInfo(@PathVariable("contactId") Long contactId)
{
return success(bmsCustomerService.selectBmsCustomerContactByContactId(contactId));
}
/**
* 新增客户联系人
*/
@Log(title = "客户联系人", businessType = BusinessType.INSERT)
@PostMapping("/contact")
public AjaxResult addContact(@RequestBody BmsCustomerContact bmsCustomerContact)
{
return toAjax(bmsCustomerService.insertBmsCustomerContact(bmsCustomerContact));
}
/**
* 修改客户联系人
*/
@Log(title = "客户联系人", businessType = BusinessType.UPDATE)
@PutMapping("/contact")
public AjaxResult editContact(@RequestBody BmsCustomerContact bmsCustomerContact)
{
return toAjax(bmsCustomerService.updateBmsCustomerContact(bmsCustomerContact));
}
/**
* 删除客户联系人
*/
@Log(title = "客户联系人", businessType = BusinessType.DELETE)
@DeleteMapping("/contact/{contactIds}")
public AjaxResult removeContact(@PathVariable Long[] contactIds)
{
return toAjax(bmsCustomerService.deleteBmsCustomerContactByContactIds(contactIds));
}
}
@@ -0,0 +1,78 @@
package com.bms.web.controller.customer.customerinfo;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.enums.BusinessType;
import com.bms.customer.customerinfo.domain.BmsCustomerPriceSet;
import com.bms.customer.customerinfo.domain.BmsPriceSetOption;
import com.bms.customer.customerinfo.service.IBmsCustomerPriceSetService;
/**
* 客户套餐绑定Controller(客户管理-客户编辑-绑定套餐)
*
* @author bms
* @date 2026-09-16
*/
@RestController
@RequestMapping("/customersupplier/customerinfo/priceset")
public class BmsCustomerPriceSetController extends BaseController
{
@Autowired
private IBmsCustomerPriceSetService customerPriceSetService;
/**
* 查询客户已绑定的套餐列表
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/bind/list")
public AjaxResult bindList(Long customerId)
{
List<BmsCustomerPriceSet> list = customerPriceSetService.selectBindList(customerId);
return success(list);
}
/**
* 查询可绑定的生效套餐选项
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/option/list")
public AjaxResult optionList(BmsPriceSetOption query)
{
return success(customerPriceSetService.selectOptionList(query));
}
/**
* 保存客户套餐绑定(已绑定则更新/更换套餐,未绑定则新增)
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:edit')")
@Log(title = "客户套餐绑定", businessType = BusinessType.INSERT)
@PostMapping("/bind")
public AjaxResult saveBind(@RequestBody BmsCustomerPriceSet bind)
{
bind.setCreateBy(getUsername());
bind.setUpdateBy(getUsername());
return toAjax(customerPriceSetService.saveBind(bind));
}
/**
* 解绑客户套餐
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:edit')")
@Log(title = "客户套餐解绑", businessType = BusinessType.DELETE)
@DeleteMapping("/bind/{customerPricesetIds}")
public AjaxResult removeBind(@PathVariable Long[] customerPricesetIds)
{
return toAjax(customerPriceSetService.deleteBindByIds(customerPricesetIds));
}
}
@@ -0,0 +1,85 @@
package com.bms.web.controller.customer.customermoney;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.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.ar.domain.ArBalanceWriteoff;
import com.bms.ar.service.IArBalanceWriteoffService;
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;
/**
* 客户余额核销应收账单 Controller(账单查看统一走应收管理-应收账单页面)
*
* @author bms
* @date 2026-09-16
*/
@RestController
@RequestMapping("/customersupplier/customerbill")
public class CustomerBillController extends BaseController
{
@Autowired
private IArBalanceWriteoffService balanceWriteoffService;
/**
* 查询客户可核销账单(已确认/部分核销,未核销额大于0)
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/unsettled/{customerId}")
public AjaxResult unsettled(@PathVariable("customerId") Long customerId)
{
return success(balanceWriteoffService.selectUnsettledBills(customerId));
}
/**
* 查询余额核销记录(分页)
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/writeoff/list")
public TableDataInfo writeoffList(ArBalanceWriteoff query)
{
if (query == null || query.getCustomerId() == null || query.getCustomerId() == 0L)
{
return getDataTable(Collections.emptyList());
}
startPage();
List<ArBalanceWriteoff> list = balanceWriteoffService.selectWriteoffList(query);
return getDataTable(list);
}
/**
* 用客户账户余额核销账单
*/
@PreAuthorize("@ss.hasPermi('customer:bill:writeoff')")
@Log(title = "余额核销账单", businessType = BusinessType.UPDATE)
@PostMapping("/writeoff")
public AjaxResult writeoff(@RequestBody ArBalanceWriteoff cmd)
{
cmd.setCreateBy(getUsername());
return success(balanceWriteoffService.writeoff(cmd));
}
/**
* 反核销(取消一笔有效余额核销)
*/
@PreAuthorize("@ss.hasPermi('customer:bill:writeoff:cancel')")
@Log(title = "余额核销反核销", businessType = BusinessType.UPDATE)
@PutMapping("/writeoff/{writeoffId}/cancel")
public AjaxResult cancelWriteoff(@PathVariable("writeoffId") Long writeoffId,
@RequestBody(required = false) ArBalanceWriteoff body)
{
balanceWriteoffService.cancelWriteoff(writeoffId, body == null ? null : body.getCancelRemark());
return success();
}
}
@@ -0,0 +1,312 @@
package com.bms.web.controller.customer.customermoney;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
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.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 com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.poi.ExcelUtil;
import com.bms.customer.customermoney.api.ICustomerAccountApi;
import com.bms.customer.customermoney.domain.CustomerBalanceFlow;
import com.bms.customer.customermoney.domain.CustomerCreditApply;
import com.bms.customer.customermoney.domain.CustomerCreditFlow;
import com.bms.customer.customermoney.domain.CustomerReceipt;
import com.bms.customer.customermoney.service.ICreditQuotaService;
import com.bms.customer.customermoney.service.ICustomerMoneyService;
import com.bms.customer.customermoney.vo.CustomerAccountStatusVO;
import com.bms.customer.customermoney.vo.CustomerBalanceFlowExportVO;
import com.bms.customer.customermoney.vo.CustomerCreditFlowExportVO;
import com.bms.customer.customermoney.vo.CustomerReceiptExportVO;
import com.bms.common.utils.SecurityUtils;
/**
* 客户余额/收款/流水 Controller
*
* @author bms
* @date 2026-09-11
*/
@RestController
@RequestMapping("/customersupplier/customermoney")
public class CustomerMoneyController extends BaseController
{
@Autowired
private ICustomerMoneyService customerMoneyService;
@Autowired
private ICustomerAccountApi customerAccountApi;
@Autowired
private ICreditQuotaService creditQuotaService;
/**
* 查询客户账号状态(收货扣件准入控制用)。支持 customerId 或 customerCode 二选一。
* 只读轻量查询,不锁账户也不物化临时额度失效。
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/account/status")
public AjaxResult accountStatus(@RequestParam(value = "customerId", required = false) Long customerId,
@RequestParam(value = "customerCode", required = false) String customerCode)
{
CustomerAccountStatusVO vo = customerAccountApi.getAccountStatus(customerId, customerCode);
if (vo == null)
{
return error("客户不存在");
}
return success(vo);
}
/**
* 查询客户各币种账户余额
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/account/list/{customerId}")
public AjaxResult accountList(@PathVariable("customerId") Long customerId)
{
return success(customerMoneyService.selectAccountList(customerId));
}
/**
* 查询收款记录列表
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/receipt/list")
public TableDataInfo receiptList(CustomerReceipt query)
{
// customerId 为空或为 0,直接返回空数据
if (query == null || query.getCustomerId() == null || query.getCustomerId() == 0L) {
return getDataTable(Collections.emptyList());
}
startPage();
List<CustomerReceipt> list = customerMoneyService.selectReceiptList(query);
return getDataTable(list);
}
/**
* 导出收款记录(按当前筛选条件,全量导出不分页)
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:export')")
@Log(title = "客户收款记录", businessType = BusinessType.EXPORT)
@PostMapping("/receipt/export")
public void receiptExport(HttpServletResponse response, CustomerReceipt query)
{
List<CustomerReceiptExportVO> list = Collections.emptyList();
if (query != null && query.getCustomerId() != null && query.getCustomerId() != 0L)
{
list = customerMoneyService.selectReceiptList(query).stream()
.map(CustomerReceiptExportVO::from).collect(Collectors.toList());
}
ExcelUtil<CustomerReceiptExportVO> util = new ExcelUtil<>(CustomerReceiptExportVO.class);
util.exportExcel(response, list, "收款记录");
}
/**
* 获取收款单详情(含收款附件)
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:query')")
@GetMapping("/receipt/{id}")
public AjaxResult getReceipt(@PathVariable("id") Long id)
{
return success(customerMoneyService.selectReceiptById(id));
}
/**
* 登记收款(默认待确认,同时保存收款凭证附件)
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:add')")
@Log(title = "客户收款", businessType = BusinessType.INSERT)
@PostMapping("/receipt")
public AjaxResult addReceipt(@RequestBody CustomerReceipt receipt)
{
receipt.setCreateBy(getUsername());
return toAjax(customerMoneyService.insertReceipt(receipt));
}
/**
* 审核通过收款单:确认后变动客户余额并生成余额流水
*/
@PreAuthorize("@ss.hasPermi('customer:balance:review')")
@Log(title = "客户收款审核", businessType = BusinessType.UPDATE)
@PutMapping("/receipt/confirm/{id}")
public AjaxResult confirmReceipt(@PathVariable("id") Long id)
{
return toAjax(customerMoneyService.confirmReceipt(id));
}
/**
* 取消收款单(仅待确认可取消)
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:edit')")
@Log(title = "客户收款取消", businessType = BusinessType.UPDATE)
@PutMapping("/receipt/cancel/{id}")
public AjaxResult cancelReceipt(@PathVariable("id") Long id)
{
return toAjax(customerMoneyService.cancelReceipt(id));
}
/**
* 查询余额流水列表
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/flow/list")
public TableDataInfo flowList(CustomerBalanceFlow query)
{
// customerId 为空或为 0,直接返回空数据
if (query == null || query.getCustomerId() == null || query.getCustomerId() == 0L) {
return getDataTable(Collections.emptyList());
}
startPage();
List<CustomerBalanceFlow> list = customerMoneyService.selectFlowList(query);
return getDataTable(list);
}
/**
* 导出余额流水(按当前筛选条件,全量导出不分页)
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:export')")
@Log(title = "客户余额流水", businessType = BusinessType.EXPORT)
@PostMapping("/flow/export")
public void flowExport(HttpServletResponse response, CustomerBalanceFlow query)
{
List<CustomerBalanceFlowExportVO> list = Collections.emptyList();
if (query != null && query.getCustomerId() != null && query.getCustomerId() != 0L)
{
list = customerMoneyService.selectFlowList(query).stream()
.map(CustomerBalanceFlowExportVO::from).collect(Collectors.toList());
}
ExcelUtil<CustomerBalanceFlowExportVO> util = new ExcelUtil<>(CustomerBalanceFlowExportVO.class);
util.exportExcel(response, list, "余额流水");
}
/**
* 查询额度流水列表
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/credit/flow/list")
public TableDataInfo creditFlowList(CustomerCreditFlow query)
{
if (query == null || query.getCustomerId() == null || query.getCustomerId() == 0L) {
return getDataTable(Collections.emptyList());
}
startPage();
List<CustomerCreditFlow> list = customerMoneyService.selectCreditFlowList(query);
return getDataTable(list);
}
/**
* 导出额度流水(按当前筛选条件,全量导出不分页)
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:export')")
@Log(title = "客户额度流水", businessType = BusinessType.EXPORT)
@PostMapping("/credit/flow/export")
public void creditFlowExport(HttpServletResponse response, CustomerCreditFlow query)
{
List<CustomerCreditFlowExportVO> list = Collections.emptyList();
if (query != null && query.getCustomerId() != null && query.getCustomerId() != 0L)
{
list = customerMoneyService.selectCreditFlowList(query).stream()
.map(CustomerCreditFlowExportVO::from).collect(Collectors.toList());
}
ExcelUtil<CustomerCreditFlowExportVO> util = new ExcelUtil<>(CustomerCreditFlowExportVO.class);
util.exportExcel(response, list, "额度流水");
}
/**
* 提交额度调整申请(信用额度/临时额度),申请待审批通过后才真正调整额度
*/
@PreAuthorize("@ss.hasPermi('customer:credit:adjust')")
@Log(title = "客户额度调整申请", businessType = BusinessType.INSERT)
@PostMapping("/credit/apply")
public AjaxResult applyCredit(@RequestBody CustomerCreditApply apply)
{
apply.setCreateBy(getUsername());
return toAjax(customerMoneyService.applyCreditAdjust(apply));
}
/**
* 查询额度调整申请列表
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/credit/apply/list")
public TableDataInfo creditApplyList(CustomerCreditApply query)
{
if (query == null || query.getCustomerId() == null || query.getCustomerId() == 0L) {
return getDataTable(Collections.emptyList());
}
startPage();
List<CustomerCreditApply> list = customerMoneyService.selectCreditApplyList(query);
return getDataTable(list);
}
/**
* 查询额度调整申请详情
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/credit/apply/{applyId}")
public AjaxResult getCreditApply(@PathVariable("applyId") Long applyId)
{
return success(customerMoneyService.selectCreditApplyById(applyId));
}
/**
* 审批通过额度调整申请:通过后调整账户额度并生成额度流水。
* 非超管审批净增额度时 body 须带 approveRoleId(占用该角色额度池)。
*/
@PreAuthorize("@ss.hasPermi('customer:credit:approve')")
@Log(title = "客户额度调整审批", businessType = BusinessType.UPDATE)
@PutMapping("/credit/apply/approve/{applyId}")
public AjaxResult approveCreditApply(@PathVariable("applyId") Long applyId,
@RequestBody(required = false) CustomerCreditApply body)
{
String remark = body != null ? body.getApproveRemark() : null;
Long approveRoleId = body != null ? body.getApproveRoleId() : null;
return toAjax(customerMoneyService.approveCreditApply(applyId, remark, approveRoleId));
}
/**
* 当前登录用户审批时可选择的角色额度池(超管返回空列表,前端不展示选框)。
*/
@PreAuthorize("@ss.hasPermi('customer:credit:approve')")
@GetMapping("/credit/quota/my-pools")
public AjaxResult myCreditPools()
{
return success(creditQuotaService.myPools(SecurityUtils.getUserId()));
}
/**
* 驳回额度调整申请(驳回原因必填)
*/
@PreAuthorize("@ss.hasPermi('customer:credit:approve')")
@Log(title = "客户额度调整驳回", businessType = BusinessType.UPDATE)
@PutMapping("/credit/apply/reject/{applyId}")
public AjaxResult rejectCreditApply(@PathVariable("applyId") Long applyId,
@RequestBody CustomerCreditApply body)
{
String reason = body != null ? body.getApproveRemark() : null;
return toAjax(customerMoneyService.rejectCreditApply(applyId, reason));
}
/**
* 撤销待审批的额度调整申请
*/
@PreAuthorize("@ss.hasPermi('customer:credit:adjust')")
@Log(title = "客户额度调整撤销", businessType = BusinessType.UPDATE)
@PutMapping("/credit/apply/cancel/{applyId}")
public AjaxResult cancelCreditApply(@PathVariable("applyId") Long applyId)
{
return toAjax(customerMoneyService.cancelCreditApply(applyId));
}
}
@@ -0,0 +1,135 @@
package com.bms.web.controller.customer.supplierinfo;
import java.util.List;
import com.bms.customer.customerinfo.domain.BmsCustomerAttachment;
import com.bms.customer.customerinfo.domain.BmsCustomerCompany;
import com.bms.customer.supplierinfo.domain.BmsSupplier;
import com.bms.customer.supplierinfo.domain.BmsSupplierAttachment;
import com.bms.customer.supplierinfo.domain.BmsSupplierCompany;
import com.bms.customer.supplierinfo.service.IBmsSupplierService;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.enums.BusinessType;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.utils.poi.ExcelUtil;
/**
* 供应商管理Controller
*
* @author bms
* @date 2026-09-09
*/
@RestController
@RequestMapping("/customersupplier/supplierinfo")
public class BmsSupplierController extends BaseController
{
@Autowired
private IBmsSupplierService bmsSupplierService;
/** 查询供应商列表 */
@PreAuthorize("@ss.hasPermi('customersupplier:supplierinfo:list')")
@GetMapping("/list")
public TableDataInfo list(BmsSupplier bmsSupplier)
{
startPage();
List<BmsSupplier> list = bmsSupplierService.selectBmsSupplierList(bmsSupplier);
return getDataTable(list);
}
/** 导出供应商列表 */
@PreAuthorize("@ss.hasPermi('customersupplier:supplierinfo:export')")
@Log(title = "供应商信息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, BmsSupplier bmsSupplier)
{
List<BmsSupplier> list = bmsSupplierService.selectBmsSupplierList(bmsSupplier);
ExcelUtil<BmsSupplier> util = new ExcelUtil<BmsSupplier>(BmsSupplier.class);
util.exportExcel(response, list, "供应商数据");
}
/** 获取供应商详细信息 */
@PreAuthorize("@ss.hasPermi('customersupplier:supplierinfo:query')")
@GetMapping(value = "/{supplierId}")
public AjaxResult getInfo(@PathVariable("supplierId") Long supplierId)
{
return success(bmsSupplierService.selectBmsSupplierBySupplierId(supplierId));
}
/** 新增供应商 */
@PreAuthorize("@ss.hasPermi('customersupplier:supplierinfo:add')")
@Log(title = "供应商信息", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BmsSupplier bmsSupplier)
{
bmsSupplierService.insertBmsSupplier(bmsSupplier);
// 返回新主键,供前端新增成功后停留在编辑页继续维护子表
AjaxResult ajax = AjaxResult.success("新增成功");
ajax.put("supplierId", bmsSupplier.getSupplierId());
return ajax;
}
/** 修改供应商 */
@PreAuthorize("@ss.hasPermi('customersupplier:supplierinfo:edit')")
@Log(title = "供应商信息", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BmsSupplier bmsSupplier)
{
return toAjax(bmsSupplierService.updateBmsSupplier(bmsSupplier));
}
/** 删除供应商 */
@PreAuthorize("@ss.hasPermi('customersupplier:supplierinfo:remove')")
@Log(title = "供应商信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{supplierIds}")
public AjaxResult remove(@PathVariable Long[] supplierIds)
{
return toAjax(bmsSupplierService.deleteBmsSupplierBySupplierIds(supplierIds));
}
/** 查看供应商附件列表(子表共用) */
@GetMapping("/attachment/list")
public TableDataInfo attachmentList(BmsSupplierAttachment bmsSupplierAttachment)
{
startPage();
List<BmsSupplierAttachment> list = bmsSupplierService.selectBmsSupplierAttachmentList(bmsSupplierAttachment);
return getDataTable(list);
}
/** 查看供应商公司主体列表(子表共用) */
@GetMapping("/company/list")
public TableDataInfo companyList(BmsSupplierCompany bmsSupplierCompany)
{
startPage();
List<BmsSupplierCompany> list = bmsSupplierService.selectBmsSupplierCompanyList(bmsSupplierCompany);
return getDataTable(list);
}
/** 删除供应商公司主体 */
@Log(title = "供应商公司主体", businessType = BusinessType.DELETE)
@DeleteMapping("/company/{companyIds}")
public AjaxResult removeCompany(@PathVariable Long[] companyIds)
{
return toAjax(bmsSupplierService.deleteBmsSupplierCompanyByCompanyIds(companyIds));
}
/** 删除供应商联系人 */
@Log(title = "供应商联系人", businessType = BusinessType.DELETE)
@DeleteMapping("/contact/{contactIds}")
public AjaxResult removeContact(@PathVariable Long[] contactIds)
{
return toAjax(bmsSupplierService.deleteBmsSupplierContactByContactIds(contactIds));
}
}
@@ -0,0 +1,163 @@
package com.bms.web.controller.data;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
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.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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.poi.ExcelUtil;
import com.bms.system.domain.BmsExchangeRate;
import com.bms.system.domain.BmsExchangeRateConvertResult;
import com.bms.system.service.IBmsExchangeRateService;
/**
* 汇率管理 信息操作处理
*
* @author bms
*/
@RestController
@RequestMapping("/data/exchangerate")
public class BmsExchangeRateController extends BaseController
{
@Autowired
private IBmsExchangeRateService exchangeRateService;
/**
* 获取汇率列表
*/
@PreAuthorize("@ss.hasPermi('data:exchangerate:list')")
@GetMapping("/list")
public TableDataInfo list(BmsExchangeRate exchangeRate)
{
startPage();
List<BmsExchangeRate> list = exchangeRateService.selectExchangeRateList(exchangeRate);
return getDataTable(list);
}
/**
* 导出汇率列表
*/
@PreAuthorize("@ss.hasPermi('data:exchangerate:export')")
@Log(title = "汇率管理", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, BmsExchangeRate exchangeRate)
{
List<BmsExchangeRate> list = exchangeRateService.selectExchangeRateList(exchangeRate);
ExcelUtil<BmsExchangeRate> util = new ExcelUtil<BmsExchangeRate>(BmsExchangeRate.class);
util.exportExcel(response, list, "汇率数据");
}
/**
* 获取汇率详细信息
*/
@PreAuthorize("@ss.hasPermi('data:exchangerate:query')")
@GetMapping(value = "/{rateId}")
public AjaxResult getInfo(@PathVariable Long rateId)
{
return success(exchangeRateService.selectExchangeRateById(rateId));
}
/**
* 新增汇率
*/
@PreAuthorize("@ss.hasPermi('data:exchangerate:add')")
@Log(title = "汇率管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody BmsExchangeRate exchangeRate)
{
exchangeRate.setCreateBy(getUsername());
return toAjax(exchangeRateService.insertExchangeRate(exchangeRate));
}
/**
* 修改汇率
*/
@PreAuthorize("@ss.hasPermi('data:exchangerate:edit')")
@Log(title = "汇率管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody BmsExchangeRate exchangeRate)
{
return toAjax(exchangeRateService.updateExchangeRate(exchangeRate));
}
/**
* 删除汇率
*/
@PreAuthorize("@ss.hasPermi('data:exchangerate:remove')")
@Log(title = "汇率管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{rateIds}")
public AjaxResult remove(@PathVariable Long[] rateIds)
{
return toAjax(exchangeRateService.deleteExchangeRateByIds(rateIds));
}
/**
* 汇率换算(业务公共接口,登录即可调用,不单独设权限)
*
* @param amount 源金额
* @param fromCurrency 源币种
* @param toCurrency 目标币种
* @param rateType 汇率类型(0即期 1月初 2月末 3固定),默认即期
* @param bizDate 业务日期 yyyy-MM-dd,默认当天
*/
@GetMapping("/convert")
public AjaxResult convert(@RequestParam BigDecimal amount,
@RequestParam String fromCurrency,
@RequestParam String toCurrency,
@RequestParam(required = false) String rateType,
@RequestParam(required = false) String bizDate)
{
Date date = parseDate(bizDate);
BmsExchangeRateConvertResult result = exchangeRateService.convert(
amount, fromCurrency, toCurrency, rateType, date);
return success(result);
}
/**
* 本位币(CNY)折算:任意币种金额折算为人民币
*/
@GetMapping("/toBase")
public AjaxResult toBase(@RequestParam BigDecimal amount,
@RequestParam String currency,
@RequestParam(required = false) String rateType,
@RequestParam(required = false) String bizDate)
{
Date date = parseDate(bizDate);
BmsExchangeRateConvertResult result = exchangeRateService.convert(
amount, currency, BmsExchangeRate.BASE_CURRENCY, rateType, date);
return success(result);
}
private Date parseDate(String bizDate)
{
if (bizDate == null || bizDate.isEmpty())
{
return null;
}
try
{
return new SimpleDateFormat("yyyy-MM-dd").parse(bizDate);
}
catch (Exception e)
{
return null;
}
}
}
@@ -0,0 +1,136 @@
package com.bms.web.controller.data;
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.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.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.poi.ExcelUtil;
import com.bms.system.domain.BmsRegion;
import com.bms.system.service.IBmsRegionService;
import com.bms.system.service.IBmsRegionSyncService;
/**
* 区域管理 信息操作处理
*
* @author bms
*/
@RestController
@RequestMapping("/data/region")
public class BmsRegionController extends BaseController
{
@Autowired
private IBmsRegionService regionService;
@Autowired
private IBmsRegionSyncService syncService;
/**
* 获取区域列表
*/
@PreAuthorize("@ss.hasPermi('data:region:list')")
@GetMapping("/list")
public TableDataInfo list(BmsRegion region)
{
startPage();
List<BmsRegion> list = regionService.selectRegionList(region);
return getDataTable(list);
}
/**
* 获取国家下拉选项列表(仅启用状态)
*/
@GetMapping("/optionselect")
public AjaxResult optionselect()
{
BmsRegion query = new BmsRegion();
query.setStatus("0");
List<BmsRegion> list = regionService.selectRegionList(query);
return success(list);
}
/**
* 导出区域列表
*/
@PreAuthorize("@ss.hasPermi('data:region:list')")
@Log(title = "区域管理", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, BmsRegion region)
{
List<BmsRegion> list = regionService.selectRegionList(region);
ExcelUtil<BmsRegion> util = new ExcelUtil<BmsRegion>(BmsRegion.class);
util.exportExcel(response, list, "区域数据");
}
/**
* 获取区域详细信息
*/
@PreAuthorize("@ss.hasPermi('data:region:query')")
@GetMapping(value = "/{regionId}")
public AjaxResult getInfo(@PathVariable Long regionId)
{
return success(regionService.selectRegionById(regionId));
}
/**
* 修改区域
*/
@PreAuthorize("@ss.hasPermi('data:region:edit')")
@Log(title = "区域管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody BmsRegion region)
{
region.setUpdateBy(getUsername());
return toAjax(regionService.updateRegion(region));
}
/**
* 删除区域
*/
@PreAuthorize("@ss.hasPermi('data:region:remove')")
@Log(title = "区域管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{regionIds}")
public AjaxResult remove(@PathVariable Long[] regionIds)
{
return toAjax(regionService.deleteRegionByIds(regionIds));
}
/**
* 修改区域状态(启用/停用)
*/
@PreAuthorize("@ss.hasPermi('data:region:edit')")
@Log(title = "区域管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
public AjaxResult changeStatus(@RequestBody BmsRegion region)
{
region.setUpdateBy(getUsername());
return toAjax(regionService.updateRegionStatus(region));
}
/**
* 同步国家数据(异步)
*/
@PreAuthorize("@ss.hasPermi('data:region:sync')")
@Log(title = "区域管理", businessType = BusinessType.OTHER)
@PostMapping("/sync")
public AjaxResult sync()
{
Long logId = syncService.syncRegion();
AjaxResult ajax = AjaxResult.success("同步任务已创建,正在后台执行");
ajax.put("logId", logId);
return ajax;
}
}
@@ -0,0 +1,63 @@
package com.bms.web.controller.data;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.system.domain.BmsRegionSyncLog;
import com.bms.system.mapper.BmsRegionSyncLogMapper;
/**
* 区域同步日志 信息操作处理
*
* @author bms
*/
@RestController
@RequestMapping("/data/region/log")
public class BmsRegionSyncLogController extends BaseController
{
@Autowired
private BmsRegionSyncLogMapper syncLogMapper;
/**
* 获取同步日志列表
*/
@PreAuthorize("@ss.hasPermi('data:region:log:list')")
@GetMapping("/list")
public TableDataInfo list(BmsRegionSyncLog syncLog)
{
startPage();
List<BmsRegionSyncLog> list = syncLogMapper.selectSyncLogList(syncLog);
return getDataTable(list);
}
/**
* 获取同步日志详细信息
*/
@PreAuthorize("@ss.hasPermi('data:region:log:list')")
@GetMapping(value = "/{logId}")
public AjaxResult getInfo(@PathVariable Long logId)
{
return success(syncLogMapper.selectSyncLogById(logId));
}
/**
* 删除同步日志
*/
@PreAuthorize("@ss.hasPermi('data:region:log:remove')")
@Log(title = "同步日志", businessType = BusinessType.DELETE)
@DeleteMapping("/{logIds}")
public AjaxResult remove(@PathVariable Long[] logIds)
{
return toAjax(syncLogMapper.deleteSyncLogByIds(logIds));
}
}
@@ -0,0 +1,114 @@
package com.bms.web.controller.monitor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.price.domain.PrcBillingTask;
import com.bms.price.enums.BillTaskSuccessStatus;
import com.bms.price.service.IPrcBillingTaskService;
/**
* Monitor: failed / waiting prc_billing_task list and manual resync.
*/
@RestController
@RequestMapping("/monitor/billingTask")
public class BillingTaskMonitorController extends BaseController
{
@Autowired
private IPrcBillingTaskService billingTaskService;
/** Default list = FAILED abnormal tasks. */
@PreAuthorize("@ss.hasPermi('monitor:billingTask:list')")
@GetMapping("/list")
public TableDataInfo list(PrcBillingTask query)
{
if (query.getSuccessStatus() == null || query.getSuccessStatus().isEmpty())
{
query.setSuccessStatus(BillTaskSuccessStatus.FAILED.getCode());
}
else if ("ALL".equalsIgnoreCase(query.getSuccessStatus()))
{
query.setSuccessStatus(null);
}
startPage();
List<PrcBillingTask> list = billingTaskService.selectList(query);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('monitor:billingTask:list')")
@GetMapping("/{taskId}")
public AjaxResult getInfo(@PathVariable("taskId") Long taskId)
{
return success(billingTaskService.getById(taskId));
}
/** Manual resync one task (reset WAITING then process). */
@PreAuthorize("@ss.hasPermi('monitor:billingTask:retry')")
@Log(title = "billing task retry", businessType = BusinessType.UPDATE)
@PostMapping("/retry/{taskId}")
public AjaxResult retry(@PathVariable("taskId") Long taskId)
{
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")
public AjaxResult retryBatch(@RequestBody Long[] taskIds)
{
if (taskIds == null || taskIds.length == 0)
{
return error("taskIds required");
}
int ok = 0;
int fail = 0;
List<Map<String, Object>> details = new ArrayList<Map<String, Object>>();
for (Long taskId : taskIds)
{
Map<String, Object> row = new HashMap<String, Object>();
row.put("taskId", taskId);
try
{
PrcBillingTask task = billingTaskService.retryTask(taskId);
row.put("successStatus", task == null ? null : task.getSuccessStatus());
row.put("errorMessage", task == null ? null : task.getErrorMessage());
if (task != null && BillTaskSuccessStatus.SUCCESS.getCode().equals(task.getSuccessStatus()))
{
ok++;
}
else
{
fail++;
}
details.add(row);
}
catch (RuntimeException e)
{
fail++;
row.put("successStatus", "ERROR");
row.put("errorMessage", e.getMessage());
details.add(row);
}
}
Map<String, Object> data = new HashMap<String, Object>();
data.put("ok", Integer.valueOf(ok));
data.put("fail", Integer.valueOf(fail));
data.put("details", details);
return success(data);
}
}
@@ -0,0 +1,122 @@
package com.bms.web.controller.monitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.constant.CacheConstants;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.utils.StringUtils;
import com.bms.system.domain.SysCache;
/**
* 缓存监控
*
* @author bms
*/
@RestController
@RequestMapping("/monitor/cache")
public class CacheController
{
@Autowired
private RedisTemplate<String, String> redisTemplate;
private final static List<SysCache> caches = new ArrayList<SysCache>();
{
caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息"));
caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典"));
caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
}
@SuppressWarnings("deprecation")
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping()
public AjaxResult getInfo() throws Exception
{
Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info());
Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats"));
Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize());
Map<String, Object> result = new HashMap<>(3);
result.put("info", info);
result.put("dbSize", dbSize);
List<Map<String, String>> pieList = new ArrayList<>();
commandStats.stringPropertyNames().forEach(key -> {
Map<String, String> data = new HashMap<>(2);
String property = commandStats.getProperty(key);
data.put("name", StringUtils.removeStart(key, "cmdstat_"));
data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
pieList.add(data);
});
result.put("commandStats", pieList);
return AjaxResult.success(result);
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getNames")
public AjaxResult cache()
{
return AjaxResult.success(caches);
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getKeys/{cacheName}")
public AjaxResult getCacheKeys(@PathVariable String cacheName)
{
Set<String> cacheKeys = redisTemplate.keys(cacheName + "*");
return AjaxResult.success(new TreeSet<>(cacheKeys));
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getValue/{cacheName}/{cacheKey}")
public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey)
{
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue);
return AjaxResult.success(sysCache);
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheName/{cacheName}")
public AjaxResult clearCacheName(@PathVariable String cacheName)
{
Collection<String> cacheKeys = redisTemplate.keys(cacheName + "*");
redisTemplate.delete(cacheKeys);
return AjaxResult.success();
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheKey/{cacheKey}")
public AjaxResult clearCacheKey(@PathVariable String cacheKey)
{
redisTemplate.delete(cacheKey);
return AjaxResult.success();
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheAll")
public AjaxResult clearCacheAll()
{
Collection<String> cacheKeys = redisTemplate.keys("*");
redisTemplate.delete(cacheKeys);
return AjaxResult.success();
}
}
@@ -0,0 +1,54 @@
package com.bms.web.controller.monitor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.constant.HttpStatus;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.PageDomain;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.core.page.TableSupport;
import com.bms.price.search.BillSearchPage;
import com.bms.price.search.IBillSearchStore;
/**
* Generic Elasticsearch business-log viewer.
*/
@RestController
@RequestMapping("/monitor/eslog")
public class EsLogController extends BaseController
{
@Autowired
private IBillSearchStore billSearchStore;
@PreAuthorize("@ss.hasPermi('monitor:eslog:list')")
@GetMapping("/list")
public TableDataInfo list(
@RequestParam(value = "logType", required = false) String logType,
@RequestParam(value = "bizId", required = false) String bizId,
@RequestParam(value = "status", required = false) String status)
{
PageDomain pageDomain = TableSupport.buildPageRequest();
int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue();
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue();
BillSearchPage page = billSearchStore.search(logType, bizId, status, pageNum, pageSize);
TableDataInfo rsp = new TableDataInfo();
rsp.setCode(HttpStatus.SUCCESS);
rsp.setMsg("\u67e5\u8be2\u6210\u529f");
rsp.setRows(page.getRows());
rsp.setTotal(page.getTotal());
return rsp;
}
@PreAuthorize("@ss.hasPermi('monitor:eslog:list')")
@GetMapping("/{docId}")
public AjaxResult getInfo(@PathVariable("docId") String docId)
{
return success(billSearchStore.getByDocId(docId));
}
}
@@ -0,0 +1,27 @@
package com.bms.web.controller.monitor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.framework.web.domain.Server;
/**
* 服务器监控
*
* @author bms
*/
@RestController
@RequestMapping("/monitor/server")
public class ServerController
{
@PreAuthorize("@ss.hasPermi('monitor:server:list')")
@GetMapping()
public AjaxResult getInfo() throws Exception
{
Server server = new Server();
server.copyTo();
return AjaxResult.success(server);
}
}
@@ -0,0 +1,82 @@
package com.bms.web.controller.monitor;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.poi.ExcelUtil;
import com.bms.framework.web.service.SysPasswordService;
import com.bms.system.domain.SysLogininfor;
import com.bms.system.service.ISysLogininforService;
/**
* 系统访问记录
*
* @author bms
*/
@RestController
@RequestMapping("/monitor/logininfor")
public class SysLogininforController extends BaseController
{
@Autowired
private ISysLogininforService logininforService;
@Autowired
private SysPasswordService passwordService;
@PreAuthorize("@ss.hasPermi('monitor:logininfor:list')")
@GetMapping("/list")
public TableDataInfo list(SysLogininfor logininfor)
{
startPage();
List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
return getDataTable(list);
}
@Log(title = "登录日志", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('monitor:logininfor:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysLogininfor logininfor)
{
List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
util.exportExcel(response, list, "登录日志");
}
@PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
@Log(title = "登录日志", businessType = BusinessType.DELETE)
@DeleteMapping("/{infoIds}")
public AjaxResult remove(@PathVariable Long[] infoIds)
{
return toAjax(logininforService.deleteLogininforByIds(infoIds));
}
@PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
@Log(title = "登录日志", businessType = BusinessType.CLEAN)
@DeleteMapping("/clean")
public AjaxResult clean()
{
logininforService.cleanLogininfor();
return success();
}
@PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')")
@Log(title = "账户解锁", businessType = BusinessType.OTHER)
@GetMapping("/unlock/{userName}")
public AjaxResult unlock(@PathVariable("userName") String userName)
{
passwordService.clearLoginRecordCache(userName);
return success();
}
}
@@ -0,0 +1,69 @@
package com.bms.web.controller.monitor;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.poi.ExcelUtil;
import com.bms.system.domain.SysOperLog;
import com.bms.system.service.ISysOperLogService;
/**
* 操作日志记录
*
* @author bms
*/
@RestController
@RequestMapping("/monitor/operlog")
public class SysOperlogController extends BaseController
{
@Autowired
private ISysOperLogService operLogService;
@PreAuthorize("@ss.hasPermi('monitor:operlog:list')")
@GetMapping("/list")
public TableDataInfo list(SysOperLog operLog)
{
startPage();
List<SysOperLog> list = operLogService.selectOperLogList(operLog);
return getDataTable(list);
}
@Log(title = "操作日志", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('monitor:operlog:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysOperLog operLog)
{
List<SysOperLog> list = operLogService.selectOperLogList(operLog);
ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
util.exportExcel(response, list, "操作日志");
}
@Log(title = "操作日志", businessType = BusinessType.DELETE)
@PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
@DeleteMapping("/{operIds}")
public AjaxResult remove(@PathVariable Long[] operIds)
{
return toAjax(operLogService.deleteOperLogByIds(operIds));
}
@Log(title = "操作日志", businessType = BusinessType.CLEAN)
@PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
@DeleteMapping("/clean")
public AjaxResult clean()
{
operLogService.cleanOperLog();
return success();
}
}
@@ -0,0 +1,83 @@
package com.bms.web.controller.monitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.constant.CacheConstants;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.domain.model.LoginUser;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.core.redis.RedisCache;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.StringUtils;
import com.bms.system.domain.SysUserOnline;
import com.bms.system.service.ISysUserOnlineService;
/**
* 在线用户监控
*
* @author bms
*/
@RestController
@RequestMapping("/monitor/online")
public class SysUserOnlineController extends BaseController
{
@Autowired
private ISysUserOnlineService userOnlineService;
@Autowired
private RedisCache redisCache;
@PreAuthorize("@ss.hasPermi('monitor:online:list')")
@GetMapping("/list")
public TableDataInfo list(String ipaddr, String userName)
{
Collection<String> keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*");
List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>();
for (String key : keys)
{
LoginUser user = redisCache.getCacheObject(key);
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
{
userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
}
else if (StringUtils.isNotEmpty(ipaddr))
{
userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
}
else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
{
userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
}
else
{
userOnlineList.add(userOnlineService.loginUserToUserOnline(user));
}
}
Collections.reverse(userOnlineList);
userOnlineList.removeAll(Collections.singleton(null));
return getDataTable(userOnlineList);
}
/**
* 强退用户
*/
@PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')")
@Log(title = "在线用户", businessType = BusinessType.FORCE)
@DeleteMapping("/{tokenId}")
public AjaxResult forceLogout(@PathVariable String tokenId)
{
redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId);
return success();
}
}
@@ -0,0 +1,203 @@
package com.bms.web.controller.price;
import java.util.List;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.price.domain.PrcOrderBill;
import com.bms.price.domain.PrcQuote;
import com.bms.price.domain.vo.AddIncomeBillFeeRequest;
import com.bms.price.domain.vo.UpdateIncomeBillFeeRequest;
import com.bms.price.domain.vo.TmsPriceBillRequest;
import com.bms.price.domain.vo.TmsPriceQuoteRequest;
import com.bms.price.domain.vo.TmsPriceQuoteResult;
import com.bms.price.service.IPrcOrderBillService;
import com.bms.price.service.ITmsPriceQuoteService;
/**
* Billing center: order quote trial (authenticated).
*
* @author bms
*/
@RestController
@RequestMapping("/price/billing")
public class BillingController extends BaseController
{
@Autowired
private ITmsPriceQuoteService tmsPriceQuoteService;
@Autowired
private IPrcOrderBillService prcOrderBillService;
/**
* Historical quote list.
*/
@PreAuthorize("@ss.hasPermi('price:billing:quote')")
@GetMapping("/quote/list")
public TableDataInfo list(PrcQuote query)
{
startPage();
List<PrcQuote> list = tmsPriceQuoteService.selectQuoteList(query);
return getDataTable(list);
}
/**
* Historical quote detail with fee lines.
*/
@PreAuthorize("@ss.hasPermi('price:billing:quote')")
@GetMapping("/quote/{quoteId}")
public AjaxResult getInfo(@PathVariable String quoteId)
{
return success(tmsPriceQuoteService.getQuote(quoteId));
}
/**
* Same matching logic as TMS open API, for BMS console trial.
*/
@PreAuthorize("@ss.hasPermi('price:billing:quote')")
@PostMapping("/quote")
public AjaxResult quote(@Validated @RequestBody TmsPriceQuoteRequest request)
{
TmsPriceQuoteResult result = tmsPriceQuoteService.quote(request);
return success(result);
}
@PreAuthorize("@ss.hasPermi('price:billing:bill')")
@GetMapping("/bill/list")
public TableDataInfo billList(PrcOrderBill query)
{
startPage();
List<PrcOrderBill> list = prcOrderBillService.selectBillList(query);
return getDataTable(list);
}
/**
* Order no options for remote-search dropdown (bill list multi search).
*/
@PreAuthorize("@ss.hasPermi('price:billing:bill')")
@GetMapping("/bill/orderNos")
public AjaxResult orderNoOptions(@RequestParam(value = "keyword", required = false) String keyword)
{
return success(prcOrderBillService.listOrderNoOptions(keyword));
}
@PreAuthorize("@ss.hasPermi('price:billing:bill')")
@GetMapping("/bill/trackingNos")
public AjaxResult trackingNoOptions(@RequestParam(value = "keyword", required = false) String keyword)
{
return success(prcOrderBillService.listTrackingNoOptions(keyword));
}
@PreAuthorize("@ss.hasPermi('price:billing:bill')")
@GetMapping("/bill/transferNos")
public AjaxResult transferNoOptions(@RequestParam(value = "keyword", required = false) String keyword)
{
return success(prcOrderBillService.listTransferNoOptions(keyword));
}
@PreAuthorize("@ss.hasPermi('price:billing:bill')")
@GetMapping("/bill/{billId}")
public AjaxResult getBill(@PathVariable String billId)
{
return success(prcOrderBillService.getBill(billId));
}
/**
* Batch update order credit arrears status (ARREARS -> NORMAL) with available-credit check.
*/
@PreAuthorize("@ss.hasPermi('price:billing:arrearsUpdate')")
@Log(title = "Billing credit arrears", businessType = BusinessType.UPDATE)
@PutMapping("/bill/creditArrears")
public AjaxResult updateCreditArrears(@RequestBody java.util.List<String> billIds)
{
if (billIds == null || billIds.isEmpty())
{
return error("请勾选要更新的订单");
}
int success = 0;
java.util.List<String> fails = new java.util.ArrayList<String>();
for (String billId : billIds)
{
try
{
prcOrderBillService.updateCreditArrearsStatus(billId);
success++;
}
catch (RuntimeException e)
{
String msg = e.getMessage();
if (msg == null || msg.isEmpty())
{
msg = e.getClass().getSimpleName();
}
fails.add((billId == null ? "" : billId) + ": " + msg);
}
}
if (fails.isEmpty())
{
return success("成功更新 " + success + " 条");
}
StringBuilder sb = new StringBuilder();
sb.append("成功 ").append(success).append(" 条,失败 ").append(fails.size()).append(" 条");
int limit = Math.min(fails.size(), 5);
for (int i = 0; i < limit; i++)
{
sb.append(";").append(fails.get(i));
}
if (fails.size() > limit)
{
sb.append("…");
}
if (success > 0)
{
return success(sb.toString());
}
return error(sb.toString());
}
@PreAuthorize("@ss.hasPermi('price:billing:feeAdd')")
@Log(title = "Billing income fee", businessType = BusinessType.INSERT)
@PostMapping("/bill/fee")
public AjaxResult addIncomeFee(@Validated @RequestBody AddIncomeBillFeeRequest request)
{
return success(prcOrderBillService.addIncomeFee(request));
}
@PreAuthorize("@ss.hasPermi('price:billing:feeEdit')")
@Log(title = "Billing income fee", businessType = BusinessType.UPDATE)
@PutMapping("/bill/fee")
public AjaxResult updateIncomeFee(@Validated @RequestBody UpdateIncomeBillFeeRequest request)
{
return success(prcOrderBillService.updateIncomeFee(request));
}
@PreAuthorize("@ss.hasPermi('price:billing:feeRemove')")
@Log(title = "Billing income fee", businessType = BusinessType.DELETE)
@DeleteMapping("/bill/fee/{feeId}")
public AjaxResult deleteIncomeFee(@PathVariable String feeId)
{
return success(prcOrderBillService.deleteIncomeFee(feeId));
}
@PreAuthorize("@ss.hasPermi('price:billing:bill')")
@PostMapping("/bill")
public AjaxResult bill(@Validated @RequestBody TmsPriceBillRequest request)
{
TmsPriceQuoteResult result = prcOrderBillService.bill(request);
return success(result);
}
}
@@ -0,0 +1,277 @@
package com.bms.web.controller.price;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.MessageUtils;
import com.bms.price.domain.PrcCustomerPriceSet;
import com.bms.price.domain.PrcPriceSet;
import com.bms.price.domain.PrcPriceSetDetail;
import com.bms.price.domain.PrcPriceSetSaveTask;
import com.bms.price.domain.PrcPriceSetVersion;
import com.bms.price.service.IPrcPriceSetService;
/**
* 价格套餐管理
*
* @author bms
*/
@RestController
@RequestMapping("/price/set")
public class PriceSetController extends BaseController
{
@Autowired
private IPrcPriceSetService priceSetService;
@PreAuthorize("@ss.hasPermi('price:set:list')")
@GetMapping("/list")
public TableDataInfo list(PrcPriceSet set)
{
startPage();
List<PrcPriceSet> list = priceSetService.selectPrcPriceSetList(set);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('price:set:query')")
@GetMapping("/{priceSetId:[0-9]+}")
public AjaxResult getInfo(@PathVariable Long priceSetId)
{
return success(priceSetService.selectPrcPriceSetById(priceSetId));
}
@PreAuthorize("@ss.hasPermi('price:set:add')")
@Log(title = "价格套餐", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody PrcPriceSet set)
{
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.
Map<String, Object> payload = new HashMap<String, Object>();
payload.put("taskId", taskId);
return success(payload);
}
@PreAuthorize("@ss.hasPermi('price:set:query')")
@GetMapping("/save/task/{taskId}")
public AjaxResult getSaveTask(@PathVariable String taskId)
{
PrcPriceSetSaveTask task = priceSetService.getSaveTask(taskId);
if (task == null)
{
return error(MessageUtils.message("price.set.save.task.notExists"));
}
return success(task);
}
@PreAuthorize("@ss.hasPermi('price:set:edit')")
@Log(title = "价格套餐", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody PrcPriceSet set)
{
set.setUpdateBy(getUsername());
return toAjax(priceSetService.updatePrcPriceSet(set));
}
@PreAuthorize("@ss.hasPermi('price:set:remove')")
@Log(title = "价格套餐", businessType = BusinessType.DELETE)
@DeleteMapping("/{priceSetIds}")
public AjaxResult remove(@PathVariable Long[] priceSetIds)
{
return toAjax(priceSetService.deletePrcPriceSetByIds(priceSetIds));
}
@PreAuthorize("@ss.hasPermi('price:set:audit')")
@Log(title = "价格套餐提交审核", businessType = BusinessType.UPDATE)
@PutMapping("/submit/{priceSetId}")
public AjaxResult submit(@PathVariable Long priceSetId)
{
return toAjax(priceSetService.submit(priceSetId, getUsername()));
}
@PreAuthorize("@ss.hasPermi('price:set:audit')")
@Log(title = "价格套餐生效", businessType = BusinessType.UPDATE)
@PutMapping("/approve/{priceSetId}")
public AjaxResult approve(@PathVariable Long priceSetId)
{
return toAjax(priceSetService.approve(priceSetId, getUsername()));
}
@PreAuthorize("@ss.hasPermi('price:set:audit')")
@Log(title = "价格套餐驳回", businessType = BusinessType.UPDATE)
@PutMapping("/reject/{priceSetId}")
public AjaxResult reject(@PathVariable Long priceSetId)
{
return toAjax(priceSetService.reject(priceSetId, getUsername()));
}
@PreAuthorize("@ss.hasPermi('price:set:edit')")
@Log(title = "Price set version activate", businessType = BusinessType.UPDATE)
@PutMapping("/version/activate/{psvId}")
public AjaxResult activateVersion(@PathVariable Long psvId, @RequestBody(required = false) PrcPriceSetVersion body)
{
Date effectiveTime = body == null ? null : body.getEffectiveTime();
return toAjax(priceSetService.activateVersion(psvId, effectiveTime, getUsername()));
}
@PreAuthorize("@ss.hasPermi('price:set:add')")
@Log(title = "Price set version copy", businessType = BusinessType.INSERT)
@PostMapping("/version/copy/{psvId}")
public AjaxResult copyVersion(@PathVariable Long psvId)
{
Long newPsvId = priceSetService.copyVersion(psvId, getUsername());
return success(newPsvId);
}
@PreAuthorize("@ss.hasPermi('price:set:edit')")
@Log(title = "Price set version delete", businessType = BusinessType.DELETE)
@DeleteMapping("/version/{psvId}")
public AjaxResult deleteVersion(@PathVariable Long psvId)
{
return toAjax(priceSetService.deleteVersion(psvId, getUsername()));
}
@PreAuthorize("@ss.hasPermi('price:set:audit')")
@Log(title = "Price set disable", businessType = BusinessType.UPDATE)
@PutMapping("/disable/{priceSetId}")
public AjaxResult disable(@PathVariable Long priceSetId)
{
return toAjax(priceSetService.disable(priceSetId, getUsername()));
}
@PreAuthorize("@ss.hasPermi('price:set:audit')")
@Log(title = "Price set enable", businessType = BusinessType.UPDATE)
@PutMapping("/enable/{priceSetId}")
public AjaxResult enable(@PathVariable Long priceSetId)
{
return toAjax(priceSetService.enable(priceSetId, getUsername()));
}
@PreAuthorize("@ss.hasPermi('price:set:add')")
@Log(title = "Price set copy", businessType = BusinessType.INSERT)
@PostMapping("/copy/{priceSetId}")
public AjaxResult copy(@PathVariable Long priceSetId)
{
Long id = priceSetService.copy(priceSetId, getUsername());
return success(id);
}
@PreAuthorize("@ss.hasPermi('price:set:query')")
@PostMapping("/validate")
public AjaxResult validate(@RequestBody PrcPriceSet set)
{
return success(priceSetService.validate(set));
}
@PreAuthorize("@ss.hasPermi('price:set:list')")
@GetMapping("/detail/list")
public TableDataInfo detailList(PrcPriceSetDetail detail)
{
startPage();
List<PrcPriceSetDetail> list = priceSetService.selectDetailList(detail);
return getDataTable(list);
}
/**
* 版本明细搜索下拉选项:按版本去重后的 产品/国家/分区
*/
@PreAuthorize("@ss.hasPermi('price:set:list')")
@GetMapping("/detail/options")
public AjaxResult detailOptions(@RequestParam("psvId") Long psvId)
{
return success(priceSetService.selectDetailOptions(psvId));
}
@PreAuthorize("@ss.hasPermi('price:set:edit') or @ss.hasPermi('price:set:forceEdit')")
@Log(title = "套餐明细", businessType = BusinessType.INSERT)
@PostMapping("/detail")
public AjaxResult addDetail(@RequestBody PrcPriceSetDetail detail)
{
return toAjax(priceSetService.insertDetail(detail));
}
@PreAuthorize("@ss.hasPermi('price:set:edit') or @ss.hasPermi('price:set:forceEdit')")
@Log(title = "\u5957\u9910\u660e\u7ec6", businessType = BusinessType.UPDATE)
@PutMapping("/detail")
public AjaxResult editDetail(@RequestBody PrcPriceSetDetail detail)
{
return toAjax(priceSetService.updateDetail(detail));
}
/**
* Async batch write of detail surcharge snapshots. Returns taskId; poll /save/task/{taskId}.
*/
@PreAuthorize("@ss.hasPermi('price:set:edit') or @ss.hasPermi('price:set:forceEdit')")
@Log(title = "\u5957\u9910\u9644\u52a0\u8d39", businessType = BusinessType.UPDATE)
@PostMapping("/detail/surcharge/async")
public AjaxResult batchSurchargeAsync(@RequestBody List<PrcPriceSetDetail> details)
{
String taskId = priceSetService.submitDetailSurchargeAsync(details);
Map<String, Object> payload = new HashMap<String, Object>();
payload.put("taskId", taskId);
return success(payload);
}
@PreAuthorize("@ss.hasPermi('price:set:edit') or @ss.hasPermi('price:set:forceEdit')")
@Log(title = "\u5957\u9910\u660e\u7ec6", businessType = BusinessType.DELETE)
@DeleteMapping("/detail/{psdIds}")
public AjaxResult removeDetail(@PathVariable Long[] psdIds)
{
return toAjax(priceSetService.deleteDetailByIds(psdIds));
}
@PreAuthorize("@ss.hasPermi('price:set:list')")
@GetMapping("/customer/list")
public TableDataInfo customerList(PrcCustomerPriceSet customer)
{
startPage();
List<PrcCustomerPriceSet> list = priceSetService.selectCustomerList(customer);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('price:set:edit')")
@Log(title = "套餐客户绑定", businessType = BusinessType.INSERT)
@PostMapping("/customer")
public AjaxResult addCustomer(@RequestBody PrcCustomerPriceSet customer)
{
customer.setCreateBy(getUsername());
return toAjax(priceSetService.insertCustomer(customer));
}
@PreAuthorize("@ss.hasPermi('price:set:edit')")
@Log(title = "套餐客户绑定", businessType = BusinessType.UPDATE)
@PutMapping("/customer")
public AjaxResult editCustomer(@RequestBody PrcCustomerPriceSet customer)
{
customer.setUpdateBy(getUsername());
return toAjax(priceSetService.updateCustomer(customer));
}
@PreAuthorize("@ss.hasPermi('price:set:edit')")
@Log(title = "套餐客户绑定", businessType = BusinessType.DELETE)
@DeleteMapping("/customer/{customerPricesetIds}")
public AjaxResult removeCustomer(@PathVariable Long[] customerPricesetIds)
{
return toAjax(priceSetService.deleteCustomerByIds(customerPricesetIds));
}
}
@@ -0,0 +1,125 @@
package com.bms.web.controller.price;
import java.util.Date;
import java.util.List;
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.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.price.domain.BmsProduct;
import com.bms.price.domain.BmsProductCountry;
import com.bms.price.service.IBmsProductService;
/**
* 产品列表(含 Excel 导入)
*
* @author bms
*/
@RestController
@RequestMapping("/price/product")
public class ProductController extends BaseController
{
@Autowired
private IBmsProductService productService;
@PreAuthorize("@ss.hasPermi('price:product:list')")
@GetMapping("/list")
public TableDataInfo list(BmsProduct product)
{
startPage();
List<BmsProduct> list = productService.selectBmsProductList(product);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('price:product:query')")
@GetMapping("/{productCode}/countries")
public AjaxResult countries(@PathVariable String productCode,
@RequestParam(required = false) Date effectiveDate,
@RequestParam(required = false) Long productId)
{
List<BmsProductCountry> list = productService.selectCountryListByProductCode(productCode, effectiveDate, productId);
return success(list);
}
@PreAuthorize("@ss.hasPermi('price:product:query')")
@GetMapping("/{productId}")
public AjaxResult getInfo(@PathVariable Long productId)
{
return success(productService.selectBmsProductById(productId));
}
@PreAuthorize("@ss.hasPermi('price:product:add')")
@Log(title = "产品列表", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody BmsProduct product)
{
product.setCreateBy(getUsername());
return toAjax(productService.insertBmsProduct(product));
}
@PreAuthorize("@ss.hasPermi('price:product:edit')")
@Log(title = "产品列表", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody BmsProduct product)
{
product.setUpdateBy(getUsername());
return toAjax(productService.updateBmsProduct(product));
}
/**
* 修改产品国家明细行(重量段/运费/挂号费/尺寸/尾程/时效/生效失效日期)
*/
@PreAuthorize("@ss.hasPermi('price:product:edit')")
@Log(title = "产品国家明细", businessType = BusinessType.UPDATE)
@PutMapping("/country")
public AjaxResult editCountry(@RequestBody BmsProductCountry country)
{
return toAjax(productService.updateBmsProductCountry(country));
}
@PreAuthorize("@ss.hasPermi('price:product:remove')")
@Log(title = "产品列表", businessType = BusinessType.DELETE)
@DeleteMapping("/{productIds}")
public AjaxResult remove(@PathVariable Long[] productIds)
{
return toAjax(productService.deleteBmsProductByIds(productIds));
}
/**
* 导入威狮B2C小包价表.xlsx(产品目录 + 国家服务明细)
*/
@Log(title = "产品列表", businessType = BusinessType.IMPORT)
@PreAuthorize("@ss.hasPermi('price:product:import')")
@PostMapping("/importData")
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
{
String message = productService.importProductExcel(file, updateSupport, getUsername());
return success(message);
}
/**
* Import a single-country linehaul cost sheet. countryCode is the destination.
*/
@Log(title = "\u4ea7\u54c1\u5217\u8868", businessType = BusinessType.IMPORT)
@PreAuthorize("@ss.hasPermi('price:product:import')")
@PostMapping("/importCountryData")
public AjaxResult importCountryData(MultipartFile file, String countryCode) throws Exception
{
String message = productService.importCountryProductExcel(file, countryCode, getUsername());
return success(message);
}
}
@@ -0,0 +1,150 @@
package com.bms.web.controller.price;
import java.util.List;
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.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.price.domain.PrcSurcharge;
import com.bms.price.domain.PrcSurchargeRel;
import com.bms.price.service.IPrcSurchargeRelService;
import com.bms.price.service.IPrcSurchargeService;
/**
* 附加费配置
*
* @author bms
*/
@RestController
@RequestMapping("/price/surcharge")
public class SurchargeController extends BaseController
{
@Autowired
private IPrcSurchargeService surchargeService;
@Autowired
private IPrcSurchargeRelService surchargeRelService;
@PreAuthorize("@ss.hasPermi('price:surcharge:list')")
@GetMapping("/list")
public TableDataInfo list(PrcSurcharge surcharge)
{
startPage();
List<PrcSurcharge> list = surchargeService.selectPrcSurchargeList(surcharge);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('price:surcharge:query')")
@GetMapping("/{scId}")
public AjaxResult getInfo(@PathVariable Long scId)
{
return success(surchargeService.selectPrcSurchargeById(scId));
}
/**
* 查询指定产品已关联的附加费(非分页,供产品列表抽屉、套餐明细展示使用)
*/
@PreAuthorize("@ss.hasPermi('price:surcharge:query') or @ss.hasPermi('price:set:query') or @ss.hasPermi('price:set:add')")
@GetMapping("/byProduct/{productCode}")
public AjaxResult listByProduct(@PathVariable String productCode)
{
return success(surchargeRelService.selectRelByProductCode(productCode));
}
/**
* 附加费配置全量(非分页,供套餐明细勾选使用)
*
* 不要求 price:surcharge:list 权限,套餐维护角色(price:set:*)也能读,避免套餐页拿到 403 空列表。
*/
@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')")
@GetMapping("/options")
public AjaxResult listOptions(PrcSurcharge surcharge)
{
return success(surchargeService.selectPrcSurchargeList(surcharge));
}
/**
* 绑定附加费到产品/明细(body: scId, productId, productCode, detailId;detailId 空=产品级)
*/
@PreAuthorize("@ss.hasPermi('price:surcharge:add')")
@Log(title = "附加费关联", businessType = BusinessType.INSERT)
@PostMapping("/rel")
public AjaxResult addRel(@RequestBody PrcSurchargeRel rel)
{
rel.setCreateBy(getUsername());
return toAjax(surchargeRelService.insertPrcSurchargeRel(rel));
}
/**
* 解绑(删除关联记录,不删除附加费本身)
*/
@PreAuthorize("@ss.hasPermi('price:surcharge:remove')")
@Log(title = "附加费关联", businessType = BusinessType.DELETE)
@DeleteMapping("/rel/{relIds}")
public AjaxResult removeRel(@PathVariable Long[] relIds)
{
return toAjax(surchargeRelService.deletePrcSurchargeRelByIds(relIds));
}
@PreAuthorize("@ss.hasPermi('price:surcharge:add')")
@Log(title = "附加费配置", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody PrcSurcharge surcharge)
{
surcharge.setCreateBy(getUsername());
return toAjax(surchargeService.insertPrcSurcharge(surcharge));
}
/**
* 修改(版本化):内容无变化时返回 warn 提示,否则原记录作废 + 插入新记录,产品关联自动重指向新记录。
*/
@PreAuthorize("@ss.hasPermi('price:surcharge:edit')")
@Log(title = "附加费配置", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody PrcSurcharge surcharge)
{
surcharge.setUpdateBy(getUsername());
int rows = surchargeService.updatePrcSurcharge(surcharge);
if (rows == -1)
{
return AjaxResult.warn("未做任何修改");
}
return toAjax(rows);
}
/**
* 停用(原地修改 ss_code=of,不新增数据,不物理删除)。
*/
@PreAuthorize("@ss.hasPermi('price:surcharge:remove')")
@Log(title = "附加费停用", businessType = BusinessType.UPDATE)
@PutMapping("/disable/{scIds}")
public AjaxResult disable(@PathVariable Long[] scIds)
{
return toAjax(surchargeService.disablePrcSurchargeByIds(scIds, getUsername()));
}
/**
* 启用(原地修改 ss_code=on,不新增数据)。
*/
@PreAuthorize("@ss.hasPermi('price:surcharge:remove')")
@Log(title = "附加费启用", businessType = BusinessType.UPDATE)
@PutMapping("/enable/{scIds}")
public AjaxResult enable(@PathVariable Long[] scIds)
{
return toAjax(surchargeService.enablePrcSurchargeByIds(scIds, getUsername()));
}
}
@@ -0,0 +1,110 @@
package com.bms.web.controller.price;
import java.util.List;
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.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.price.domain.PrcWeightSectionDetail;
import com.bms.price.domain.PrcWeightSectionTemplate;
import com.bms.price.service.IPrcWeightService;
/**
* 重量段管理
*
* @author bms
*/
@RestController
@RequestMapping("/price/weight")
public class WeightController extends BaseController
{
@Autowired
private IPrcWeightService weightService;
@PreAuthorize("@ss.hasPermi('price:weight:list')")
@GetMapping("/template/list")
public TableDataInfo templateList(PrcWeightSectionTemplate template)
{
startPage();
List<PrcWeightSectionTemplate> list = weightService.selectPrcWeightSectionTemplateList(template);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('price:weight:query')")
@GetMapping("/template/{wstId}")
public AjaxResult getTemplate(@PathVariable Long wstId)
{
return success(weightService.selectPrcWeightSectionTemplateById(wstId));
}
@PreAuthorize("@ss.hasPermi('price:weight:add')")
@Log(title = "重量段模板", businessType = BusinessType.INSERT)
@PostMapping("/template")
public AjaxResult addTemplate(@Validated @RequestBody PrcWeightSectionTemplate template)
{
template.setCreateBy(getUsername());
return toAjax(weightService.insertPrcWeightSectionTemplate(template));
}
@PreAuthorize("@ss.hasPermi('price:weight:edit')")
@Log(title = "重量段模板", businessType = BusinessType.UPDATE)
@PutMapping("/template")
public AjaxResult editTemplate(@Validated @RequestBody PrcWeightSectionTemplate template)
{
template.setUpdateBy(getUsername());
return toAjax(weightService.updatePrcWeightSectionTemplate(template));
}
@PreAuthorize("@ss.hasPermi('price:weight:remove')")
@Log(title = "重量段模板", businessType = BusinessType.DELETE)
@DeleteMapping("/template/{wstIds}")
public AjaxResult removeTemplate(@PathVariable Long[] wstIds)
{
return toAjax(weightService.deletePrcWeightSectionTemplateByIds(wstIds));
}
@PreAuthorize("@ss.hasPermi('price:weight:list')")
@GetMapping("/detail/list")
public TableDataInfo detailList(PrcWeightSectionDetail detail)
{
startPage();
List<PrcWeightSectionDetail> list = weightService.selectPrcWeightSectionDetailList(detail);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('price:weight:add')")
@Log(title = "重量段明细", businessType = BusinessType.INSERT)
@PostMapping("/detail")
public AjaxResult addDetail(@Validated @RequestBody PrcWeightSectionDetail detail)
{
return toAjax(weightService.insertPrcWeightSectionDetail(detail));
}
@PreAuthorize("@ss.hasPermi('price:weight:edit')")
@Log(title = "重量段明细", businessType = BusinessType.UPDATE)
@PutMapping("/detail")
public AjaxResult editDetail(@Validated @RequestBody PrcWeightSectionDetail detail)
{
return toAjax(weightService.updatePrcWeightSectionDetail(detail));
}
@PreAuthorize("@ss.hasPermi('price:weight:remove')")
@Log(title = "重量段明细", businessType = BusinessType.DELETE)
@DeleteMapping("/detail/{wsdIds}")
public AjaxResult removeDetail(@PathVariable Long[] wsdIds)
{
return toAjax(weightService.deletePrcWeightSectionDetailByIds(wsdIds));
}
}
@@ -0,0 +1,147 @@
package com.bms.web.controller.price;
import java.util.List;
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 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.price.domain.PrcZone;
import com.bms.price.domain.PrcZoneDetail;
import com.bms.price.domain.PrcZoneRegion;
import com.bms.price.service.IPrcZoneService;
/**
* Zone management: bms_region -> zone -> postcode detail
*/
@RestController
@RequestMapping("/price/zone")
public class ZoneController extends BaseController
{
@Autowired
private IPrcZoneService zoneService;
// ========== countries that already have zones (from bms_region) ==========
@PreAuthorize("@ss.hasPermi('price:zone:list')")
@GetMapping("/country/list")
public TableDataInfo countryList(PrcZoneRegion query)
{
startPage();
List<PrcZoneRegion> list = zoneService.selectZoneRegionList(query);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('price:zone:query')")
@GetMapping("/country/{regionId}")
public AjaxResult getCountry(@PathVariable Long regionId)
{
return success(zoneService.selectZoneRegionById(regionId));
}
@PreAuthorize("@ss.hasPermi('price:zone:remove')")
@Log(title = "ZoneRegion", businessType = BusinessType.DELETE)
@DeleteMapping("/country/{regionIds}")
public AjaxResult removeCountryZones(@PathVariable Long[] regionIds)
{
return toAjax(zoneService.deleteZonesByRegionIds(regionIds));
}
// ========== zone ==========
@PreAuthorize("@ss.hasPermi('price:zone:list')")
@GetMapping("/item/list")
public TableDataInfo zoneList(PrcZone zone)
{
startPage();
List<PrcZone> list = zoneService.selectPrcZoneList(zone);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('price:zone:query')")
@GetMapping("/item/{zoneId}")
public AjaxResult getZone(@PathVariable Long zoneId)
{
return success(zoneService.selectPrcZoneById(zoneId));
}
@PreAuthorize("@ss.hasPermi('price:zone:add')")
@Log(title = "Zone", businessType = BusinessType.INSERT)
@PostMapping("/item")
public AjaxResult addZone(@Validated @RequestBody PrcZone zone)
{
return toAjax(zoneService.insertPrcZone(zone));
}
@PreAuthorize("@ss.hasPermi('price:zone:edit')")
@Log(title = "Zone", businessType = BusinessType.UPDATE)
@PutMapping("/item")
public AjaxResult editZone(@Validated @RequestBody PrcZone zone)
{
return toAjax(zoneService.updatePrcZone(zone));
}
@PreAuthorize("@ss.hasPermi('price:zone:remove')")
@Log(title = "Zone", businessType = BusinessType.DELETE)
@DeleteMapping("/item/{zoneIds}")
public AjaxResult removeZone(@PathVariable Long[] zoneIds)
{
return toAjax(zoneService.deletePrcZoneByIds(zoneIds));
}
// ========== detail ==========
@PreAuthorize("@ss.hasPermi('price:zone:list')")
@GetMapping("/detail/list")
public TableDataInfo detailList(PrcZoneDetail detail)
{
startPage();
List<PrcZoneDetail> list = zoneService.selectPrcZoneDetailList(detail);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('price:zone:add')")
@Log(title = "ZoneDetail", businessType = BusinessType.INSERT)
@PostMapping("/detail")
public AjaxResult addDetail(@Validated @RequestBody PrcZoneDetail detail)
{
return toAjax(zoneService.insertPrcZoneDetail(detail));
}
@PreAuthorize("@ss.hasPermi('price:zone:edit')")
@Log(title = "ZoneDetail", businessType = BusinessType.UPDATE)
@PutMapping("/detail")
public AjaxResult editDetail(@Validated @RequestBody PrcZoneDetail detail)
{
return toAjax(zoneService.updatePrcZoneDetail(detail));
}
@PreAuthorize("@ss.hasPermi('price:zone:remove')")
@Log(title = "ZoneDetail", businessType = BusinessType.DELETE)
@DeleteMapping("/detail/{detailIds}")
public AjaxResult removeDetail(@PathVariable Long[] detailIds)
{
return toAjax(zoneService.deletePrcZoneDetailByIds(detailIds));
}
@PreAuthorize("@ss.hasPermi('price:zone:add')")
@Log(title = "ZoneImport", businessType = BusinessType.IMPORT)
@PostMapping("/importData")
public AjaxResult importData(MultipartFile file) throws Exception
{
String message = zoneService.importZoneExcel(file, getUsername());
return success(message);
}
}
@@ -0,0 +1,66 @@
package com.bms.web.controller.system;
import java.util.List;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.customer.customermoney.domain.CustomerCreditGrant;
import com.bms.customer.customermoney.service.ICreditQuotaService;
import com.bms.customer.customermoney.vo.RoleCreditQuotaVo;
/**
* 角色可分配额度总额(角色额度池)管理
*
* @author bms
* @date 2026-09-20
*/
@RestController
@RequestMapping("/system/role/creditQuota")
public class RoleCreditQuotaController extends BaseController
{
@Autowired
private ICreditQuotaService creditQuotaService;
/**
* 全部角色额度池列表(含已分配/剩余可分配,实时统计)
*/
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/list")
public AjaxResult list()
{
return success(creditQuotaService.listRoleQuotas());
}
/**
* 设置角色可分配额度总额(不能小于该池当前已分配额)
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色额度池设置", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult set(@RequestBody RoleCreditQuotaVo body)
{
creditQuotaService.setRoleQuota(body.getRoleId(), body.getTotalQuota(), body.getRemark());
return success();
}
/**
* 额度池占用台账查询(可按角色/客户/类型/状态筛选)
*/
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/grants")
public TableDataInfo grants(CustomerCreditGrant query)
{
startPage();
List<CustomerCreditGrant> list = creditQuotaService.listGrants(query);
return getDataTable(list);
}
}
@@ -0,0 +1,133 @@
package com.bms.web.controller.system;
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.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.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.poi.ExcelUtil;
import com.bms.system.domain.SysConfig;
import com.bms.system.service.ISysConfigService;
/**
* 参数配置 信息操作处理
*
* @author bms
*/
@RestController
@RequestMapping("/system/config")
public class SysConfigController extends BaseController
{
@Autowired
private ISysConfigService configService;
/**
* 获取参数配置列表
*/
@PreAuthorize("@ss.hasPermi('system:config:list')")
@GetMapping("/list")
public TableDataInfo list(SysConfig config)
{
startPage();
List<SysConfig> list = configService.selectConfigList(config);
return getDataTable(list);
}
@Log(title = "参数管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:config:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysConfig config)
{
List<SysConfig> list = configService.selectConfigList(config);
ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class);
util.exportExcel(response, list, "参数数据");
}
/**
* 根据参数编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:config:query')")
@GetMapping(value = "/{configId}")
public AjaxResult getInfo(@PathVariable Long configId)
{
return success(configService.selectConfigById(configId));
}
/**
* 根据参数键名查询参数值
*/
@GetMapping(value = "/configKey/{configKey}")
public AjaxResult getConfigKey(@PathVariable String configKey)
{
return success(configService.selectConfigByKey(configKey));
}
/**
* 新增参数配置
*/
@PreAuthorize("@ss.hasPermi('system:config:add')")
@Log(title = "参数管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysConfig config)
{
if (!configService.checkConfigKeyUnique(config))
{
return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
config.setCreateBy(getUsername());
return toAjax(configService.insertConfig(config));
}
/**
* 修改参数配置
*/
@PreAuthorize("@ss.hasPermi('system:config:edit')")
@Log(title = "参数管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysConfig config)
{
if (!configService.checkConfigKeyUnique(config))
{
return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
config.setUpdateBy(getUsername());
return toAjax(configService.updateConfig(config));
}
/**
* 删除参数配置
*/
@PreAuthorize("@ss.hasPermi('system:config:remove')")
@Log(title = "参数管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{configIds}")
public AjaxResult remove(@PathVariable Long[] configIds)
{
configService.deleteConfigByIds(configIds);
return success();
}
/**
* 刷新参数缓存
*/
@PreAuthorize("@ss.hasPermi('system:config:remove')")
@Log(title = "参数管理", businessType = BusinessType.CLEAN)
@DeleteMapping("/refreshCache")
public AjaxResult refreshCache()
{
configService.resetConfigCache();
return success();
}
}
@@ -0,0 +1,147 @@
package com.bms.web.controller.system;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.ArrayUtils;
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.UserConstants;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.domain.entity.SysDept;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.StringUtils;
import com.bms.system.service.ISysDeptService;
/**
* 部门信息
*
* @author bms
*/
@RestController
@RequestMapping("/system/dept")
public class SysDeptController extends BaseController
{
@Autowired
private ISysDeptService deptService;
/**
* 获取部门列表
*/
@PreAuthorize("@ss.hasPermi('system:dept:list')")
@GetMapping("/list")
public AjaxResult list(SysDept dept)
{
List<SysDept> depts = deptService.selectDeptList(dept);
return success(depts);
}
/**
* 查询部门列表(排除节点)
*/
@PreAuthorize("@ss.hasPermi('system:dept:list')")
@GetMapping("/list/exclude/{deptId}")
public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId)
{
List<SysDept> depts = deptService.selectDeptList(new SysDept());
depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
return success(depts);
}
/**
* 根据部门编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:dept:query')")
@GetMapping(value = "/{deptId}")
public AjaxResult getInfo(@PathVariable Long deptId)
{
deptService.checkDeptDataScope(deptId);
return success(deptService.selectDeptById(deptId));
}
/**
* 新增部门
*/
@PreAuthorize("@ss.hasPermi('system:dept:add')")
@Log(title = "部门管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDept dept)
{
if (!deptService.checkDeptNameUnique(dept))
{
return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}
dept.setCreateBy(getUsername());
return toAjax(deptService.insertDept(dept));
}
/**
* 修改部门
*/
@PreAuthorize("@ss.hasPermi('system:dept:edit')")
@Log(title = "部门管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDept dept)
{
Long deptId = dept.getDeptId();
deptService.checkDeptDataScope(deptId);
if (!deptService.checkDeptNameUnique(dept))
{
return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}
else if (dept.getParentId().equals(deptId))
{
return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
}
else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0)
{
return error("该部门包含未停用的子部门!");
}
dept.setUpdateBy(getUsername());
return toAjax(deptService.updateDept(dept));
}
/**
* 保存部门排序
*/
@PreAuthorize("@ss.hasPermi('system:dept:edit')")
@Log(title = "保存部门排序", businessType = BusinessType.UPDATE)
@PutMapping("/updateSort")
public AjaxResult updateSort(@RequestBody Map<String, String> params)
{
String[] deptIds = params.get("deptIds").split(",");
String[] orderNums = params.get("orderNums").split(",");
deptService.updateDeptSort(deptIds, orderNums);
return success();
}
/**
* 删除部门
*/
@PreAuthorize("@ss.hasPermi('system:dept:remove')")
@Log(title = "部门管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{deptId}")
public AjaxResult remove(@PathVariable Long deptId)
{
if (deptService.hasChildByDeptId(deptId))
{
return warn("存在下级部门,不允许删除");
}
if (deptService.checkDeptExistUser(deptId))
{
return warn("部门存在用户,不允许删除");
}
deptService.checkDeptDataScope(deptId);
return toAjax(deptService.deleteDeptById(deptId));
}
}
@@ -0,0 +1,132 @@
package com.bms.web.controller.system;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import jakarta.servlet.http.HttpServletResponse;
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.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.domain.entity.SysDictData;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.StringUtils;
import com.bms.common.utils.poi.ExcelUtil;
import com.bms.system.service.ISysDictDataService;
import com.bms.system.service.ISysDictTypeService;
/**
* 数据字典信息
*
* @author bms
*/
@RestController
@RequestMapping("/system/dict/data")
public class SysDictDataController extends BaseController
{
@Autowired
private ISysDictDataService dictDataService;
@Autowired
private ISysDictTypeService dictTypeService;
@PreAuthorize("@ss.hasPermi('system:dict:list')")
@GetMapping("/list")
public TableDataInfo list(SysDictData dictData)
{
startPage();
List<SysDictData> list = dictDataService.selectDictDataList(dictData);
return getDataTable(list);
}
@Log(title = "字典数据", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:dict:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysDictData dictData)
{
List<SysDictData> list = dictDataService.selectDictDataList(dictData);
ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class);
util.exportExcel(response, list, "字典数据");
}
/**
* 全量查询正常状态字典数据(按 dictType 分组)
*/
@GetMapping(value = "/all")
public AjaxResult all()
{
Map<String, List<SysDictData>> data = dictTypeService.selectDictDataAllMap();
return success(data);
}
/**
* 查询字典数据详细
*/
@PreAuthorize("@ss.hasPermi('system:dict:query')")
@GetMapping(value = "/{dictCode}")
public AjaxResult getInfo(@PathVariable Long dictCode)
{
return success(dictDataService.selectDictDataById(dictCode));
}
/**
* 根据字典类型查询字典数据信息
*/
@GetMapping(value = "/type/{dictType}")
public AjaxResult dictType(@PathVariable String dictType)
{
List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
if (StringUtils.isNull(data))
{
data = new ArrayList<SysDictData>();
}
return success(data);
}
/**
* 新增字典类型
*/
@PreAuthorize("@ss.hasPermi('system:dict:add')")
@Log(title = "字典数据", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDictData dict)
{
dict.setCreateBy(getUsername());
return toAjax(dictDataService.insertDictData(dict));
}
/**
* 修改保存字典类型
*/
@PreAuthorize("@ss.hasPermi('system:dict:edit')")
@Log(title = "字典数据", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDictData dict)
{
dict.setUpdateBy(getUsername());
return toAjax(dictDataService.updateDictData(dict));
}
/**
* 删除字典类型
*/
@PreAuthorize("@ss.hasPermi('system:dict:remove')")
@Log(title = "字典类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictCodes}")
public AjaxResult remove(@PathVariable Long[] dictCodes)
{
dictDataService.deleteDictDataByIds(dictCodes);
return success();
}
}
@@ -0,0 +1,131 @@
package com.bms.web.controller.system;
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.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.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.domain.entity.SysDictType;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.poi.ExcelUtil;
import com.bms.system.service.ISysDictTypeService;
/**
* 数据字典信息
*
* @author bms
*/
@RestController
@RequestMapping("/system/dict/type")
public class SysDictTypeController extends BaseController
{
@Autowired
private ISysDictTypeService dictTypeService;
@PreAuthorize("@ss.hasPermi('system:dict:list')")
@GetMapping("/list")
public TableDataInfo list(SysDictType dictType)
{
startPage();
List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
return getDataTable(list);
}
@Log(title = "字典类型", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:dict:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysDictType dictType)
{
List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class);
util.exportExcel(response, list, "字典类型");
}
/**
* 查询字典类型详细
*/
@PreAuthorize("@ss.hasPermi('system:dict:query')")
@GetMapping(value = "/{dictId}")
public AjaxResult getInfo(@PathVariable Long dictId)
{
return success(dictTypeService.selectDictTypeById(dictId));
}
/**
* 新增字典类型
*/
@PreAuthorize("@ss.hasPermi('system:dict:add')")
@Log(title = "字典类型", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDictType dict)
{
if (!dictTypeService.checkDictTypeUnique(dict))
{
return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
dict.setCreateBy(getUsername());
return toAjax(dictTypeService.insertDictType(dict));
}
/**
* 修改字典类型
*/
@PreAuthorize("@ss.hasPermi('system:dict:edit')")
@Log(title = "字典类型", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDictType dict)
{
if (!dictTypeService.checkDictTypeUnique(dict))
{
return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
dict.setUpdateBy(getUsername());
return toAjax(dictTypeService.updateDictType(dict));
}
/**
* 删除字典类型
*/
@PreAuthorize("@ss.hasPermi('system:dict:remove')")
@Log(title = "字典类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictIds}")
public AjaxResult remove(@PathVariable Long[] dictIds)
{
dictTypeService.deleteDictTypeByIds(dictIds);
return success();
}
/**
* 刷新字典缓存
*/
@PreAuthorize("@ss.hasPermi('system:dict:remove')")
@Log(title = "字典类型", businessType = BusinessType.CLEAN)
@DeleteMapping("/refreshCache")
public AjaxResult refreshCache()
{
dictTypeService.resetDictCache();
return success();
}
/**
* 获取字典选择框列表
*/
@GetMapping("/optionselect")
public AjaxResult optionselect()
{
List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();
return success(dictTypes);
}
}
@@ -0,0 +1,64 @@
package com.bms.web.controller.system;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.config.BmsConfig;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.domain.entity.SysUser;
import com.bms.common.utils.SecurityUtils;
import com.bms.common.utils.StringUtils;
import com.bms.system.service.ISysUserService;
/**
* 首页
*
* @author bms
*/
@RestController
public class SysIndexController
{
/** 系统基础配置 */
@Autowired
private BmsConfig bmsConfig;
@Autowired
private ISysUserService userService;
/**
* 访问首页,提示语
*/
@RequestMapping("/")
public String index()
{
return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", bmsConfig.getName(), bmsConfig.getVersion());
}
/**
* 解锁屏幕
*/
@PostMapping("/unlockscreen")
public AjaxResult unlockScreen(@RequestBody Map<String, String> body)
{
String password = body.get("password");
if (StringUtils.isEmpty(password))
{
return AjaxResult.error("密码不能为空");
}
String username = SecurityUtils.getUsername();
SysUser user = userService.selectUserByUserName(username);
if (user == null)
{
return AjaxResult.error("服务器超时,请重新登录");
}
if (!SecurityUtils.matchesPassword(password, user.getPassword()))
{
return AjaxResult.error("密码错误,请重新输入");
}
return AjaxResult.success("解锁成功");
}
}
@@ -0,0 +1,138 @@
package com.bms.web.controller.system;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
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.RestController;
import com.bms.common.constant.Constants;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.domain.entity.SysMenu;
import com.bms.common.core.domain.entity.SysUser;
import com.bms.common.core.domain.model.LoginBody;
import com.bms.common.core.domain.model.LoginUser;
import com.bms.common.core.text.Convert;
import com.bms.common.utils.DateUtils;
import com.bms.common.utils.SecurityUtils;
import com.bms.common.utils.StringUtils;
import com.bms.framework.web.service.SysLoginService;
import com.bms.framework.web.service.SysPermissionService;
import com.bms.framework.web.service.TokenService;
import com.bms.system.service.ISysConfigService;
import com.bms.system.service.ISysMenuService;
/**
* 登录验证
*
* @author bms
*/
@RestController
public class SysLoginController
{
@Autowired
private SysLoginService loginService;
@Autowired
private ISysMenuService menuService;
@Autowired
private SysPermissionService permissionService;
@Autowired
private TokenService tokenService;
@Autowired
private ISysConfigService configService;
/**
* 登录方法
*
* @param loginBody 登录信息
* @return 结果
*/
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody)
{
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
loginBody.getUuid());
ajax.put(Constants.TOKEN, token);
return ajax;
}
/**
* 获取用户信息
*
* @return 用户信息
*/
@GetMapping("getInfo")
public AjaxResult getInfo()
{
LoginUser loginUser = SecurityUtils.getLoginUser();
SysUser user = loginUser.getUser();
// 角色集合
Set<String> roles = permissionService.getRolePermission(user);
// 权限集合
Set<String> permissions = permissionService.getMenuPermission(user);
if (!loginUser.getPermissions().equals(permissions))
{
loginUser.setPermissions(permissions);
tokenService.refreshToken(loginUser);
}
AjaxResult ajax = AjaxResult.success();
ajax.put("user", user);
ajax.put("roles", roles);
ajax.put("permissions", permissions);
ajax.put("pwdChrtype", getSysAccountChrtype());
ajax.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate()));
ajax.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate()));
return ajax;
}
/**
* 获取路由信息
*
* @return 路由信息
*/
@GetMapping("getRouters")
public AjaxResult getRouters()
{
Long userId = SecurityUtils.getUserId();
List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
return AjaxResult.success(menuService.buildMenus(menus));
}
// 获取用户密码自定义配置规则
public String getSysAccountChrtype()
{
return Convert.toStr(configService.selectConfigByKey("sys.account.chrtype"), "0");
}
// 检查初始密码是否提醒修改
public boolean initPasswordIsModify(Date pwdUpdateDate)
{
Integer initPasswordModify = Convert.toInt(configService.selectConfigByKey("sys.account.initPasswordModify"));
return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null;
}
// 检查密码是否过期
public boolean passwordIsExpiration(Date pwdUpdateDate)
{
Integer passwordValidateDays = Convert.toInt(configService.selectConfigByKey("sys.account.passwordValidateDays"));
if (passwordValidateDays != null && passwordValidateDays > 0)
{
if (StringUtils.isNull(pwdUpdateDate))
{
// 如果从未修改过初始密码,直接提醒过期
return true;
}
Date nowDate = DateUtils.getNowDate();
return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays;
}
return false;
}
}
@@ -0,0 +1,165 @@
package com.bms.web.controller.system;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.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.UserConstants;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.domain.entity.SysMenu;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.StringUtils;
import com.bms.system.service.ISysMenuService;
/**
* 菜单信息
*
* @author bms
*/
@RestController
@RequestMapping("/system/menu")
public class SysMenuController extends BaseController
{
@Autowired
private ISysMenuService menuService;
/**
* 获取菜单列表
*/
@PreAuthorize("@ss.hasPermi('system:menu:list')")
@GetMapping("/list")
public AjaxResult list(SysMenu menu)
{
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
return success(menus);
}
/**
* 根据菜单编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:menu:query')")
@GetMapping(value = "/{menuId}")
public AjaxResult getInfo(@PathVariable Long menuId)
{
return success(menuService.selectMenuById(menuId));
}
/**
* 获取菜单下拉树列表
*/
@GetMapping("/treeselect")
public AjaxResult treeselect(SysMenu menu)
{
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
return success(menuService.buildMenuTreeSelect(menus));
}
/**
* 加载对应角色菜单列表树
*/
@GetMapping(value = "/roleMenuTreeselect/{roleId}")
public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId)
{
List<SysMenu> menus = menuService.selectMenuList(getUserId());
AjaxResult ajax = AjaxResult.success();
ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
ajax.put("menus", menuService.buildMenuTreeSelect(menus));
return ajax;
}
/**
* 新增菜单
*/
@PreAuthorize("@ss.hasPermi('system:menu:add')")
@Log(title = "菜单管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysMenu menu)
{
if (!menuService.checkMenuNameUnique(menu))
{
return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
{
return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
}
else if (!menuService.checkRouteConfigUnique(menu))
{
return error("新增菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
}
menu.setCreateBy(getUsername());
return toAjax(menuService.insertMenu(menu));
}
/**
* 修改菜单
*/
@PreAuthorize("@ss.hasPermi('system:menu:edit')")
@Log(title = "菜单管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysMenu menu)
{
if (!menuService.checkMenuNameUnique(menu))
{
return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
{
return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
}
else if (menu.getMenuId().equals(menu.getParentId()))
{
return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
}
else if (!menuService.checkRouteConfigUnique(menu))
{
return error("修改菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
}
menu.setUpdateBy(getUsername());
return toAjax(menuService.updateMenu(menu));
}
/**
* 保存菜单排序
*/
@PreAuthorize("@ss.hasPermi('system:menu:edit')")
@Log(title = "保存菜单排序", businessType = BusinessType.UPDATE)
@PutMapping("/updateSort")
public AjaxResult updateSort(@RequestBody Map<String, String> params)
{
String[] menuIds = params.get("menuIds").split(",");
String[] orderNums = params.get("orderNums").split(",");
menuService.updateMenuSort(menuIds, orderNums);
return success();
}
/**
* 删除菜单
*/
@PreAuthorize("@ss.hasPermi('system:menu:remove')")
@Log(title = "菜单管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{menuId}")
public AjaxResult remove(@PathVariable("menuId") Long menuId)
{
if (menuService.hasChildByMenuId(menuId))
{
return warn("存在子菜单,不允许删除");
}
if (menuService.checkMenuExistRole(menuId))
{
return warn("菜单已分配,不允许删除");
}
return toAjax(menuService.deleteMenuById(menuId));
}
}
@@ -0,0 +1,150 @@
package com.bms.web.controller.system;
import java.util.List;
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.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.core.text.Convert;
import com.bms.common.enums.BusinessType;
import com.bms.system.domain.SysNotice;
import com.bms.system.service.ISysNoticeReadService;
import com.bms.system.service.ISysNoticeService;
/**
* 公告 信息操作处理
*
* @author bms
*/
@RestController
@RequestMapping("/system/notice")
public class SysNoticeController extends BaseController
{
@Autowired
private ISysNoticeService noticeService;
@Autowired
private ISysNoticeReadService noticeReadService;
/**
* 获取通知公告列表
*/
@PreAuthorize("@ss.hasPermi('system:notice:list')")
@GetMapping("/list")
public TableDataInfo list(SysNotice notice)
{
startPage();
List<SysNotice> list = noticeService.selectNoticeList(notice);
return getDataTable(list);
}
/**
* 根据通知公告编号获取详细信息
*/
@GetMapping(value = "/{noticeId}")
public AjaxResult getInfo(@PathVariable Long noticeId)
{
return success(noticeService.selectNoticeById(noticeId));
}
/**
* 新增通知公告
*/
@PreAuthorize("@ss.hasPermi('system:notice:add')")
@Log(title = "通知公告", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysNotice notice)
{
notice.setCreateBy(getUsername());
return toAjax(noticeService.insertNotice(notice));
}
/**
* 修改通知公告
*/
@PreAuthorize("@ss.hasPermi('system:notice:edit')")
@Log(title = "通知公告", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysNotice notice)
{
notice.setUpdateBy(getUsername());
return toAjax(noticeService.updateNotice(notice));
}
/**
* 首页顶部公告列表(返回全部正常公告,带当前用户已读标记,最多5条)
*/
@GetMapping("/listTop")
@ResponseBody
public AjaxResult listTop()
{
Long userId = getUserId();
List<SysNotice> list = noticeReadService.selectNoticeListWithReadStatus(userId, 5);
long unreadCount = list.stream().filter(n -> !n.getIsRead()).count();
AjaxResult result = AjaxResult.success(list);
result.put("unreadCount", unreadCount);
return result;
}
/**
* 标记公告已读
*/
@PostMapping("/markRead")
@ResponseBody
public AjaxResult markRead(Long noticeId)
{
Long userId = getUserId();
noticeReadService.markRead(noticeId, userId);
return success();
}
/**
* 批量标记已读
*/
@PostMapping("/markReadAll")
@ResponseBody
public AjaxResult markReadAll(String ids)
{
Long userId = getUserId();
Long[] noticeIds = Convert.toLongArray(ids);
noticeReadService.markReadBatch(userId, noticeIds);
return success();
}
/**
* 已读用户列表数据
*/
@PreAuthorize("@ss.hasPermi('system:notice:list')")
@GetMapping("/readUsers/list")
@ResponseBody
public TableDataInfo readUsersList(Long noticeId, String searchValue)
{
startPage();
List<?> list = noticeReadService.selectReadUsersByNoticeId(noticeId, searchValue);
return getDataTable(list);
}
/**
* 删除通知公告
*/
@PreAuthorize("@ss.hasPermi('system:notice:remove')")
@Log(title = "通知公告", businessType = BusinessType.DELETE)
@DeleteMapping("/{noticeIds}")
public AjaxResult remove(@PathVariable Long[] noticeIds)
{
noticeReadService.deleteByNoticeIds(noticeIds);
return toAjax(noticeService.deleteNoticeByIds(noticeIds));
}
}
@@ -0,0 +1,129 @@
package com.bms.web.controller.system;
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.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.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.poi.ExcelUtil;
import com.bms.system.domain.SysPost;
import com.bms.system.service.ISysPostService;
/**
* 岗位信息操作处理
*
* @author bms
*/
@RestController
@RequestMapping("/system/post")
public class SysPostController extends BaseController
{
@Autowired
private ISysPostService postService;
/**
* 获取岗位列表
*/
@PreAuthorize("@ss.hasPermi('system:post:list')")
@GetMapping("/list")
public TableDataInfo list(SysPost post)
{
startPage();
List<SysPost> list = postService.selectPostList(post);
return getDataTable(list);
}
@Log(title = "岗位管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:post:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysPost post)
{
List<SysPost> list = postService.selectPostList(post);
ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class);
util.exportExcel(response, list, "岗位数据");
}
/**
* 根据岗位编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:post:query')")
@GetMapping(value = "/{postId}")
public AjaxResult getInfo(@PathVariable Long postId)
{
return success(postService.selectPostById(postId));
}
/**
* 新增岗位
*/
@PreAuthorize("@ss.hasPermi('system:post:add')")
@Log(title = "岗位管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysPost post)
{
if (!postService.checkPostNameUnique(post))
{
return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
}
else if (!postService.checkPostCodeUnique(post))
{
return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
post.setCreateBy(getUsername());
return toAjax(postService.insertPost(post));
}
/**
* 修改岗位
*/
@PreAuthorize("@ss.hasPermi('system:post:edit')")
@Log(title = "岗位管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysPost post)
{
if (!postService.checkPostNameUnique(post))
{
return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
}
else if (!postService.checkPostCodeUnique(post))
{
return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
post.setUpdateBy(getUsername());
return toAjax(postService.updatePost(post));
}
/**
* 删除岗位
*/
@PreAuthorize("@ss.hasPermi('system:post:remove')")
@Log(title = "岗位管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{postIds}")
public AjaxResult remove(@PathVariable Long[] postIds)
{
return toAjax(postService.deletePostByIds(postIds));
}
/**
* 获取岗位选择框列表
*/
@GetMapping("/optionselect")
public AjaxResult optionselect()
{
List<SysPost> posts = postService.selectPostAll();
return success(posts);
}
}
@@ -0,0 +1,149 @@
package com.bms.web.controller.system;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
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.config.BmsConfig;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.domain.entity.SysUser;
import com.bms.common.core.domain.model.LoginUser;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.DateUtils;
import com.bms.common.utils.SecurityUtils;
import com.bms.common.utils.StringUtils;
import com.bms.common.utils.file.FileUploadUtils;
import com.bms.common.utils.file.FileUtils;
import com.bms.common.utils.file.MimeTypeUtils;
import com.bms.framework.web.service.TokenService;
import com.bms.system.service.ISysUserService;
/**
* 个人信息 业务处理
*
* @author bms
*/
@RestController
@RequestMapping("/system/user/profile")
public class SysProfileController extends BaseController
{
@Autowired
private ISysUserService userService;
@Autowired
private TokenService tokenService;
/**
* 个人信息
*/
@GetMapping
public AjaxResult profile()
{
LoginUser loginUser = getLoginUser();
SysUser user = loginUser.getUser();
AjaxResult ajax = AjaxResult.success(user);
ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
return ajax;
}
/**
* 修改用户
*/
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult updateProfile(@RequestBody SysUser user)
{
LoginUser loginUser = getLoginUser();
SysUser currentUser = loginUser.getUser();
currentUser.setNickName(user.getNickName());
currentUser.setEmail(user.getEmail());
currentUser.setPhonenumber(user.getPhonenumber());
currentUser.setSex(user.getSex());
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
{
return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在");
}
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser))
{
return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
}
if (userService.updateUserProfile(currentUser) > 0)
{
// 更新缓存用户信息
tokenService.setLoginUser(loginUser);
return success();
}
return error("修改个人信息异常,请联系管理员");
}
/**
* 重置密码
*/
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd")
public AjaxResult updatePwd(@RequestBody Map<String, String> params)
{
String oldPassword = params.get("oldPassword");
String newPassword = params.get("newPassword");
LoginUser loginUser = getLoginUser();
Long userId = loginUser.getUserId();
SysUser user = userService.selectUserById(userId);
String password = user.getPassword();
if (!SecurityUtils.matchesPassword(oldPassword, password))
{
return error("修改密码失败,旧密码错误");
}
if (SecurityUtils.matchesPassword(newPassword, password))
{
return error("新密码不能与旧密码相同");
}
newPassword = SecurityUtils.encryptPassword(newPassword);
if (userService.resetUserPwd(userId, newPassword) > 0)
{
// 更新缓存用户密码&密码最后更新时间
loginUser.getUser().setPwdUpdateDate(DateUtils.getNowDate());
loginUser.getUser().setPassword(newPassword);
tokenService.setLoginUser(loginUser);
return success();
}
return error("修改密码异常,请联系管理员");
}
/**
* 头像上传
*/
@Log(title = "用户头像", businessType = BusinessType.UPDATE)
@PostMapping("/avatar")
public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception
{
if (!file.isEmpty())
{
LoginUser loginUser = getLoginUser();
String avatar = FileUploadUtils.upload(BmsConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true);
if (userService.updateUserAvatar(loginUser.getUserId(), avatar))
{
String oldAvatar = loginUser.getUser().getAvatar();
if (StringUtils.isNotEmpty(oldAvatar))
{
FileUtils.deleteFile(BmsConfig.getProfile() + FileUtils.stripPrefix(oldAvatar));
}
AjaxResult ajax = AjaxResult.success();
ajax.put("imgUrl", avatar);
// 更新缓存用户头像
loginUser.getUser().setAvatar(avatar);
tokenService.setLoginUser(loginUser);
return ajax;
}
}
return error("上传图片异常,请联系管理员");
}
}
@@ -0,0 +1,38 @@
package com.bms.web.controller.system;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.domain.model.RegisterBody;
import com.bms.common.utils.StringUtils;
import com.bms.framework.web.service.SysRegisterService;
import com.bms.system.service.ISysConfigService;
/**
* 注册验证
*
* @author bms
*/
@RestController
public class SysRegisterController extends BaseController
{
@Autowired
private SysRegisterService registerService;
@Autowired
private ISysConfigService configService;
@PostMapping("/register")
public AjaxResult register(@RequestBody RegisterBody user)
{
if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser"))))
{
return error("当前系统没有开启注册功能!");
}
String msg = registerService.register(user);
return StringUtils.isEmpty(msg) ? success() : error(msg);
}
}
@@ -0,0 +1,254 @@
package com.bms.web.controller.system;
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.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.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.domain.entity.SysDept;
import com.bms.common.core.domain.entity.SysRole;
import com.bms.common.core.domain.entity.SysUser;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.poi.ExcelUtil;
import com.bms.framework.web.service.SysPermissionService;
import com.bms.framework.web.service.TokenService;
import com.bms.system.domain.SysUserRole;
import com.bms.system.service.ISysDeptService;
import com.bms.system.service.ISysRoleService;
import com.bms.system.service.ISysUserService;
/**
* 角色信息
*
* @author bms
*/
@RestController
@RequestMapping("/system/role")
public class SysRoleController extends BaseController
{
@Autowired
private ISysRoleService roleService;
@Autowired
private TokenService tokenService;
@Autowired
private SysPermissionService permissionService;
@Autowired
private ISysUserService userService;
@Autowired
private ISysDeptService deptService;
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/list")
public TableDataInfo list(SysRole role)
{
startPage();
List<SysRole> list = roleService.selectRoleList(role);
return getDataTable(list);
}
@Log(title = "角色管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:role:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysRole role)
{
List<SysRole> list = roleService.selectRoleList(role);
ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class);
util.exportExcel(response, list, "角色数据");
}
/**
* 根据角色编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping(value = "/{roleId}")
public AjaxResult getInfo(@PathVariable Long roleId)
{
roleService.checkRoleDataScope(roleId);
return success(roleService.selectRoleById(roleId));
}
/**
* 新增角色
*/
@PreAuthorize("@ss.hasPermi('system:role:add')")
@Log(title = "角色管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysRole role)
{
if (!roleService.checkRoleNameUnique(role))
{
return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
}
else if (!roleService.checkRoleKeyUnique(role))
{
return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
role.setCreateBy(getUsername());
return toAjax(roleService.insertRole(role));
}
/**
* 修改保存角色
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysRole role)
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
if (!roleService.checkRoleNameUnique(role))
{
return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
}
else if (!roleService.checkRoleKeyUnique(role))
{
return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
role.setUpdateBy(getUsername());
if (roleService.updateRole(role) > 0)
{
// 刷新所有持有该角色的在线用户权限
tokenService.refreshPermissionByRoleId(role.getRoleId(), permissionService);
return success();
}
return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
}
/**
* 修改保存数据权限
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/dataScope")
public AjaxResult dataScope(@RequestBody SysRole role)
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
return toAjax(roleService.authDataScope(role));
}
/**
* 状态修改
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
public AjaxResult changeStatus(@RequestBody SysRole role)
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
role.setUpdateBy(getUsername());
return toAjax(roleService.updateRoleStatus(role));
}
/**
* 删除角色
*/
@PreAuthorize("@ss.hasPermi('system:role:remove')")
@Log(title = "角色管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{roleIds}")
public AjaxResult remove(@PathVariable Long[] roleIds)
{
return toAjax(roleService.deleteRoleByIds(roleIds));
}
/**
* 获取角色选择框列表
*/
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping("/optionselect")
public AjaxResult optionselect()
{
return success(roleService.selectRoleAll());
}
/**
* 查询已分配用户角色列表
*/
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/authUser/allocatedList")
public TableDataInfo allocatedList(SysUser user)
{
startPage();
List<SysUser> list = userService.selectAllocatedList(user);
return getDataTable(list);
}
/**
* 查询未分配用户角色列表
*/
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/authUser/unallocatedList")
public TableDataInfo unallocatedList(SysUser user)
{
startPage();
List<SysUser> list = userService.selectUnallocatedList(user);
return getDataTable(list);
}
/**
* 取消授权用户
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/cancel")
public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole)
{
return toAjax(roleService.deleteAuthUser(userRole));
}
/**
* 批量取消授权用户
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/cancelAll")
public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds)
{
return toAjax(roleService.deleteAuthUsers(roleId, userIds));
}
/**
* 批量选择用户授权
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/selectAll")
public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds)
{
roleService.checkRoleDataScope(roleId);
return toAjax(roleService.insertAuthUsers(roleId, userIds));
}
/**
* 获取对应角色部门树列表
*/
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping(value = "/deptTree/{roleId}")
public AjaxResult deptTree(@PathVariable("roleId") Long roleId)
{
AjaxResult ajax = AjaxResult.success();
ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
return ajax;
}
}
@@ -0,0 +1,279 @@
package com.bms.web.controller.system;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
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 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.domain.entity.SysDept;
import com.bms.common.core.domain.entity.SysRole;
import com.bms.common.core.domain.entity.SysUser;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.SecurityUtils;
import com.bms.common.utils.StringUtils;
import com.bms.common.utils.poi.ExcelUtil;
import com.bms.system.service.ISysDeptService;
import com.bms.system.service.ISysPostService;
import com.bms.system.service.ISysRoleService;
import com.bms.system.service.ISysUserService;
/**
* 用户信息
*
* @author bms
*/
@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController
{
@Autowired
private ISysUserService userService;
@Autowired
private ISysRoleService roleService;
@Autowired
private ISysDeptService deptService;
@Autowired
private ISysPostService postService;
/**
* 获取用户列表
*/
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/list")
public TableDataInfo list(SysUser user)
{
startPage();
List<SysUser> list = userService.selectUserList(user);
return getDataTable(list);
}
/**
* 获取用户下拉选项(业务员/客服/采购员等选择用)。
* 不加 @PreAuthorize:登录即可访问,只返回 id/账号/姓名/部门等展示字段,
* 避免给业务角色额外开放 system:user:list(含手机号/邮箱等完整信息)。
*/
@GetMapping("/optionselect")
public AjaxResult optionselect()
{
List<Map<String, Object>> options = userService.selectUserOptions().stream().map(u -> {
Map<String, Object> m = new LinkedHashMap<>();
m.put("userId", u.getUserId());
m.put("userName", u.getUserName());
m.put("nickName", u.getNickName());
m.put("deptId", u.getDeptId());
m.put("deptName", u.getDept() == null ? null : u.getDept().getDeptName());
m.put("status", u.getStatus());
return m;
}).collect(Collectors.toList());
return AjaxResult.success(options);
}
@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:user:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysUser user)
{
List<SysUser> list = userService.selectUserList(user);
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
util.exportExcel(response, list, "用户数据");
}
@Log(title = "用户管理", businessType = BusinessType.IMPORT)
@PreAuthorize("@ss.hasPermi('system:user:import')")
@PostMapping("/importData")
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
{
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
List<SysUser> userList = util.importExcel(file.getInputStream());
String operName = getUsername();
String message = userService.importUser(userList, updateSupport, operName);
return success(message);
}
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response)
{
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
util.importTemplateExcel(response, "用户数据");
}
/**
* 根据用户编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping(value = { "/", "/{userId}" })
public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId)
{
AjaxResult ajax = AjaxResult.success();
if (StringUtils.isNotNull(userId))
{
userService.checkUserDataScope(userId);
SysUser sysUser = userService.selectUserById(userId);
ajax.put(AjaxResult.DATA_TAG, sysUser);
ajax.put("postIds", postService.selectPostListByUserId(userId));
ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
}
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", SecurityUtils.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
return ajax;
}
/**
* 新增用户
*/
@PreAuthorize("@ss.hasPermi('system:user:add')")
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user)
{
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkUserNameUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setCreateBy(getUsername());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
return toAjax(userService.insertUser(user));
}
/**
* 修改用户
*/
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysUser user)
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkUserNameUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setUpdateBy(getUsername());
return toAjax(userService.updateUser(user));
}
/**
* 删除用户
*/
@PreAuthorize("@ss.hasPermi('system:user:remove')")
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
public AjaxResult remove(@PathVariable Long[] userIds)
{
if (ArrayUtils.contains(userIds, getUserId()))
{
return error("当前用户不能删除");
}
return toAjax(userService.deleteUserByIds(userIds));
}
/**
* 重置密码
*/
@PreAuthorize("@ss.hasPermi('system:user:resetPwd')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/resetPwd")
public AjaxResult resetPwd(@RequestBody SysUser user)
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
user.setUpdateBy(getUsername());
return toAjax(userService.resetPwd(user));
}
/**
* 状态修改
*/
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
public AjaxResult changeStatus(@RequestBody SysUser user)
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
user.setUpdateBy(getUsername());
return toAjax(userService.updateUserStatus(user));
}
/**
* 根据用户编号获取授权角色
*/
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping("/authRole/{userId}")
public AjaxResult authRole(@PathVariable("userId") Long userId)
{
AjaxResult ajax = AjaxResult.success();
SysUser user = userService.selectUserById(userId);
List<SysRole> roles = roleService.selectRolesByUserId(userId);
ajax.put("user", user);
ajax.put("roles", SecurityUtils.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
return ajax;
}
/**
* 用户授权角色
*/
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.GRANT)
@PutMapping("/authRole")
public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
{
userService.checkUserDataScope(userId);
roleService.checkRoleDataScope(roleIds);
userService.insertUserAuth(userId, roleIds);
return success();
}
/**
* 获取部门树列表
*/
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/deptTree")
public AjaxResult deptTree(SysDept dept)
{
return success(deptService.selectDeptTreeList(dept));
}
}
@@ -0,0 +1,175 @@
package com.bms.web.controller.tool;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
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.core.controller.BaseController;
import com.bms.common.core.domain.R;
import com.bms.common.utils.StringUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* swagger 用户测试方法
*
* @author bms
*/
@Tag(name = "用户信息管理")
@RestController
@RequestMapping("/test/user")
public class TestController extends BaseController
{
private final static Map<Integer, UserEntity> users = new LinkedHashMap<Integer, UserEntity>();
{
users.put(1, new UserEntity(1, "admin", "admin123", "15888888888"));
users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
}
@Operation(summary = "获取用户列表")
@GetMapping("/list")
public R<List<UserEntity>> userList()
{
List<UserEntity> userList = new ArrayList<UserEntity>(users.values());
return R.ok(userList);
}
@Operation(summary = "获取用户详细")
@GetMapping("/{userId}")
public R<UserEntity> getUser(@PathVariable(name = "userId")
Integer userId)
{
if (!users.isEmpty() && users.containsKey(userId))
{
return R.ok(users.get(userId));
}
else
{
return R.fail("用户不存在");
}
}
@Operation(summary = "新增用户")
@PostMapping("/save")
public R<String> save(UserEntity user)
{
if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
{
return R.fail("用户ID不能为空");
}
users.put(user.getUserId(), user);
return R.ok();
}
@Operation(summary = "更新用户")
@PutMapping("/update")
public R<String> update(@RequestBody
UserEntity user)
{
if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
{
return R.fail("用户ID不能为空");
}
if (users.isEmpty() || !users.containsKey(user.getUserId()))
{
return R.fail("用户不存在");
}
users.remove(user.getUserId());
users.put(user.getUserId(), user);
return R.ok();
}
@Operation(summary = "删除用户信息")
@DeleteMapping("/{userId}")
public R<String> delete(@PathVariable(name = "userId")
Integer userId)
{
if (!users.isEmpty() && users.containsKey(userId))
{
users.remove(userId);
return R.ok();
}
else
{
return R.fail("用户不存在");
}
}
}
@Schema(description = "用户实体")
class UserEntity
{
@Schema(title = "用户ID")
private Integer userId;
@Schema(title = "用户名称")
private String username;
@Schema(title = "用户密码")
private String password;
@Schema(title = "用户手机")
private String mobile;
public UserEntity()
{
}
public UserEntity(Integer userId, String username, String password, String mobile)
{
this.userId = userId;
this.username = username;
this.password = password;
this.mobile = mobile;
}
public Integer getUserId()
{
return userId;
}
public void setUserId(Integer userId)
{
this.userId = userId;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String getMobile()
{
return mobile;
}
public void setMobile(String mobile)
{
this.mobile = mobile;
}
}
@@ -0,0 +1,288 @@
package com.bms.web.controller.tool;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
/**
* BMS TMS 计费接口 demo(单文件,纯 JDK,无第三方依赖)
*
* 功能:读取 D:\code\view\orders_10000.xlsx 的每一行(跳过表头),
* 组装为 JSON 后 POST 到本地 BMS 计费接口。
*
* 接口:POST http://127.0.0.1:8080/api/tms/price/bill
* 鉴权:Header X-Api-Code
*
* 编译运行:
* javac TmsPriceBillDemo.java
* java TmsPriceBillDemo
*/
public class TmsPriceBillDemo {
// ===== 可配置项 =====
private static final String BASE_URL = "http://127.0.0.1:8080";
private static final String BILL_PATH = "/api/tms/price/bill";
private static final String API_CODE = "BMS.TMS.ZYaOvlQzpcfau8TUaCFfO3Hp8ieHIqGw";
private static final String EXCEL_PATH = "D:\\code\\view\\orders_10000.xlsx";
private static final int CONNECT_TIMEOUT_MS = 10_000;
private static final int READ_TIMEOUT_MS = 30_000;
/** 每多少条打印一次进度 */
private static final int PROGRESS_STEP = 200;
/** 数值字段:JSON 中不加引号,直接输出 Excel 中的原始数字字符串 */
private static final Set<String> NUMERIC_FIELDS = new HashSet<>(Arrays.asList(
"weight", "pieces", "actualWeight", "volume", "volumeWeight",
"pkgLength", "pkgWidth", "pkgHeight", "tmsId"
));
public static void main(String[] args) throws Exception {
// 可通过命令行参数覆盖 Excel 路径
String excel = (args.length > 0 && !args[0].isEmpty()) ? args[0] : EXCEL_PATH;
System.out.println("[config] base=" + BASE_URL + BILL_PATH);
System.out.println("[config] xApiCode=" + API_CODE);
System.out.println("[config] excel=" + excel);
long start = System.currentTimeMillis();
int ok = 0, fail = 0, rowIdx = 0;
try (ZipFile zf = new ZipFile(excel)) {
List<String> sst = readSharedStrings(zf);
System.out.println("[read] sharedStrings uniqueCount=" + sst.size());
SheetReader sr = new SheetReader(zf, sst);
String[] headers = null;
List<String> row;
while ((row = sr.nextRow()) != null) {
rowIdx++;
if (headers == null) {
headers = row.toArray(new String[0]);
System.out.println("[read] headers=" + row);
continue;
}
String json = buildJson(row, headers);
String resp;
try {
resp = postJson(BASE_URL + BILL_PATH, API_CODE, json);
} catch (Exception e) {
resp = "ERR: " + e.getMessage();
}
if (resp.contains("\"code\":200") || resp.contains("\"code\": 200")) {
ok++;
} else {
fail++;
}
if (rowIdx % PROGRESS_STEP == 0 || rowIdx == 1) {
String snippet = resp.length() > 120 ? resp.substring(0, 120) + "..." : resp;
System.out.printf("[progress] %d ok=%d fail=%d last=%s%n", rowIdx, ok, fail, snippet);
}
}
System.out.printf("[done] rows=%d ok=%d fail=%d cost=%dms%n",
rowIdx, ok, fail, System.currentTimeMillis() - start);
}
}
// ===== JSON 构造 =====
private static String buildJson(List<String> row, String[] headers) {
StringBuilder sb = new StringBuilder(256);
sb.append('{');
for (int i = 0; i < headers.length; i++) {
if (i > 0) sb.append(',');
String k = headers[i];
String v = (i < row.size()) ? row.get(i) : "";
sb.append('"').append(escape(k)).append("\":");
boolean isNumeric = NUMERIC_FIELDS.contains(k)
&& v != null && !v.isEmpty() && !"null".equals(v);
if (isNumeric) {
sb.append(v);
} else if (v == null || v.isEmpty() || "null".equals(v)) {
sb.append("null");
} else {
sb.append('"').append(escape(v)).append('"');
}
}
sb.append('}');
return sb.toString();
}
private static String escape(String s) {
if (s == null) return "";
StringBuilder sb = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '"': sb.append("\\\""); break;
case '\\': sb.append("\\\\"); break;
case '\n': sb.append("\\n"); break;
case '\r': sb.append("\\r"); break;
case '\t': sb.append("\\t"); break;
default:
if (c < 0x20) sb.append(String.format("\\u%04x", (int) c));
else sb.append(c);
}
}
return sb.toString();
}
// ===== HTTP =====
private static String postJson(String url, String apiCode, String json) throws Exception {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setConnectTimeout(CONNECT_TIMEOUT_MS);
conn.setReadTimeout(READ_TIMEOUT_MS);
conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
conn.setRequestProperty("X-Api-Code", apiCode);
conn.setRequestProperty("Accept", "application/json");
byte[] body = json.getBytes(StandardCharsets.UTF_8);
conn.setRequestProperty("Content-Length", String.valueOf(body.length));
try (OutputStream os = conn.getOutputStream()) {
os.write(body);
os.flush();
}
int code = conn.getResponseCode();
InputStream is = (code >= 200 && code < 300) ? conn.getInputStream() : conn.getErrorStream();
String respBody = readAll(is);
return "HTTP " + code + " " + respBody;
} finally {
if (conn != null) conn.disconnect();
}
}
private static String readAll(InputStream is) throws Exception {
if (is == null) return "";
StringBuilder sb = new StringBuilder();
try (BufferedReader r = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
String line;
while ((line = r.readLine()) != null) {
if (sb.length() > 0) sb.append('\n');
sb.append(line);
}
}
return sb.toString();
}
// ===== XLSX 解析(纯 JDK) =====
/** 读取 xl/sharedStrings.xml,返回共享字符串表 */
private static List<String> readSharedStrings(ZipFile zf) throws Exception {
ZipEntry e = zf.getEntry("xl/sharedStrings.xml");
if (e == null) return Collections.emptyList();
List<String> out = new ArrayList<>();
try (InputStream in = zf.getInputStream(e)) {
XMLInputFactory f = XMLInputFactory.newInstance();
// 禁用 DTD 以避免 XXE
f.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
f.setProperty(XMLInputFactory.SUPPORT_DTD, false);
XMLStreamReader r = f.createXMLStreamReader(in);
StringBuilder text = new StringBuilder();
boolean inT = false;
while (r.hasNext()) {
int ev = r.next();
if (ev == XMLStreamConstants.START_ELEMENT) {
if ("t".equals(r.getLocalName())) {
inT = true;
text.setLength(0);
}
} else if (ev == XMLStreamConstants.CHARACTERS) {
if (inT) text.append(r.getText());
} else if (ev == XMLStreamConstants.END_ELEMENT) {
String ln = r.getLocalName();
if ("t".equals(ln)) {
inT = false;
} else if ("si".equals(ln)) {
out.add(text.toString());
text.setLength(0);
}
}
}
r.close();
}
return out;
}
/** 流式读取 xl/worksheets/sheet1.xml,nextRow() 逐行返回 */
static class SheetReader {
private final XMLStreamReader r;
private final List<String> sst;
private final InputStream stream;
SheetReader(ZipFile zf, List<String> sst) throws Exception {
ZipEntry e = zf.getEntry("xl/worksheets/sheet1.xml");
this.stream = zf.getInputStream(e);
this.sst = sst;
XMLInputFactory f = XMLInputFactory.newInstance();
f.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
f.setProperty(XMLInputFactory.SUPPORT_DTD, false);
this.r = f.createXMLStreamReader(stream);
}
List<String> nextRow() throws Exception {
List<String> row = null;
String curType = null;
StringBuilder curVal = new StringBuilder();
boolean inV = false;
while (r.hasNext()) {
int ev = r.next();
if (ev == XMLStreamConstants.START_ELEMENT) {
String ln = r.getLocalName();
if ("row".equals(ln)) {
row = new ArrayList<>();
} else if ("c".equals(ln) && row != null) {
curType = r.getAttributeValue(null, "t");
curVal.setLength(0);
} else if ("v".equals(ln) && row != null) {
inV = true;
} else if ("t".equals(ln) && row != null) {
inV = true; // inlineStr 的 <t>
}
} else if (ev == XMLStreamConstants.CHARACTERS) {
if (inV && row != null) curVal.append(r.getText());
} else if (ev == XMLStreamConstants.END_ELEMENT) {
String ln = r.getLocalName();
if ("v".equals(ln) || "t".equals(ln)) {
inV = false;
} else if ("c".equals(ln) && row != null) {
String v = curVal.toString();
String val;
if ("s".equals(curType)) {
int idx = Integer.parseInt(v.trim());
val = idx < sst.size() ? sst.get(idx) : "";
} else if ("b".equals(curType)) {
val = "1".equals(v.trim()) ? "true" : "false";
} else {
// 数值、str、inlineStr、空等,直接取原值
val = v;
}
row.add(val);
} else if ("row".equals(ln) && row != null) {
return row;
}
}
}
try { r.close(); stream.close(); } catch (Exception ignored) {}
return null;
}
}
}
@@ -0,0 +1,64 @@
package com.bms.web.core.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.bms.common.config.BmsConfig;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
/**
* Swagger2的接口配置
*
* @author bms
*/
@Configuration
public class SwaggerConfig
{
/** 系统基础配置 */
@Autowired
private BmsConfig bmsConfig;
/**
* 自定义的 OpenAPI 对象
*/
@Bean
public OpenAPI customOpenApi()
{
return new OpenAPI().components(new Components()
// 设置认证的请求头
.addSecuritySchemes("apikey", securityScheme()))
.addSecurityItem(new SecurityRequirement().addList("apikey"))
.info(getApiInfo());
}
@Bean
public SecurityScheme securityScheme()
{
return new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
.name("Authorization")
.in(SecurityScheme.In.HEADER)
.scheme("Bearer");
}
/**
* 添加摘要信息
*/
public Info getApiInfo()
{
return new Info()
// 设置标题
.title("标题:BMS 管理系统_接口文档")
// 描述
.description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
// 作者信息
.contact(new Contact().name(bmsConfig.getName()))
// 版本
.version("版本号:" + bmsConfig.getVersion());
}
}
@@ -0,0 +1 @@
restart.include.json=/com.alibaba.fastjson2.*.jar
@@ -0,0 +1,80 @@
# 本地配置(默认生效)
# 启动:不传 profile,或 --spring.profiles.active=local
# Elasticsearch:本机 Docker http://127.0.0.1:9200
# 数据源暂与线上相同,换本地库时只改本文件
bms:
elasticsearch:
enabled: true
uris: http://127.0.0.1:9200
username:
password:
index: bms_biz_log
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://47.106.220.100:3307/bms?useUnicode=true&characterEncoding=utf8&connectionCollation=utf8mb4_unicode_ci&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
username: bms
password: D3wsHf2ZMfFtWtKX
connectionInitSqls: SET NAMES utf8mb4
# Slave DS: Oracle upstream order (TAB_ORDER)
slave:
enabled: true
driverClassName: oracle.jdbc.OracleDriver
url: jdbc:oracle:thin:@//120.79.189.203:15281/WSDB
username: readonly_report
password: k5x15WP1hE
# Oracle remote query may need longer read timeout than MySQL
connectTimeout: 30000
socketTimeout: 120000
validationQuery: SELECT 1 FROM DUAL
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久检测一次空闲连接,避免远程数据库断开后连接池继续复用失效连接,单位是毫秒
timeBetweenEvictionRunsMillis: 30000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
# 异步账单任务借用连接时校验连接有效性,避免拿到已被数据库断开的连接
testOnBorrow: true
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: bms
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
@@ -0,0 +1,90 @@
# 线上配置
# 启动:--spring.profiles.active=prod
# Elasticsearch:172.19.64.13:9200
# 生产环境文件存储根目录
# 上传、导入、头像及账单下载文件都会基于此目录保存
bms:
profile: /www/server/bms/data
elasticsearch:
enabled: true
uris: https://127.0.0.1:9200
username: elastic
password: d4aoZYB2yQHXmWZiH6Gw
index: bms_biz_log
logging:
level:
com.bms: info
org.springframework: warn
# 数据源配置
spring:
devtools:
restart:
enabled: false
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://127.0.0.1:3306/bms?useUnicode=true&characterEncoding=utf8&connectionCollation=utf8mb4_unicode_ci&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
username: bms
password: JnE6nTRSNZjnhrcP
connectionInitSqls: SET NAMES utf8mb4
# Slave DS: Oracle upstream order (TAB_ORDER)
slave:
enabled: true
driverClassName: oracle.jdbc.OracleDriver
url: jdbc:oracle:thin:@//120.79.189.203:15281/WSDB
username: readonly_report
password: k5x15WP1hE
# Oracle remote query may need longer read timeout than MySQL
connectTimeout: 30000
socketTimeout: 120000
validationQuery: SELECT 1 FROM DUAL
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久检测一次空闲连接,避免远程数据库断开后连接池继续复用失效连接,单位是毫秒
timeBetweenEvictionRunsMillis: 30000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
# 异步账单任务借用连接时校验连接有效性,避免拿到已被数据库断开的连接
testOnBorrow: true
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: bms
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
@@ -0,0 +1,171 @@
# 项目相关配置
bms:
# 名称
name: BMS
# 版本
version: 3.9.2
# 版权年份
copyrightYear: 2026
# 文件路径 示例( Windows配置D:/bms/uploadPath,Linux配置 /home/bms/uploadPath)
profile: D:/bms/uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
captchaType: math
# TMS open API
tms:
# Agreed API identification code for TMS callers
api-code: BMS.TMS.ZYaOvlQzpcfau8TUaCFfO3Hp8ieHIqGw
billing:
# MQ mode: redis (default list queue) | local (Spring event)
mq-mode: redis
mq-queue-key: bms:billing:task
consumer-enabled: true
# Pop/process interval after previous tick finishes
consumer-delay-ms: 200
# Max messages popped per tick; processed by consumer-threads pool
consumer-batch-size: 32
consumer-threads: 8
oracle:
# SLAVE datasource Oracle main-order table (schema.table allowed)
order-table: WEISHI_USER.TAB_ORDER
# Fallback product when Oracle row has no PRODUCT_CODE
default-product-code: ""
# 开发环境配置
server:
# 服务器的HTTP端口,默认为8080
port: 8080
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数,默认为100
accept-count: 1000
threads:
# tomcat最大线程数,默认为200
max: 800
# Tomcat启动初始化的线程数,默认值10
min-spare: 100
# 日志配置
logging:
level:
com.bms: debug
org.springframework: warn
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间(默认10分钟)
lockTime: 10
# Spring配置
spring:
# 允许循环引用(price 模块 PrcOrderBillServiceImpl <-> PrcCreditArrearsClearAsyncRunner 互依赖)
main:
allow-circular-references: true
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
# local -> application-local.yml ; prod -> application-prod.yml
active: '@profiles.active@'
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
data:
# redis 配置
redis:
# 地址
host: localhost
# 端口,默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期(默认30分钟)
expireTime: 30
# MyBatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.bms.**.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# PageHelper分页插件
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
params: count=countSql
# Springdoc配置
springdoc:
api-docs:
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
tags-sorter: alpha
group-configs:
- group: 'default'
display-name: '测试模块'
paths-to-match: '/**'
packages-to-scan: com.bms.web.controller.tool
# 防盗链配置
referer:
# 防盗链开关
enabled: false
# 允许的域名列表
allowed-domains: localhost,127.0.0.1
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
@@ -0,0 +1,24 @@
Application Version: ${bms.version}
Spring Boot Version: ${spring-boot.version}
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////
@@ -0,0 +1,107 @@
[
{"name":{"common":"China","official":"People's Republic of China"},"cca2":"CN","cca3":"CHN","region":"Asia","subregion":"Eastern Asia","currencies":{"CNY":{"name":"Chinese yuan","symbol":"¥"}},"idd":{"root":"+86","suffixes":[""]},"flag":"🇨🇳","flags":{"png":"https://flagcdn.com/w320/cn.png"},"independent":true},
{"name":{"common":"United States","official":"United States of America"},"cca2":"US","cca3":"USA","region":"Americas","subregion":"North America","currencies":{"USD":{"name":"United States dollar","symbol":"$"}},"idd":{"root":"+1","suffixes":[""]},"flag":"🇺🇸","flags":{"png":"https://flagcdn.com/w320/us.png"},"independent":true},
{"name":{"common":"Germany","official":"Federal Republic of Germany"},"cca2":"DE","cca3":"DEU","region":"Europe","subregion":"Western Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+49","suffixes":[""]},"flag":"🇩🇪","flags":{"png":"https://flagcdn.com/w320/de.png"},"independent":true},
{"name":{"common":"Japan","official":"Japan"},"cca2":"JP","cca3":"JPN","region":"Asia","subregion":"Eastern Asia","currencies":{"JPY":{"name":"Japanese yen","symbol":"¥"}},"idd":{"root":"+81","suffixes":[""]},"flag":"🇯🇵","flags":{"png":"https://flagcdn.com/w320/jp.png"},"independent":true},
{"name":{"common":"South Korea","official":"Republic of Korea"},"cca2":"KR","cca3":"KOR","region":"Asia","subregion":"Eastern Asia","currencies":{"KRW":{"name":"South Korean won","symbol":"₩"}},"idd":{"root":"+82","suffixes":[""]},"flag":"🇰🇷","flags":{"png":"https://flagcdn.com/w320/kr.png"},"independent":true},
{"name":{"common":"United Kingdom","official":"United Kingdom of Great Britain and Northern Ireland"},"cca2":"GB","cca3":"GBR","region":"Europe","subregion":"Northern Europe","currencies":{"GBP":{"name":"British pound","symbol":"£"}},"idd":{"root":"+44","suffixes":[""]},"flag":"🇬🇧","flags":{"png":"https://flagcdn.com/w320/gb.png"},"independent":true},
{"name":{"common":"France","official":"French Republic"},"cca2":"FR","cca3":"FRA","region":"Europe","subregion":"Western Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+33","suffixes":[""]},"flag":"🇫🇷","flags":{"png":"https://flagcdn.com/w320/fr.png"},"independent":true},
{"name":{"common":"Italy","official":"Italian Republic"},"cca2":"IT","cca3":"ITA","region":"Europe","subregion":"Southern Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+39","suffixes":[""]},"flag":"🇮🇹","flags":{"png":"https://flagcdn.com/w320/it.png"},"independent":true},
{"name":{"common":"Spain","official":"Kingdom of Spain"},"cca2":"ES","cca3":"ESP","region":"Europe","subregion":"Southern Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+34","suffixes":[""]},"flag":"🇪🇸","flags":{"png":"https://flagcdn.com/w320/es.png"},"independent":true},
{"name":{"common":"Netherlands","official":"Kingdom of the Netherlands"},"cca2":"NL","cca3":"NLD","region":"Europe","subregion":"Western Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+31","suffixes":[""]},"flag":"🇳🇱","flags":{"png":"https://flagcdn.com/w320/nl.png"},"independent":true},
{"name":{"common":"Belgium","official":"Kingdom of Belgium"},"cca2":"BE","cca3":"BEL","region":"Europe","subregion":"Western Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+32","suffixes":[""]},"flag":"🇧🇪","flags":{"png":"https://flagcdn.com/w320/be.png"},"independent":true},
{"name":{"common":"Switzerland","official":"Swiss Confederation"},"cca2":"CH","cca3":"CHE","region":"Europe","subregion":"Western Europe","currencies":{"CHF":{"name":"Swiss franc","symbol":"Fr."}},"idd":{"root":"+41","suffixes":[""]},"flag":"🇨🇭","flags":{"png":"https://flagcdn.com/w320/ch.png"},"independent":true},
{"name":{"common":"Austria","official":"Republic of Austria"},"cca2":"AT","cca3":"AUT","region":"Europe","subregion":"Western Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+43","suffixes":[""]},"flag":"🇦🇹","flags":{"png":"https://flagcdn.com/w320/at.png"},"independent":true},
{"name":{"common":"Sweden","official":"Kingdom of Sweden"},"cca2":"SE","cca3":"SWE","region":"Europe","subregion":"Northern Europe","currencies":{"SEK":{"name":"Swedish krona","symbol":"kr"}},"idd":{"root":"+46","suffixes":[""]},"flag":"🇸🇪","flags":{"png":"https://flagcdn.com/w320/se.png"},"independent":true},
{"name":{"common":"Norway","official":"Kingdom of Norway"},"cca2":"NO","cca3":"NOR","region":"Europe","subregion":"Northern Europe","currencies":{"NOK":{"name":"Norwegian krone","symbol":"kr"}},"idd":{"root":"+47","suffixes":[""]},"flag":"🇳🇴","flags":{"png":"https://flagcdn.com/w320/no.png"},"independent":true},
{"name":{"common":"Denmark","official":"Kingdom of Denmark"},"cca2":"DK","cca3":"DNK","region":"Europe","subregion":"Northern Europe","currencies":{"DKK":{"name":"Danish krone","symbol":"kr"}},"idd":{"root":"+45","suffixes":[""]},"flag":"🇩🇰","flags":{"png":"https://flagcdn.com/w320/dk.png"},"independent":true},
{"name":{"common":"Finland","official":"Republic of Finland"},"cca2":"FI","cca3":"FIN","region":"Europe","subregion":"Northern Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+358","suffixes":[""]},"flag":"🇫🇮","flags":{"png":"https://flagcdn.com/w320/fi.png"},"independent":true},
{"name":{"common":"Poland","official":"Republic of Poland"},"cca2":"PL","cca3":"POL","region":"Europe","subregion":"Central Europe","currencies":{"PLN":{"name":"Polish zloty","symbol":"zł"}},"idd":{"root":"+48","suffixes":[""]},"flag":"🇵🇱","flags":{"png":"https://flagcdn.com/w320/pl.png"},"independent":true},
{"name":{"common":"Russia","official":"Russian Federation"},"cca2":"RU","cca3":"RUS","region":"Europe","subregion":"Eastern Europe","currencies":{"RUB":{"name":"Russian ruble","symbol":"₽"}},"idd":{"root":"+7","suffixes":[""]},"flag":"🇷🇺","flags":{"png":"https://flagcdn.com/w320/ru.png"},"independent":true},
{"name":{"common":"India","official":"Republic of India"},"cca2":"IN","cca3":"IND","region":"Asia","subregion":"Southern Asia","currencies":{"INR":{"name":"Indian rupee","symbol":"₹"}},"idd":{"root":"+91","suffixes":[""]},"flag":"🇮🇳","flags":{"png":"https://flagcdn.com/w320/in.png"},"independent":true},
{"name":{"common":"Brazil","official":"Federative Republic of Brazil"},"cca2":"BR","cca3":"BRA","region":"Americas","subregion":"South America","currencies":{"BRL":{"name":"Brazilian real","symbol":"R$"}},"idd":{"root":"+55","suffixes":[""]},"flag":"🇧🇷","flags":{"png":"https://flagcdn.com/w320/br.png"},"independent":true},
{"name":{"common":"Canada","official":"Canada"},"cca2":"CA","cca3":"CAN","region":"Americas","subregion":"North America","currencies":{"CAD":{"name":"Canadian dollar","symbol":"$"}},"idd":{"root":"+1","suffixes":[""]},"flag":"🇨🇦","flags":{"png":"https://flagcdn.com/w320/ca.png"},"independent":true},
{"name":{"common":"Australia","official":"Commonwealth of Australia"},"cca2":"AU","cca3":"AUS","region":"Oceania","subregion":"Australia and New Zealand","currencies":{"AUD":{"name":"Australian dollar","symbol":"$"}},"idd":{"root":"+61","suffixes":[""]},"flag":"🇦🇺","flags":{"png":"https://flagcdn.com/w320/au.png"},"independent":true},
{"name":{"common":"Mexico","official":"United Mexican States"},"cca2":"MX","cca3":"MEX","region":"Americas","subregion":"North America","currencies":{"MXN":{"name":"Mexican peso","symbol":"$"}},"idd":{"root":"+52","suffixes":[""]},"flag":"🇲🇽","flags":{"png":"https://flagcdn.com/w320/mx.png"},"independent":true},
{"name":{"common":"Indonesia","official":"Republic of Indonesia"},"cca2":"ID","cca3":"IDN","region":"Asia","subregion":"South-Eastern Asia","currencies":{"IDR":{"name":"Indonesian rupiah","symbol":"Rp"}},"idd":{"root":"+62","suffixes":[""]},"flag":"🇮🇩","flags":{"png":"https://flagcdn.com/w320/id.png"},"independent":true},
{"name":{"common":"Thailand","official":"Kingdom of Thailand"},"cca2":"TH","cca3":"THA","region":"Asia","subregion":"South-Eastern Asia","currencies":{"THB":{"name":"Thai baht","symbol":"฿"}},"idd":{"root":"+66","suffixes":[""]},"flag":"🇹🇭","flags":{"png":"https://flagcdn.com/w320/th.png"},"independent":true},
{"name":{"common":"Vietnam","official":"Socialist Republic of Vietnam"},"cca2":"VN","cca3":"VNM","region":"Asia","subregion":"South-Eastern Asia","currencies":{"VND":{"name":"Vietnamese đồng","symbol":"₫"}},"idd":{"root":"+84","suffixes":[""]},"flag":"🇻🇳","flags":{"png":"https://flagcdn.com/w320/vn.png"},"independent":true},
{"name":{"common":"Malaysia","official":"Malaysia"},"cca2":"MY","cca3":"MYS","region":"Asia","subregion":"South-Eastern Asia","currencies":{"MYR":{"name":"Malaysian ringgit","symbol":"RM"}},"idd":{"root":"+60","suffixes":[""]},"flag":"🇲🇾","flags":{"png":"https://flagcdn.com/w320/my.png"},"independent":true},
{"name":{"common":"Singapore","official":"Republic of Singapore"},"cca2":"SG","cca3":"SGP","region":"Asia","subregion":"South-Eastern Asia","currencies":{"SGD":{"name":"Singapore dollar","symbol":"$"}},"idd":{"root":"+65","suffixes":[""]},"flag":"🇸🇬","flags":{"png":"https://flagcdn.com/w320/sg.png"},"independent":true},
{"name":{"common":"Philippines","official":"Republic of the Philippines"},"cca2":"PH","cca3":"PHL","region":"Asia","subregion":"South-Eastern Asia","currencies":{"PHP":{"name":"Philippine peso","symbol":"₱"}},"idd":{"root":"+63","suffixes":[""]},"flag":"🇵🇭","flags":{"png":"https://flagcdn.com/w320/ph.png"},"independent":true},
{"name":{"common":"Turkey","official":"Republic of Türkiye"},"cca2":"TR","cca3":"TUR","region":"Asia","subregion":"Western Asia","currencies":{"TRY":{"name":"Turkish lira","symbol":"₺"}},"idd":{"root":"+90","suffixes":[""]},"flag":"🇹🇷","flags":{"png":"https://flagcdn.com/w320/tr.png"},"independent":true},
{"name":{"common":"Saudi Arabia","official":"Kingdom of Saudi Arabia"},"cca2":"SA","cca3":"SAU","region":"Asia","subregion":"Western Asia","currencies":{"SAR":{"name":"Saudi riyal","symbol":"ر.س"}},"idd":{"root":"+966","suffixes":[""]},"flag":"🇸🇦","flags":{"png":"https://flagcdn.com/w320/sa.png"},"independent":true},
{"name":{"common":"United Arab Emirates","official":"United Arab Emirates"},"cca2":"AE","cca3":"ARE","region":"Asia","subregion":"Western Asia","currencies":{"AED":{"name":"United Arab Emirates dirham","symbol":"د.إ"}},"idd":{"root":"+971","suffixes":[""]},"flag":"🇦🇪","flags":{"png":"https://flagcdn.com/w320/ae.png"},"independent":true},
{"name":{"common":"Israel","official":"State of Israel"},"cca2":"IL","cca3":"ISR","region":"Asia","subregion":"Western Asia","currencies":{"ILS":{"name":"Israeli new shekel","symbol":"₪"}},"idd":{"root":"+972","suffixes":[""]},"flag":"🇮🇱","flags":{"png":"https://flagcdn.com/w320/il.png"},"independent":true},
{"name":{"common":"Egypt","official":"Arab Republic of Egypt"},"cca2":"EG","cca3":"EGY","region":"Africa","subregion":"Northern Africa","currencies":{"EGP":{"name":"Egyptian pound","symbol":"£"}},"idd":{"root":"+20","suffixes":[""]},"flag":"🇪🇬","flags":{"png":"https://flagcdn.com/w320/eg.png"},"independent":true},
{"name":{"common":"South Africa","official":"Republic of South Africa"},"cca2":"ZA","cca3":"ZAF","region":"Africa","subregion":"Southern Africa","currencies":{"ZAR":{"name":"South African rand","symbol":"R"}},"idd":{"root":"+27","suffixes":[""]},"flag":"🇿🇦","flags":{"png":"https://flagcdn.com/w320/za.png"},"independent":true},
{"name":{"common":"Nigeria","official":"Federal Republic of Nigeria"},"cca2":"NG","cca3":"NGA","region":"Africa","subregion":"Western Africa","currencies":{"NGN":{"name":"Nigerian naira","symbol":"₦"}},"idd":{"root":"+234","suffixes":[""]},"flag":"🇳🇬","flags":{"png":"https://flagcdn.com/w320/ng.png"},"independent":true},
{"name":{"common":"Argentina","official":"Argentine Republic"},"cca2":"AR","cca3":"ARG","region":"Americas","subregion":"South America","currencies":{"ARS":{"name":"Argentine peso","symbol":"$"}},"idd":{"root":"+54","suffixes":[""]},"flag":"🇦🇷","flags":{"png":"https://flagcdn.com/w320/ar.png"},"independent":true},
{"name":{"common":"Chile","official":"Republic of Chile"},"cca2":"CL","cca3":"CHL","region":"Americas","subregion":"South America","currencies":{"CLP":{"name":"Chilean peso","symbol":"$"}},"idd":{"root":"+56","suffixes":[""]},"flag":"🇨🇱","flags":{"png":"https://flagcdn.com/w320/cl.png"},"independent":true},
{"name":{"common":"Colombia","official":"Republic of Colombia"},"cca2":"CO","cca3":"COL","region":"Americas","subregion":"South America","currencies":{"COP":{"name":"Colombian peso","symbol":"$"}},"idd":{"root":"+57","suffixes":[""]},"flag":"🇨🇴","flags":{"png":"https://flagcdn.com/w320/co.png"},"independent":true},
{"name":{"common":"Peru","official":"Republic of Peru"},"cca2":"PE","cca3":"PER","region":"Americas","subregion":"South America","currencies":{"PEN":{"name":"Peruvian sol","symbol":"S/"}},"idd":{"root":"+51","suffixes":[""]},"flag":"🇵🇪","flags":{"png":"https://flagcdn.com/w320/pe.png"},"independent":true},
{"name":{"common":"New Zealand","official":"New Zealand"},"cca2":"NZ","cca3":"NZL","region":"Oceania","subregion":"Australia and New Zealand","currencies":{"NZD":{"name":"New Zealand dollar","symbol":"$"}},"idd":{"root":"+64","suffixes":[""]},"flag":"🇳🇿","flags":{"png":"https://flagcdn.com/w320/nz.png"},"independent":true},
{"name":{"common":"Ireland","official":"Republic of Ireland"},"cca2":"IE","cca3":"IRL","region":"Europe","subregion":"Northern Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+353","suffixes":[""]},"flag":"🇮🇪","flags":{"png":"https://flagcdn.com/w320/ie.png"},"independent":true},
{"name":{"common":"Portugal","official":"Portuguese Republic"},"cca2":"PT","cca3":"PRT","region":"Europe","subregion":"Southern Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+351","suffixes":[""]},"flag":"🇵🇹","flags":{"png":"https://flagcdn.com/w320/pt.png"},"independent":true},
{"name":{"common":"Greece","official":"Hellenic Republic"},"cca2":"GR","cca3":"GRC","region":"Europe","subregion":"Southern Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+30","suffixes":[""]},"flag":"🇬🇷","flags":{"png":"https://flagcdn.com/w320/gr.png"},"independent":true},
{"name":{"common":"Czechia","official":"Czech Republic"},"cca2":"CZ","cca3":"CZE","region":"Europe","subregion":"Central Europe","currencies":{"CZK":{"name":"Czech koruna","symbol":"Kč"}},"idd":{"root":"+420","suffixes":[""]},"flag":"🇨🇿","flags":{"png":"https://flagcdn.com/w320/cz.png"},"independent":true},
{"name":{"common":"Hungary","official":"Hungary"},"cca2":"HU","cca3":"HUN","region":"Europe","subregion":"Central Europe","currencies":{"HUF":{"name":"Hungarian forint","symbol":"Ft"}},"idd":{"root":"+36","suffixes":[""]},"flag":"🇭🇺","flags":{"png":"https://flagcdn.com/w320/hu.png"},"independent":true},
{"name":{"common":"Romania","official":"Romania"},"cca2":"RO","cca3":"ROU","region":"Europe","subregion":"Central Europe","currencies":{"RON":{"name":"Romanian leu","symbol":"lei"}},"idd":{"root":"+40","suffixes":[""]},"flag":"🇷🇴","flags":{"png":"https://flagcdn.com/w320/ro.png"},"independent":true},
{"name":{"common":"Bulgaria","official":"Republic of Bulgaria"},"cca2":"BG","cca3":"BGR","region":"Europe","subregion":"Southeast Europe","currencies":{"BGN":{"name":"Bulgarian lev","symbol":"лв"}},"idd":{"root":"+359","suffixes":[""]},"flag":"🇧🇬","flags":{"png":"https://flagcdn.com/w320/bg.png"},"independent":true},
{"name":{"common":"Croatia","official":"Republic of Croatia"},"cca2":"HR","cca3":"HRV","region":"Europe","subregion":"Southeast Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+385","suffixes":[""]},"flag":"🇭🇷","flags":{"png":"https://flagcdn.com/w320/hr.png"},"independent":true},
{"name":{"common":"Slovakia","official":"Slovak Republic"},"cca2":"SK","cca3":"SVK","region":"Europe","subregion":"Central Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+421","suffixes":[""]},"flag":"🇸🇰","flags":{"png":"https://flagcdn.com/w320/sk.png"},"independent":true},
{"name":{"common":"Slovenia","official":"Republic of Slovenia"},"cca2":"SI","cca3":"SVN","region":"Europe","subregion":"Central Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+386","suffixes":[""]},"flag":"🇸🇮","flags":{"png":"https://flagcdn.com/w320/si.png"},"independent":true},
{"name":{"common":"Lithuania","official":"Republic of Lithuania"},"cca2":"LT","cca3":"LTU","region":"Europe","subregion":"Northern Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+370","suffixes":[""]},"flag":"🇱🇹","flags":{"png":"https://flagcdn.com/w320/lt.png"},"independent":true},
{"name":{"common":"Latvia","official":"Republic of Latvia"},"cca2":"LV","cca3":"LVA","region":"Europe","subregion":"Northern Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+371","suffixes":[""]},"flag":"🇱🇻","flags":{"png":"https://flagcdn.com/w320/lv.png"},"independent":true},
{"name":{"common":"Estonia","official":"Republic of Estonia"},"cca2":"EE","cca3":"EST","region":"Europe","subregion":"Northern Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+372","suffixes":[""]},"flag":"🇪🇪","flags":{"png":"https://flagcdn.com/w320/ee.png"},"independent":true},
{"name":{"common":"Ukraine","official":"Ukraine"},"cca2":"UA","cca3":"UKR","region":"Europe","subregion":"Eastern Europe","currencies":{"UAH":{"name":"Ukrainian hryvnia","symbol":"₴"}},"idd":{"root":"+380","suffixes":[""]},"flag":"🇺🇦","flags":{"png":"https://flagcdn.com/w320/ua.png"},"independent":true},
{"name":{"common":"Morocco","official":"Kingdom of Morocco"},"cca2":"MA","cca3":"MAR","region":"Africa","subregion":"Northern Africa","currencies":{"MAD":{"name":"Moroccan dirham","symbol":"د.م."}},"idd":{"root":"+212","suffixes":[""]},"flag":"🇲🇦","flags":{"png":"https://flagcdn.com/w320/ma.png"},"independent":true},
{"name":{"common":"Kenya","official":"Republic of Kenya"},"cca2":"KE","cca3":"KEN","region":"Africa","subregion":"Eastern Africa","currencies":{"KES":{"name":"Kenyan shilling","symbol":"KSh"}},"idd":{"root":"+254","suffixes":[""]},"flag":"🇰🇪","flags":{"png":"https://flagcdn.com/w320/ke.png"},"independent":true},
{"name":{"common":"Pakistan","official":"Islamic Republic of Pakistan"},"cca2":"PK","cca3":"PAK","region":"Asia","subregion":"Southern Asia","currencies":{"PKR":{"name":"Pakistani rupee","symbol":"₨"}},"idd":{"root":"+92","suffixes":[""]},"flag":"🇵🇰","flags":{"png":"https://flagcdn.com/w320/pk.png"},"independent":true},
{"name":{"common":"Bangladesh","official":"People's Republic of Bangladesh"},"cca2":"BD","cca3":"BGD","region":"Asia","subregion":"Southern Asia","currencies":{"BDT":{"name":"Bangladeshi taka","symbol":"৳"}},"idd":{"root":"+880","suffixes":[""]},"flag":"🇧🇩","flags":{"png":"https://flagcdn.com/w320/bd.png"},"independent":true},
{"name":{"common":"Iran","official":"Islamic Republic of Iran"},"cca2":"IR","cca3":"IRN","region":"Asia","subregion":"Southern Asia","currencies":{"IRR":{"name":"Iranian rial","symbol":"﷼"}},"idd":{"root":"+98","suffixes":[""]},"flag":"🇮🇷","flags":{"png":"https://flagcdn.com/w320/ir.png"},"independent":true},
{"name":{"common":"Iraq","official":"Republic of Iraq"},"cca2":"IQ","cca3":"IRQ","region":"Asia","subregion":"Western Asia","currencies":{"IQD":{"name":"Iraqi dinar","symbol":"د.ع"}},"idd":{"root":"+964","suffixes":[""]},"flag":"🇮🇶","flags":{"png":"https://flagcdn.com/w320/iq.png"},"independent":true},
{"name":{"common":"Kuwait","official":"State of Kuwait"},"cca2":"KW","cca3":"KWT","region":"Asia","subregion":"Western Asia","currencies":{"KWD":{"name":"Kuwaiti dinar","symbol":"د.ك"}},"idd":{"root":"+965","suffixes":[""]},"flag":"🇰🇼","flags":{"png":"https://flagcdn.com/w320/kw.png"},"independent":true},
{"name":{"common":"Qatar","official":"State of Qatar"},"cca2":"QA","cca3":"QAT","region":"Asia","subregion":"Western Asia","currencies":{"QAR":{"name":"Qatari riyal","symbol":"ر.ق"}},"idd":{"root":"+974","suffixes":[""]},"flag":"🇶🇦","flags":{"png":"https://flagcdn.com/w320/qa.png"},"independent":true},
{"name":{"common":"Hong Kong","official":"Hong Kong Special Administrative Region of China"},"cca2":"HK","cca3":"HKG","region":"Asia","subregion":"Eastern Asia","currencies":{"HKD":{"name":"Hong Kong dollar","symbol":"$"}},"idd":{"root":"+852","suffixes":[""]},"flag":"🇭🇰","flags":{"png":"https://flagcdn.com/w320/hk.png"},"independent":false},
{"name":{"common":"Taiwan","official":"Republic of China"},"cca2":"TW","cca3":"TWN","region":"Asia","subregion":"Eastern Asia","currencies":{"TWD":{"name":"New Taiwan dollar","symbol":"$"}},"idd":{"root":"+886","suffixes":[""]},"flag":"🇹🇼","flags":{"png":"https://flagcdn.com/w320/tw.png"},"independent":false},
{"name":{"common":"Iceland","official":"Iceland"},"cca2":"IS","cca3":"ISL","region":"Europe","subregion":"Northern Europe","currencies":{"ISK":{"name":"Icelandic króna","symbol":"kr"}},"idd":{"root":"+354","suffixes":[""]},"flag":"🇮🇸","flags":{"png":"https://flagcdn.com/w320/is.png"},"independent":true},
{"name":{"common":"Luxembourg","official":"Grand Duchy of Luxembourg"},"cca2":"LU","cca3":"LUX","region":"Europe","subregion":"Western Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+352","suffixes":[""]},"flag":"🇱🇺","flags":{"png":"https://flagcdn.com/w320/lu.png"},"independent":true},
{"name":{"common":"Malta","official":"Republic of Malta"},"cca2":"MT","cca3":"MLT","region":"Europe","subregion":"Southern Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+356","suffixes":[""]},"flag":"🇲🇹","flags":{"png":"https://flagcdn.com/w320/mt.png"},"independent":true},
{"name":{"common":"Cyprus","official":"Republic of Cyprus"},"cca2":"CY","cca3":"CYP","region":"Europe","subregion":"Southern Europe","currencies":{"EUR":{"name":"Euro","symbol":"€"}},"idd":{"root":"+357","suffixes":[""]},"flag":"🇨🇾","flags":{"png":"https://flagcdn.com/w320/cy.png"},"independent":true},
{"name":{"common":"Albania","official":"Republic of Albania"},"cca2":"AL","cca3":"ALB","region":"Europe","subregion":"Southeast Europe","currencies":{"ALL":{"name":"Albanian lek","symbol":"L"}},"idd":{"root":"+355","suffixes":[""]},"flag":"🇦🇱","flags":{"png":"https://flagcdn.com/w320/al.png"},"independent":true},
{"name":{"common":"Serbia","official":"Republic of Serbia"},"cca2":"RS","cca3":"SRB","region":"Europe","subregion":"Southeast Europe","currencies":{"RSD":{"name":"Serbian dinar","symbol":"дин"}},"idd":{"root":"+381","suffixes":[""]},"flag":"🇷🇸","flags":{"png":"https://flagcdn.com/w320/rs.png"},"independent":true},
{"name":{"common":"Belarus","official":"Republic of Belarus"},"cca2":"BY","cca3":"BLR","region":"Europe","subregion":"Eastern Europe","currencies":{"BYN":{"name":"Belarusian ruble","symbol":"Br"}},"idd":{"root":"+375","suffixes":[""]},"flag":"🇧🇾","flags":{"png":"https://flagcdn.com/w320/by.png"},"independent":true},
{"name":{"common":"Kazakhstan","official":"Republic of Kazakhstan"},"cca2":"KZ","cca3":"KAZ","region":"Asia","subregion":"Central Asia","currencies":{"KZT":{"name":"Kazakhstani tenge","symbol":"₸"}},"idd":{"root":"+7","suffixes":[""]},"flag":"🇰🇿","flags":{"png":"https://flagcdn.com/w320/kz.png"},"independent":true},
{"name":{"common":"Uzbekistan","official":"Republic of Uzbekistan"},"cca2":"UZ","cca3":"UZB","region":"Asia","subregion":"Central Asia","currencies":{"UZS":{"name":"Uzbekistani soʻm","symbol":"so'm"}},"idd":{"root":"+998","suffixes":[""]},"flag":"🇺🇿","flags":{"png":"https://flagcdn.com/w320/uz.png"},"independent":true},
{"name":{"common":"Sri Lanka","official":"Democratic Socialist Republic of Sri Lanka"},"cca2":"LK","cca3":"LKA","region":"Asia","subregion":"Southern Asia","currencies":{"LKR":{"name":"Sri Lankan rupee","symbol":"Rs"}},"idd":{"root":"+94","suffixes":[""]},"flag":"🇱🇰","flags":{"png":"https://flagcdn.com/w320/lk.png"},"independent":true},
{"name":{"common":"Cambodia","official":"Kingdom of Cambodia"},"cca2":"KH","cca3":"KHM","region":"Asia","subregion":"South-Eastern Asia","currencies":{"KHR":{"name":"Cambodian riel","symbol":"៛"}},"idd":{"root":"+855","suffixes":[""]},"flag":"🇰🇭","flags":{"png":"https://flagcdn.com/w320/kh.png"},"independent":true},
{"name":{"common":"Myanmar","official":"Republic of the Union of Myanmar"},"cca2":"MM","cca3":"MMR","region":"Asia","subregion":"South-Eastern Asia","currencies":{"MMK":{"name":"Burmese kyat","symbol":"K"}},"idd":{"root":"+95","suffixes":[""]},"flag":"🇲🇲","flags":{"png":"https://flagcdn.com/w320/mm.png"},"independent":true},
{"name":{"common":"Laos","official":"Lao People's Democratic Republic"},"cca2":"LA","cca3":"LAO","region":"Asia","subregion":"South-Eastern Asia","currencies":{"LAK":{"name":"Lao kip","symbol":"₭"}},"idd":{"root":"+856","suffixes":[""]},"flag":"🇱🇦","flags":{"png":"https://flagcdn.com/w320/la.png"},"independent":true},
{"name":{"common":"Nepal","official":"Federal Democratic Republic of Nepal"},"cca2":"NP","cca3":"NPL","region":"Asia","subregion":"Southern Asia","currencies":{"NPR":{"name":"Nepalese rupee","symbol":"रू"}},"idd":{"root":"+977","suffixes":[""]},"flag":"🇳🇵","flags":{"png":"https://flagcdn.com/w320/np.png"},"independent":true},
{"name":{"common":"Afghanistan","official":"Islamic Republic of Afghanistan"},"cca2":"AF","cca3":"AFG","region":"Asia","subregion":"Southern Asia","currencies":{"AFN":{"name":"Afghan afghani","symbol":"؋"}},"idd":{"root":"+93","suffixes":[""]},"flag":"🇦🇫","flags":{"png":"https://flagcdn.com/w320/af.png"},"independent":true},
{"name":{"common":"Jordan","official":"Hashemite Kingdom of Jordan"},"cca2":"JO","cca3":"JOR","region":"Asia","subregion":"Western Asia","currencies":{"JOD":{"name":"Jordanian dinar","symbol":"د.ا"}},"idd":{"root":"+962","suffixes":[""]},"flag":"🇯🇴","flags":{"png":"https://flagcdn.com/w320/jo.png"},"independent":true},
{"name":{"common":"Lebanon","official":"Lebanese Republic"},"cca2":"LB","cca3":"LBN","region":"Asia","subregion":"Western Asia","currencies":{"LBP":{"name":"Lebanese pound","symbol":"ل.ل"}},"idd":{"root":"+961","suffixes":[""]},"flag":"🇱🇧","flags":{"png":"https://flagcdn.com/w320/lb.png"},"independent":true},
{"name":{"common":"Oman","official":"Sultanate of Oman"},"cca2":"OM","cca3":"OMN","region":"Asia","subregion":"Western Asia","currencies":{"OMR":{"name":"Omani rial","symbol":"ر.ع."}},"idd":{"root":"+968","suffixes":[""]},"flag":"🇴🇲","flags":{"png":"https://flagcdn.com/w320/om.png"},"independent":true},
{"name":{"common":"Bahrain","official":"Kingdom of Bahrain"},"cca2":"BH","cca3":"BHR","region":"Asia","subregion":"Western Asia","currencies":{"BHD":{"name":"Bahraini dinar","symbol":"د.ب"}},"idd":{"root":"+973","suffixes":[""]},"flag":"🇧🇭","flags":{"png":"https://flagcdn.com/w320/bh.png"},"independent":true},
{"name":{"common":"Algeria","official":"People's Democratic Republic of Algeria"},"cca2":"DZ","cca3":"DZA","region":"Africa","subregion":"Northern Africa","currencies":{"DZD":{"name":"Algerian dinar","symbol":"دج"}},"idd":{"root":"+213","suffixes":[""]},"flag":"🇩🇿","flags":{"png":"https://flagcdn.com/w320/dz.png"},"independent":true},
{"name":{"common":"Tunisia","official":"Republic of Tunisia"},"cca2":"TN","cca3":"TUN","region":"Africa","subregion":"Northern Africa","currencies":{"TND":{"name":"Tunisian dinar","symbol":"د.ت"}},"idd":{"root":"+216","suffixes":[""]},"flag":"🇹🇳","flags":{"png":"https://flagcdn.com/w320/tn.png"},"independent":true},
{"name":{"common":"Ghana","official":"Republic of Ghana"},"cca2":"GH","cca3":"GHA","region":"Africa","subregion":"Western Africa","currencies":{"GHS":{"name":"Ghanaian cedi","symbol":"₵"}},"idd":{"root":"+233","suffixes":[""]},"flag":"🇬🇭","flags":{"png":"https://flagcdn.com/w320/gh.png"},"independent":true},
{"name":{"common":"Ethiopia","official":"Federal Democratic Republic of Ethiopia"},"cca2":"ET","cca3":"ETH","region":"Africa","subregion":"Eastern Africa","currencies":{"ETB":{"name":"Ethiopian birr","symbol":"Br"}},"idd":{"root":"+251","suffixes":[""]},"flag":"🇪🇹","flags":{"png":"https://flagcdn.com/w320/et.png"},"independent":true},
{"name":{"common":"Tanzania","official":"United Republic of Tanzania"},"cca2":"TZ","cca3":"TZA","region":"Africa","subregion":"Eastern Africa","currencies":{"TZS":{"name":"Tanzanian shilling","symbol":"Sh"}},"idd":{"root":"+255","suffixes":[""]},"flag":"🇹🇿","flags":{"png":"https://flagcdn.com/w320/tz.png"},"independent":true},
{"name":{"common":"Uganda","official":"Republic of Uganda"},"cca2":"UG","cca3":"UGA","region":"Africa","subregion":"Eastern Africa","currencies":{"UGX":{"name":"Ugandan shilling","symbol":"Sh"}},"idd":{"root":"+256","suffixes":[""]},"flag":"🇺🇬","flags":{"png":"https://flagcdn.com/w320/ug.png"},"independent":true},
{"name":{"common":"Cameroon","official":"Republic of Cameroon"},"cca2":"CM","cca3":"CMR","region":"Africa","subregion":"Middle Africa","currencies":{"XAF":{"name":"Central African CFA franc","symbol":"FCFA"}},"idd":{"root":"+237","suffixes":[""]},"flag":"🇨🇲","flags":{"png":"https://flagcdn.com/w320/cm.png"},"independent":true},
{"name":{"common":"Angola","official":"Republic of Angola"},"cca2":"AO","cca3":"AGO","region":"Africa","subregion":"Middle Africa","currencies":{"AOA":{"name":"Angolan kwanza","symbol":"Kz"}},"idd":{"root":"+244","suffixes":[""]},"flag":"🇦🇴","flags":{"png":"https://flagcdn.com/w320/ao.png"},"independent":true},
{"name":{"common":"Senegal","official":"Republic of Senegal"},"cca2":"SN","cca3":"SEN","region":"Africa","subregion":"Western Africa","currencies":{"XOF":{"name":"West African CFA franc","symbol":"FCFA"}},"idd":{"root":"+221","suffixes":[""]},"flag":"🇸🇳","flags":{"png":"https://flagcdn.com/w320/sn.png"},"independent":true},
{"name":{"common":"Zimbabwe","official":"Republic of Zimbabwe"},"cca2":"ZW","cca3":"ZWE","region":"Africa","subregion":"Eastern Africa","currencies":{"ZWL":{"name":"Zimbabwean dollar","symbol":"$"}},"idd":{"root":"+263","suffixes":[""]},"flag":"🇿🇼","flags":{"png":"https://flagcdn.com/w320/zw.png"},"independent":true},
{"name":{"common":"Bolivia","official":"Plurinational State of Bolivia"},"cca2":"BO","cca3":"BOL","region":"Americas","subregion":"South America","currencies":{"BOB":{"name":"Bolivian boliviano","symbol":"Bs"}},"idd":{"root":"+591","suffixes":[""]},"flag":"🇧🇴","flags":{"png":"https://flagcdn.com/w320/bo.png"},"independent":true},
{"name":{"common":"Ecuador","official":"Republic of Ecuador"},"cca2":"EC","cca3":"ECU","region":"Americas","subregion":"South America","currencies":{"USD":{"name":"United States dollar","symbol":"$"}},"idd":{"root":"+593","suffixes":[""]},"flag":"🇪🇨","flags":{"png":"https://flagcdn.com/w320/ec.png"},"independent":true},
{"name":{"common":"Uruguay","official":"Oriental Republic of Uruguay"},"cca2":"UY","cca3":"URY","region":"Americas","subregion":"South America","currencies":{"UYU":{"name":"Uruguayan peso","symbol":"$"}},"idd":{"root":"+598","suffixes":[""]},"flag":"🇺🇾","flags":{"png":"https://flagcdn.com/w320/uy.png"},"independent":true},
{"name":{"common":"Paraguay","official":"Republic of Paraguay"},"cca2":"PY","cca3":"PRY","region":"Americas","subregion":"South America","currencies":{"PYG":{"name":"Paraguayan guaraní","symbol":"₲"}},"idd":{"root":"+595","suffixes":[""]},"flag":"🇵🇾","flags":{"png":"https://flagcdn.com/w320/py.png"},"independent":true},
{"name":{"common":"Venezuela","official":"Bolivarian Republic of Venezuela"},"cca2":"VE","cca3":"VEN","region":"Americas","subregion":"South America","currencies":{"VES":{"name":"Venezuelan bolívar soberano","symbol":"Bs.S"}},"idd":{"root":"+58","suffixes":[""]},"flag":"🇻🇪","flags":{"png":"https://flagcdn.com/w320/ve.png"},"independent":true},
{"name":{"common":"Dominican Republic","official":"Dominican Republic"},"cca2":"DO","cca3":"DOM","region":"Americas","subregion":"Caribbean","currencies":{"DOP":{"name":"Dominican peso","symbol":"$"}},"idd":{"root":"+1","suffixes":["809"]},"flag":"🇩🇴","flags":{"png":"https://flagcdn.com/w320/do.png"},"independent":true},
{"name":{"common":"Guatemala","official":"Republic of Guatemala"},"cca2":"GT","cca3":"GTM","region":"Americas","subregion":"Central America","currencies":{"GTQ":{"name":"Guatemalan quetzal","symbol":"Q"}},"idd":{"root":"+502","suffixes":[""]},"flag":"🇬🇹","flags":{"png":"https://flagcdn.com/w320/gt.png"},"independent":true},
{"name":{"common":"Panama","official":"Republic of Panama"},"cca2":"PA","cca3":"PAN","region":"Americas","subregion":"Central America","currencies":{"USD":{"name":"United States dollar","symbol":"$"}},"idd":{"root":"+507","suffixes":[""]},"flag":"🇵🇦","flags":{"png":"https://flagcdn.com/w320/pa.png"},"independent":true},
{"name":{"common":"Costa Rica","official":"Republic of Costa Rica"},"cca2":"CR","cca3":"CRI","region":"Americas","subregion":"Central America","currencies":{"CRC":{"name":"Costa Rican colón","symbol":"₡"}},"idd":{"root":"+506","suffixes":[""]},"flag":"🇨🇷","flags":{"png":"https://flagcdn.com/w320/cr.png"},"independent":true},
{"name":{"common":"Vietnam","official":"Socialist Republic of Vietnam"},"cca2":"VN","cca3":"VNM","region":"Asia","subregion":"South-Eastern Asia","currencies":{"VND":{"name":"Vietnamese đồng","symbol":"₫"}},"idd":{"root":"+84","suffixes":[""]},"flag":"🇻🇳","flags":{"png":"https://flagcdn.com/w320/vn.png"},"independent":true}
]
@@ -0,0 +1,175 @@
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
login.blocked=很遗憾,访问IP已被列入系统黑名单
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
##价格模块提示语(price)
# 附加费 PrcSurcharge
price.surcharge.scName.empty=附加费名称不能为空
price.surcharge.chargeType.empty=计费方式不能为空
price.surcharge.chargeValue.empty=计费数值不能为空
price.surcharge.chargeValue.negative=计费数值不能为负数
# 附加费关联 PrcSurchargeRel
price.surchargeRel.scId.empty=附加费ID不能为空
price.surchargeRel.productId.empty=产品ID不能为空
price.surchargeRel.productCode.empty=产品代码不能为空
# 套餐 PrcPriceSet
price.set.notExists=套餐不存在
price.set.name.empty=套餐名称不能为空
price.set.name.exists=套餐名称已存在
price.set.effectiveDate.empty=生效日期不能为空
price.set.date.invalid=生效日期不能晚于失效日期
price.set.discount.range=折扣模式默认折扣需在 1~99
price.set.delete.statusNotAllowed=仅待启用或驳回状态的套餐允许删除:{0}
price.set.delete.boundCustomer=套餐【{0}】已绑定客户,请先解绑后再删除
price.set.submit.statusNotAllowed=当前状态不允许提交审核
price.set.approve.statusNotAllowed=仅审核中的套餐可以生效
price.set.reject.statusNotAllowed=仅审核中的套餐可以驳回
price.set.disable.statusNotAllowed=仅生效中的套餐可停用
price.set.enable.statusNotAllowed=套餐已是生效状态
price.set.edit.audit=审核中的套餐不允许修改
price.set.edit.stopped=已停用的套餐不允许修改,请使用复用新建
price.set.childEdit.audit=审核中的套餐不允许改明细或客户
price.set.childEdit.stopped=已停用的套餐不允许改明细或客户
price.set.detail.notExists=套餐明细不存在
price.set.detail.productCode.empty=明细产品代码不能为空
price.set.detail.country.empty=固定价明细必须填写国家
price.set.detail.weight.empty=固定价明细必须填写重量段
price.set.detail.freight.empty=固定价明细必须填写运费单价
price.set.detail.registration.empty=固定价明细必须填写挂号费
price.set.detail.zone.empty=固定价明细必须选择分区
price.set.detail.roundMode.invalid=取值方式仅支持 UP / DOWN / HALF_UP
price.set.detail.freight.belowBase=套餐运费不能低于导入基准价 {0}
price.set.detail.registration.belowBase=套餐挂号费不能低于导入基准价 {0}
price.set.customer.notExists=客户绑定不存在
price.set.customer.unbindAudit=审核中的套餐不允许解绑客户
price.set.customer.customerId.empty=客户ID不能为空
price.set.customer.beginDate.empty=客户绑定生效日期不能为空
price.set.customer.date.invalid=客户绑定生效日期不能晚于失效日期
price.set.customer.alreadyBound=该客户已绑定套餐,一个客户只能绑定一个套餐
price.set.customer.masterNotFound=客户主数据不存在
price.set.customer.priority.invalid=优先级必须大于等于 1
# 重量段 PrcWeight
price.weight.range.empty=起始重量和结束重量不能为空
price.weight.range.invalid=起始重量必须小于结束重量
# Zone PrcZone (country -> zone -> postcode)
price.zone.import.fileEmpty=上传文件不能为空
price.zone.import.fileType=仅支持 xls / xlsx 文件
price.zone.import.noSheet=未找到可导入的分区 sheet(澳大利亚分区/加拿大偏远附加费/法国MR邮编/日本附加费/英国附加费)
price.zone.import.noData=分区 sheet 无有效邮编/分区数据
price.zone.import.success=导入成功:{0} 个国家,{1} 个分区,{2} 条邮编明细({3})
price.zone.import.unmatched=;未匹配国家已跳过:{0}
price.zone.import.regionNotFound=未在 bms_region 匹配到国家:{0}
price.zone.import.regionEmpty=bms_region 无可用国家数据,请先同步/维护国家表
price.zone.region.notFound=国家不存在或已删除
price.zone.region.required=请选择国家(bms_region)
price.zone.countryCode.empty=国家编码不能为空,请检查 bms_region.country_code_2
# 产品导入 BmsProduct
price.product.codeDate.exists=同一产品代码与生效日期已存在,不能覆盖历史数据
price.product.effectiveDate.empty=生效日期不能为空
price.product.import.effEmpty=缺少生效日期,已跳过
price.product.import.fileEmpty=上传文件不能为空
price.product.import.fileType=仅支持 xls / xlsx 文件
price.product.import.sheetNotFound=未找到「产品目录」sheet,请上传威狮B2C小包价表格格式文件
price.product.import.noData=产品目录无有效数据行
price.product.import.regionEmpty=bms_region 无可用国家数据,请先同步/维护国家表后再导入
price.product.import.partialFail=导入完成但存在失败项:新增 {0},跳过 {1};失败明细:{2}
price.product.import.effTooEarly=生效日期 {0} 早于当前开放版 {1},请使用更晚的生效日期导入
price.product.import.needFeeAlter=请先执行 SQL:bms-back/sql/price/product_country_fee_alter.sql(缺少运费/挂号费字段)
price.product.import.needExpireAlter=请先执行 SQL:bms-back/sql/price/product_country_expire_alter.sql(缺少失效日期/版本唯一键)
price.product.import.needCodeEffUnique=无法新增「同代码、不同生效日」的产品。请先执行 SQL:bms-back/sql/price/product_code_eff_unique_alter.sql(去掉 product_code 单列唯一,改为代码+生效日唯一)
price.product.import.success=导入成功:新增 {0} 个产品,跳过 {1} 个(同代码同生效日不覆盖);写入国家/重量段明细 {2} 行。
price.product.import.success.detail1=产品主数据按「产品代码+生效日期」新增,不覆盖已有记录。国家明细:新生效日晚于上次则保留历史,上次失效日=本次生效日。
price.product.import.success.detail2=范围:产品目录 + 重量段 + 运费/挂号费 + 尺寸/尾程/时效;国家码来自 bms_region。
price.product.import.noSheet=未找到产品明细sheet(需与产品名称一致):{0}
price.product.import.noCountry=产品明细sheet无有效国家/重量段数据:{0}
price.product.import.euEmpty=国家表无欧盟成员(is_eu=1),无法展开「欧盟」
price.product.import.success.unmatched=未匹配国家表(country_code 为空):{0}
price.product.import.countryRequired=请选择目的国家
price.product.import.countryNotFound=国家不存在或已停用:{0}
price.product.import.countrySheetNotFound=未找到「产品目录」sheet,请上传单国专线成本价表
price.product.import.countryNoPrice=未找到该产品的重量段价格:{0}
price.product.import.countrySuccess=按国家导入成功({0}):新增 {1} 个产品,跳过 {2} 个(同代码同生效日不覆盖主数据);写入重量段 {3} 行。
# 报价试算 TmsPriceQuote(对外 TMS API 错误码,保持英文契约)
price.quote.postcodeRequired=该国家/产品有分区,请填写目的邮编
price.quote.postcodeZoneNotFound=邮编未匹配到分区,请核对目的邮编
price.quote.zonePriceNotHit=已匹配分区,但该分区无对应产品重量段价格
price.quote.priceNotHit=没有产品/国家/重量段绑定,暂无价格
price.quote.freightMissing1=产品默认运费为空
price.quote.freightMissing2=产品默认运费或挂号费为空
price.quote.freightMissing3=运费为空
price.quote.requestBodyRequired=请求体为空
price.quote.customerIdRequired=客户ID为空
price.quote.customerCodeRequired=客户代码不能为空
price.quote.customerCodeNotFound=客户代码不存在
price.quote.customerDisabled=客户已停用,不可计费
price.quote.productCodeRequired=产品代码为空
price.quote.countryRequired=国家代码或国家名称为空
price.quote.weightInvalid=重量必须大于0
price.quote.bizDateInvalid=业务日期格式必须为yyyy-MM-dd
price.quote.fixedPackageNoDetails=套餐固定价格没有明细,使用产品默认价格
price.quote.noCustomerPackageOverride=没有客户套餐覆盖,使用产品默认运费/挂号费
price.quote.noCustomerPackage=客户未绑定套餐,无法试算
price.quote.quoteIdRequired=报价ID为空
price.quote.hitCustomerFixedPackageDetail=命中客户套餐固定价格明细
# receivable bill
price.bill.orderNoRequired=订单号为空
price.bill.billIdRequired=计费ID为空
price.bill.notFound=计费记录不存在
price.bill.customerRequired=订单同步缺少有效客户
price.bill.alreadyInArBill=订单{0}已生成应收账单,作废后方可重新计费
price.bill.bizDateRequired=业务日期为空
price.bill.noCustomerPackage=客户未绑定套餐,无法计费
price.bill.fee.scIdRequired=附加费不能为空
price.bill.fee.surchargeNotFound=附加费配置不存在或已停用
price.bill.fee.notFound=费用明细不存在
price.bill.fee.notManual=仅手动增加的费用可编辑或删除
price.bill.fee.alreadyBilled=费用已生成账单,不可编辑或删除
price.bill.fee.idRequired=费用ID不能为空
price.set.version.effectiveTime.empty=版本生效时间不能为空
price.set.version.notExists=套餐版本不存在
price.set.version.activate.statusNotAllowed=仅草稿或待生效版本可设置生效时间
price.set.version.delete.statusNotAllowed=仅草稿版本可删除
price.set.version.copy.draftExists=已有草稿版本,请先启动或删除后再复制
price.set.save.task.notExists=保存任务不存在或已过期
price.set.library.empty=产品库无可用价格明细,请检查产品国家配置
price.set.detail.duplicate.fixed=产品{0} 国家{1} 重量段{2} {3}(共{4}条)
price.set.detail.duplicate.discount=产品{0}(共{1}条)
price.set.detail.duplicate.hasZone=有分区({0})
price.set.detail.duplicate.noZone=无分区
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="${user.dir}/logs" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.bms" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
<!--系统用户操作日志-->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 全局参数 -->
<settings>
<!-- 使全局的映射器启用或禁用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 允许JDBC 支持自动生成主键 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J" />
<!-- 使用驼峰命名法转换字段 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
</settings>
</configuration>