Merge branch 'test'
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
# 应收账单行级权限
|
||||
|
||||
规则独立于 `@DataScope` 和角色管理的 `dataScope`,现有菜单、按钮权限保持不变。
|
||||
|
||||
| roleKey | 数据范围 |
|
||||
| --- | --- |
|
||||
| ORA_6041 / ORA_6163 | 销售员、商务专员:本人负责客户 |
|
||||
| ORA_6040 | 本部门人员负责客户,不含下级部门 |
|
||||
| ORA_6042 / ORA_6043 | 财务经理、应收财务:全部账单 |
|
||||
|
||||
超级管理员沿用系统判断。仅参与当前入口功能授权的有效角色合并范围;未知角色、空权限对象默认无数据。财务本期不使用账单 `create_user_id`。应付财务不参与应收账单权限规则。
|
||||
|
||||
归属链为 `ar_bill.customer_id → ctr_customer.sales_user_id → sys_user.user_id / dept_id`。销售员和商务专员均通过客户的 `sales_user_id` 与当前登录用户 ID 匹配本人客户。客户转交后,历史账单跟随当前归属变化。全量角色可见未关联销售、销售已删除、客户已删除的历史账单;受限角色不放行无有效归属的数据。业务员仅停用但未删除时,其部门经理仍可查看。
|
||||
|
||||
核心实现位于 `bms-system/src/main/java/com/bms/ar/security` 和 `mapper/ar/ArBillScopeMapper.xml`。内部权限对象作为独立 Mapper 参数传递,不绑定 HTTP 查询对象;后台生成和导出任务继续使用独立的内部读取方法。
|
||||
|
||||
接入范围包括列表、汇总、列表导出、详情/费用、日志、生成预览及提交、生成任务、导出任务及下载、客户账单入口,以及客户/收款页面的待核销账单、核销记录、核销和反核销。应收筛选及生成客户选项分别使用 `/ar/bill/customer-options`、`/ar/bill/generate/customer-options`。
|
||||
|
||||
用户编辑、调部门、停用、分配角色,以及角色编辑、停用、删除和授权关系调整,会在事务提交后让受影响的登录会话失效;相关用户需要重新登录。不改变角色配置本身。
|
||||
|
||||
## 本地验证
|
||||
|
||||
在 `bms-back` 下执行(PowerShell):
|
||||
|
||||
```powershell
|
||||
mvn -o -pl bms-admin -am -DskipTests compile
|
||||
```
|
||||
|
||||
本次新增的行级权限测试用例及其辅助脚本已按要求删除。
|
||||
|
||||
在 `bms-front` 下执行 `npm run build:prod` 验证前端构建。
|
||||
|
||||
## 部署前核实
|
||||
|
||||
- 本次没有新增数据库字段、权限配置表或迁移 SQL。
|
||||
- 目标环境 `ctr_customer.sales_user_id` 必须已经统一为系统用户 ID,不能仍是旧业务员工号。现有迁移文件位于 `sql/customer+supplier/20261028_customer_sales_user_migration.sql`,需要按原有流程确认执行状态,本次不自动执行。
|
||||
- 使用真实销售 A/B、经理、财务和商务专员账号检查数据范围与功能授权,特别是生产角色实际拥有的接口权限。
|
||||
- 上线前仍需在 MySQL 5.7 环境联调,并检查实际数据的执行计划。
|
||||
@@ -35,6 +35,7 @@ import com.bms.ar.domain.vo.ArBillGenerateRequest;
|
||||
import com.bms.ar.domain.vo.ArBillListExportRow;
|
||||
import com.bms.ar.domain.vo.ArBillQuery;
|
||||
import com.bms.ar.service.IArBillService;
|
||||
import com.bms.ar.security.ArBillScopeResolver;
|
||||
import com.bms.common.annotation.Log;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
@@ -59,6 +60,24 @@ public class ArBillController extends BaseController {
|
||||
@Autowired
|
||||
private IArBillService billService;
|
||||
|
||||
@Autowired
|
||||
private ArBillScopeResolver billScope;
|
||||
|
||||
/** 应收筛选专用客户选项,与账单使用同一角色规则。 */
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:list')")
|
||||
@GetMapping("/customer-options")
|
||||
public TableDataInfo customerOptions(@RequestParam(required = false) String keywords) {
|
||||
startPage();
|
||||
return getDataTable(billScope.selectCustomerOptions(keywords));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
|
||||
@GetMapping("/generate/customer-options")
|
||||
public TableDataInfo generateCustomerOptions(@RequestParam(required = false) String keywords) {
|
||||
startPage();
|
||||
return getDataTable(billScope.selectCustomerOptions(keywords));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询参数中的日期必须严格按 yyyy-MM-dd 解析,禁止非法日期自动滚动到其他日期。
|
||||
*/
|
||||
|
||||
+28
@@ -5,6 +5,7 @@ import java.util.List;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerAttachment;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerCompany;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerContact;
|
||||
import com.bms.customer.customerinfo.vo.UnlinkedSalesCustomerVO;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -22,6 +23,7 @@ import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomer;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerOption;
|
||||
import com.bms.customer.customerinfo.service.IBmsCustomerService;
|
||||
import com.bms.common.utils.poi.ExcelUtil;
|
||||
import com.bms.common.core.page.TableDataInfo;
|
||||
@@ -51,6 +53,32 @@ public class BmsCustomerController extends BaseController
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/** 查询未关联业务员的客户列表 */
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
|
||||
@GetMapping("/unlinked-sales-user/list")
|
||||
public TableDataInfo unlinkedSalesUserList()
|
||||
{
|
||||
startPage();
|
||||
BmsCustomer query = new BmsCustomer();
|
||||
List<UnlinkedSalesCustomerVO> list = bmsCustomerService.selectUnlinkedSalesUserCustomerList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户下拉选项。
|
||||
* 只返回客户ID、客户编码、客户名称,供各页面下拉框使用。
|
||||
* 查询参数 keywords 按客户名称、客户编码、对外代码模糊匹配,多行之间为或者关系。
|
||||
* 分页参数与原下拉一致,避免一次查出全部客户。
|
||||
*/
|
||||
// @PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
|
||||
@GetMapping("/optionselect")
|
||||
public TableDataInfo optionselect(BmsCustomer query)
|
||||
{
|
||||
startPage();
|
||||
List<BmsCustomerOption> list = bmsCustomerService.selectCustomerOptions(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出客户信息列表
|
||||
*/
|
||||
|
||||
+98
-1
@@ -1,17 +1,29 @@
|
||||
package com.bms.web.controller.masterdata;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.bms.common.annotation.Log;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.core.page.TableDataInfo;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.common.utils.poi.ExcelUtil;
|
||||
import com.bms.masterdata.domain.ServiceChannel;
|
||||
import com.bms.masterdata.service.IServiceChannelService;
|
||||
|
||||
/**
|
||||
* 服务渠道(老系统镜像 srv_service_channel)
|
||||
* 渠道管理(srv_service_channel)
|
||||
* 下拉选项仍走 /optionselect,登录即可,供供应商绑定渠道使用。
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
@@ -23,6 +35,32 @@ public class ServiceChannelController extends BaseController
|
||||
@Autowired
|
||||
private IServiceChannelService serviceChannelService;
|
||||
|
||||
/**
|
||||
* 分页查询渠道列表。
|
||||
* 渠道编码、渠道名称、服务商编码、服务商名称、目的港/目的地区域、发货人名称均可检索,名称类条件为模糊匹配。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(ServiceChannel query)
|
||||
{
|
||||
startPage();
|
||||
List<ServiceChannel> list = serviceChannelService.selectServiceChannelList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出渠道列表,筛选条件与列表一致。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:export')")
|
||||
@Log(title = "渠道管理", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, ServiceChannel query)
|
||||
{
|
||||
List<ServiceChannel> list = serviceChannelService.selectServiceChannelList(query);
|
||||
ExcelUtil<ServiceChannel> util = new ExcelUtil<ServiceChannel>(ServiceChannel.class);
|
||||
util.exportExcel(response, list, "渠道数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务渠道下拉选项(非分页)。
|
||||
* 供供应商绑定渠道等场景选择渠道;不做权限控制(仍需登录)。
|
||||
@@ -33,4 +71,63 @@ public class ServiceChannelController extends BaseController
|
||||
List<ServiceChannel> list = serviceChannelService.selectServiceChannelList(query);
|
||||
return success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询渠道详情
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:query')")
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getInfo(@PathVariable String id)
|
||||
{
|
||||
return success(serviceChannelService.selectServiceChannelById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增渠道
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:add')")
|
||||
@Log(title = "渠道管理", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody ServiceChannel channel)
|
||||
{
|
||||
String username = getUsername();
|
||||
channel.setCreateName(username);
|
||||
channel.setModifier(username);
|
||||
return toAjax(serviceChannelService.insertServiceChannel(channel));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改渠道
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:edit')")
|
||||
@Log(title = "渠道管理", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody ServiceChannel channel)
|
||||
{
|
||||
channel.setModifier(getUsername());
|
||||
return toAjax(serviceChannelService.updateServiceChannel(channel));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用或停用渠道
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:edit')")
|
||||
@Log(title = "渠道管理", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/changeStatus")
|
||||
public AjaxResult changeStatus(@RequestBody ServiceChannel channel)
|
||||
{
|
||||
channel.setModifier(getUsername());
|
||||
return toAjax(serviceChannelService.updateServiceChannelStatus(channel));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除渠道。已被产品渠道或供应商引用时不允许删除。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('data:serviceChannel:remove')")
|
||||
@Log(title = "渠道管理", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable String[] ids)
|
||||
{
|
||||
return toAjax(serviceChannelService.deleteServiceChannelByIds(ids));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,6 +227,14 @@ public class PriceSetController extends BaseController
|
||||
return success(priceSetService.selectDetailListByProduct(psvId, productCode));
|
||||
}
|
||||
|
||||
/** All detail rows for a version (no page); batch prefetch instead of N byProduct calls. */
|
||||
@PreAuthorize("@ss.hasPermi('price:set:list')")
|
||||
@GetMapping("/detail/byVersion")
|
||||
public AjaxResult detailByVersion(@RequestParam("psvId") Long psvId)
|
||||
{
|
||||
return success(priceSetService.selectDetailListByVersion(psvId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 版本明细搜索下拉选项:按版本去重后的 产品/国家/分区
|
||||
*/
|
||||
|
||||
+3
-3
@@ -63,13 +63,13 @@ public class ProductController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* 产品下拉选项(非分页)。
|
||||
* 供计费列表、客户绑定等场景选择产品;不做权限控制(仍需登录)。
|
||||
* Product dropdown (non-paged). Slim query, no list-page status refresh.
|
||||
* Used by price-set / billing / customer bind; login required, no extra perm.
|
||||
*/
|
||||
@GetMapping("/optionselect")
|
||||
public AjaxResult optionselect(BmsProduct product)
|
||||
{
|
||||
List<BmsProduct> list = productService.selectBmsProductList(product);
|
||||
List<BmsProduct> list = productService.selectBmsProductOptions(product);
|
||||
return success(list);
|
||||
}
|
||||
|
||||
|
||||
@@ -122,6 +122,7 @@ price.product.import.sheetNotFound=未找到「产品目录」sheet,请上传
|
||||
price.product.import.noData=产品目录无有效数据行
|
||||
price.product.import.catalogHeaderMissing=「产品目录」未找到表头「对接代码」,请在标题行下补齐表头(对接代码、产品名称、时效、走货属性、国家等)
|
||||
price.product.import.countryCatalogHeaderMissing=「产品目录」未找到表头「产品名称」或「产品ID」,请确认是否为单国专线成本价表格式
|
||||
price.product.import.singleCountryAsMulti=当前选择的是「全部导入(多国家价表)」,但上传的文件更像单国专线成本价表(价表无「国家」列)。请改用「按国家导入」,并选择目的国家后再上传。
|
||||
price.product.import.regionEmpty=region 无可用国家数据,请先同步/维护国家表后再导入
|
||||
price.product.import.partialFail=导入完成但存在失败项:新增 {0},跳过 {1};失败明细:{2}
|
||||
price.product.import.effTooEarly=产品{0}({1})导入生效时间{2}早于该产品上一版生效时间{3},不允许导入
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.bms.common.utils;
|
||||
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
||||
/** 由服务端功能鉴权写入的请求上下文,不读取请求参数。 */
|
||||
public final class PermissionContext {
|
||||
private static final String ATTRIBUTE = "PERMISSION_CONTEXT";
|
||||
|
||||
private PermissionContext() {}
|
||||
|
||||
public static void set(String permission) {
|
||||
RequestContextHolder.currentRequestAttributes().setAttribute(ATTRIBUTE, permission, RequestAttributes.SCOPE_REQUEST);
|
||||
}
|
||||
|
||||
public static String get() {
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
Object value = attributes == null ? null : attributes.getAttribute(ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
||||
return value instanceof String ? (String) value : null;
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.bms.framework.aspectj;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import com.bms.common.core.domain.entity.SysRole;
|
||||
import com.bms.common.core.domain.entity.SysUser;
|
||||
import com.bms.framework.web.service.TokenService;
|
||||
import com.bms.system.domain.SysUserRole;
|
||||
|
||||
/** 授权关系修改提交后,让旧登录态失效;不参与业务 SQL 数据范围计算。 */
|
||||
@Aspect
|
||||
@Component
|
||||
@Order(1)
|
||||
public class AuthorizationSessionAspect {
|
||||
private final TokenService tokenService;
|
||||
|
||||
public AuthorizationSessionAspect(TokenService tokenService) {
|
||||
this.tokenService = tokenService;
|
||||
}
|
||||
|
||||
@AfterReturning(pointcut = "execution(* com.bms.system.service.impl.SysUserServiceImpl.updateUser(..))"
|
||||
+ " || execution(* com.bms.system.service.impl.SysUserServiceImpl.updateUserStatus(..))"
|
||||
+ " || execution(* com.bms.system.service.impl.SysUserServiceImpl.insertUserAuth(..))"
|
||||
+ " || execution(* com.bms.system.service.impl.SysUserServiceImpl.deleteUserBy*(..))", returning = "result")
|
||||
public void userChanged(JoinPoint point, Object result) {
|
||||
if (result instanceof Number && ((Number) result).intValue() <= 0) return;
|
||||
Object target = point.getArgs()[0];
|
||||
Set<Long> users = new HashSet<>();
|
||||
if (target instanceof SysUser) users.add(((SysUser) target).getUserId());
|
||||
else if (target instanceof Long[]) users.addAll(Arrays.asList((Long[]) target));
|
||||
else if (target instanceof Long) users.add((Long) target);
|
||||
invalidate(users, Set.of());
|
||||
}
|
||||
|
||||
@AfterReturning(pointcut = "execution(* com.bms.system.service.impl.SysRoleServiceImpl.updateRole(..))"
|
||||
+ " || execution(* com.bms.system.service.impl.SysRoleServiceImpl.updateRoleStatus(..))"
|
||||
+ " || execution(* com.bms.system.service.impl.SysRoleServiceImpl.deleteRoleBy*(..))"
|
||||
+ " || execution(* com.bms.system.service.impl.SysRoleServiceImpl.deleteAuthUser*(..))"
|
||||
+ " || execution(* com.bms.system.service.impl.SysRoleServiceImpl.insertAuthUsers(..))", returning = "result")
|
||||
public void roleChanged(JoinPoint point, Object result) {
|
||||
if (result instanceof Number && ((Number) result).intValue() <= 0) return;
|
||||
Object target = point.getArgs()[0];
|
||||
Set<Long> users = new HashSet<>();
|
||||
Set<Long> roles = new HashSet<>();
|
||||
if (target instanceof SysUserRole) users.add(((SysUserRole) target).getUserId());
|
||||
else if (point.getArgs().length > 1 && point.getArgs()[1] instanceof Long[])
|
||||
users.addAll(Arrays.asList((Long[]) point.getArgs()[1]));
|
||||
else if (target instanceof SysRole) roles.add(((SysRole) target).getRoleId());
|
||||
else if (target instanceof Long[]) roles.addAll(Arrays.asList((Long[]) target));
|
||||
else if (target instanceof Long) roles.add((Long) target);
|
||||
invalidate(users, roles);
|
||||
}
|
||||
|
||||
private void invalidate(Set<Long> users, Set<Long> roles) {
|
||||
users.remove(null);
|
||||
Set<Long> roleIds = new HashSet<>(roles);
|
||||
roleIds.remove(null);
|
||||
Set<Long> userIds = Set.copyOf(users);
|
||||
if (userIds.isEmpty() && roleIds.isEmpty()) return;
|
||||
Runnable action = () -> tokenService.evictAuthorizationSessions(userIds, roleIds);
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() { action.run(); }
|
||||
});
|
||||
} else {
|
||||
action.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-9
@@ -1,8 +1,6 @@
|
||||
package com.bms.framework.security.context;
|
||||
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import com.bms.common.core.text.Convert;
|
||||
import com.bms.common.utils.PermissionContext;
|
||||
|
||||
/**
|
||||
* 权限信息
|
||||
@@ -11,17 +9,13 @@ import com.bms.common.core.text.Convert;
|
||||
*/
|
||||
public class PermissionContextHolder
|
||||
{
|
||||
private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT";
|
||||
|
||||
public static void setContext(String permission)
|
||||
{
|
||||
RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission,
|
||||
RequestAttributes.SCOPE_REQUEST);
|
||||
PermissionContext.set(permission);
|
||||
}
|
||||
|
||||
public static String getContext()
|
||||
{
|
||||
return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES,
|
||||
RequestAttributes.SCOPE_REQUEST));
|
||||
return PermissionContext.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.bms.framework.web.service;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -231,6 +232,22 @@ public class TokenService
|
||||
return CacheConstants.LOGIN_TOKEN_KEY + uuid;
|
||||
}
|
||||
|
||||
/** 授权或部门变更后失效受影响会话,避免缓存中的旧角色继续扩大业务范围。 */
|
||||
public void evictAuthorizationSessions(Set<Long> userIds, Set<Long> roleIds)
|
||||
{
|
||||
Collection<String> keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*");
|
||||
if (keys == null) return;
|
||||
for (String key : keys)
|
||||
{
|
||||
LoginUser user = redisCache.getCacheObject(key);
|
||||
if (user == null || user.getUser() == null) continue;
|
||||
boolean affected = userIds.contains(user.getUserId())
|
||||
|| (user.getUser().getRoles() != null && user.getUser().getRoles().stream()
|
||||
.anyMatch(role -> role != null && roleIds.contains(role.getRoleId())));
|
||||
if (affected) redisCache.deleteObject(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色权限变更后,刷新所有持有该角色的在线用户权限
|
||||
*
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.bms.ar.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.ar.security.ArBillRowScope;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.ar.domain.ArBalanceWriteoff;
|
||||
|
||||
@@ -33,5 +35,5 @@ public interface ArBalanceWriteoffMapper
|
||||
@Param("effectiveStatus") String effectiveStatus);
|
||||
|
||||
/** 查询核销记录列表(关联账单名称/客户名称) */
|
||||
List<ArBalanceWriteoff> selectWriteoffList(ArBalanceWriteoff query);
|
||||
List<ArBalanceWriteoff> selectWriteoffList(@Param("query") ArBalanceWriteoff query, @Param("scope") ArBillRowScope scope);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.bms.ar.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.ar.security.ArBillRowScope;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.ar.domain.ArBillDetailFee;
|
||||
|
||||
public interface ArBillDetailFeeMapper {
|
||||
List<ArBillDetailFee> selectByBillIdAndDetailIdScoped(@Param("arBillId") Long arBillId, @Param("detailId") Long detailId, @Param("scope") ArBillRowScope scope);
|
||||
|
||||
List<ArBillDetailFee> selectByDetailId(Long detailId);
|
||||
|
||||
List<ArBillDetailFee> selectByBillIdAndDetailId(@Param("arBillId") Long arBillId,
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package com.bms.ar.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.ar.security.ArBillRowScope;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.ar.domain.ArBillDetail;
|
||||
|
||||
public interface ArBillDetailMapper {
|
||||
List<ArBillDetail> selectByBillIdScoped(@Param("arBillId") Long arBillId, @Param("scope") ArBillRowScope scope);
|
||||
|
||||
List<ArBillDetail> selectByBillId(Long arBillId);
|
||||
|
||||
List<ArBillDetail> selectByBillIdAndSourceIds(@Param("arBillId") Long arBillId,
|
||||
|
||||
@@ -10,6 +10,9 @@ import com.bms.ar.domain.ArBillGenerateTask;
|
||||
* 应收账单异步生成任务 Mapper。
|
||||
*/
|
||||
public interface ArBillGenerateTaskMapper {
|
||||
List<ArBillGenerateTask> selectActiveByCreateByScoped(@Param("createBy") String createBy,
|
||||
@Param("scope") com.bms.ar.security.ArBillRowScope scope);
|
||||
|
||||
ArBillGenerateTask selectById(Long taskId);
|
||||
|
||||
ArBillGenerateTask selectByRequestToken(String requestToken);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package com.bms.ar.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.ar.security.ArBillRowScope;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.ar.domain.ArBillLog;
|
||||
|
||||
public interface ArBillLogMapper
|
||||
{
|
||||
List<ArBillLog> selectList(ArBillLog query);
|
||||
List<ArBillLog> selectList(@Param("query") ArBillLog query, @Param("scope") ArBillRowScope scope);
|
||||
|
||||
ArBillLog selectSuccessByRequestId(@Param("requestId") String requestId,
|
||||
@Param("objectType") String objectType,
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.bms.ar.mapper;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.bms.ar.security.ArBillRowScope;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.ar.domain.ArBill;
|
||||
@@ -10,12 +11,14 @@ import com.bms.ar.domain.vo.ArBillQuery;
|
||||
import com.bms.ar.domain.vo.ArBillSummary;
|
||||
|
||||
public interface ArBillMapper {
|
||||
List<ArBill> selectArBillList(ArBillQuery query);
|
||||
List<ArBill> selectArBillList(@Param("query") ArBillQuery query, @Param("scope") ArBillRowScope scope);
|
||||
|
||||
ArBillSummary selectArBillSummary(ArBillQuery query);
|
||||
ArBillSummary selectArBillSummary(@Param("query") ArBillQuery query, @Param("scope") ArBillRowScope scope);
|
||||
|
||||
ArBill selectArBillById(Long arBillId);
|
||||
|
||||
ArBill selectArBillByIdScoped(@Param("arBillId") Long arBillId, @Param("scope") ArBillRowScope scope);
|
||||
|
||||
ArBill selectArBillByBillNo(String billNo);
|
||||
|
||||
ArBill selectGeneratingByCustomerPeriod(@Param("customerId") Long customerId,
|
||||
@@ -29,7 +32,7 @@ public interface ArBillMapper {
|
||||
/**
|
||||
* 查询客户可核销账单(已确认/部分核销),按账单日期升序,供余额核销选择
|
||||
*/
|
||||
List<ArBill> selectUnsettledByCustomer(@Param("customerId") Long customerId);
|
||||
List<ArBill> selectUnsettledByCustomer(@Param("customerId") Long customerId, @Param("scope") ArBillRowScope scope);
|
||||
|
||||
int insertArBill(ArBill bill);
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.bms.ar.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.ar.security.ArBillRowScope;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerOption;
|
||||
|
||||
public interface ArBillScopeMapper {
|
||||
int countCustomerSourceBills(@Param("customerId") Long customerId, @Param("sourceIds") List<String> sourceIds);
|
||||
|
||||
int countAccessibleBill(@Param("arBillId") Long arBillId, @Param("scope") ArBillRowScope scope);
|
||||
|
||||
BmsCustomerOption selectAccessibleCustomer(@Param("customerId") Long customerId, @Param("scope") ArBillRowScope scope);
|
||||
|
||||
List<BmsCustomerOption> selectCustomerOptions(@Param("keywords") List<String> keywords,
|
||||
@Param("scope") ArBillRowScope scope);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.bms.ar.security;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/** 应收业务范围,与角色管理的 dataScope 配置无关。 */
|
||||
public final class ArBillRoleKeys {
|
||||
public static final String SALES = "ORA_6041";
|
||||
public static final String BUSINESS_SPECIALIST = "ORA_6163";
|
||||
public static final String SALES_MANAGER = "ORA_6040";
|
||||
/** 销售员、商务专员:本人负责客户的账单。 */
|
||||
public static final Set<String> SELF_DATA_ROLES = Set.of(SALES, BUSINESS_SPECIALIST);
|
||||
/** 财务经理、应收财务;仍须具备对应接口的功能权限。 */
|
||||
public static final Set<String> ALL_DATA_ROLES = Set.of("ORA_6042", "ORA_6043");
|
||||
|
||||
static {
|
||||
if (SELF_DATA_ROLES.contains(SALES_MANAGER) || ALL_DATA_ROLES.contains(SALES_MANAGER)
|
||||
|| SELF_DATA_ROLES.stream().anyMatch(ALL_DATA_ROLES::contains)) {
|
||||
throw new IllegalStateException("应收数据权限角色映射冲突");
|
||||
}
|
||||
}
|
||||
|
||||
private ArBillRoleKeys() {}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.bms.ar.security;
|
||||
|
||||
/** 仅由后端构造,不能绑定到 HTTP 查询对象。 */
|
||||
public final class ArBillRowScope {
|
||||
private final boolean all;
|
||||
private final boolean self;
|
||||
private final boolean department;
|
||||
private final Long userId;
|
||||
private final Long deptId;
|
||||
|
||||
ArBillRowScope(boolean all, boolean self, boolean department, Long userId, Long deptId) {
|
||||
this.all = all;
|
||||
this.self = self && userId != null && userId > 0;
|
||||
this.department = department && deptId != null && deptId > 0;
|
||||
this.userId = userId;
|
||||
this.deptId = deptId;
|
||||
}
|
||||
|
||||
public boolean isAll() { return all; }
|
||||
public boolean isSelf() { return self; }
|
||||
public boolean isDepartment() { return department; }
|
||||
public Long getUserId() { return userId; }
|
||||
public Long getDeptId() { return deptId; }
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.bms.ar.security;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.bms.ar.mapper.ArBillScopeMapper;
|
||||
import com.bms.common.constant.Constants;
|
||||
import com.bms.common.core.domain.entity.SysRole;
|
||||
import com.bms.common.core.domain.entity.SysUser;
|
||||
import com.bms.common.core.domain.model.LoginUser;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
import com.bms.common.utils.PermissionContext;
|
||||
import com.bms.common.utils.SecurityUtils;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerOption;
|
||||
|
||||
@Component
|
||||
public class ArBillScopeResolver {
|
||||
private final ArBillScopeMapper mapper;
|
||||
|
||||
public ArBillScopeResolver(ArBillScopeMapper mapper) {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
/** 只读取登录态;功能上下文由 @ss.hasPermi 写入,缺失时不授权。 */
|
||||
public ArBillRowScope resolve() {
|
||||
return resolve(SecurityUtils.getLoginUser(), PermissionContext.get());
|
||||
}
|
||||
|
||||
public ArBillRowScope resolve(LoginUser actor, String permission) {
|
||||
if (actor == null || actor.getUser() == null || actor.getUserId() == null) {
|
||||
throw new ServiceException("登录状态无效,请重新登录", 401);
|
||||
}
|
||||
SysUser user = actor.getUser();
|
||||
if (!"0".equals(user.getStatus()) || !"0".equals(user.getDelFlag())) {
|
||||
return new ArBillRowScope(false, false, false, actor.getUserId(), actor.getDeptId());
|
||||
}
|
||||
if (user.isAdmin()) {
|
||||
return new ArBillRowScope(true, false, false, actor.getUserId(), actor.getDeptId());
|
||||
}
|
||||
boolean all = false;
|
||||
boolean self = false;
|
||||
boolean department = false;
|
||||
if (permission != null && !permission.isBlank() && user.getRoles() != null) {
|
||||
for (SysRole role : user.getRoles()) {
|
||||
if (role == null || !"0".equals(role.getStatus()) || !permits(role.getPermissions(), permission)) continue;
|
||||
String key = role.getRoleKey();
|
||||
all |= ArBillRoleKeys.ALL_DATA_ROLES.contains(key == null ? "" : key);
|
||||
self |= ArBillRoleKeys.SELF_DATA_ROLES.contains(key == null ? "" : key);
|
||||
department |= ArBillRoleKeys.SALES_MANAGER.equals(key);
|
||||
}
|
||||
}
|
||||
return new ArBillRowScope(all, self, department, actor.getUserId(), actor.getDeptId());
|
||||
}
|
||||
|
||||
private boolean permits(Set<String> permissions, String required) {
|
||||
return permissions != null && (permissions.contains(Constants.ALL_PERMISSION)
|
||||
|| Arrays.stream(required.split(Constants.PERMISSION_DELIMITER)).map(String::trim).anyMatch(permissions::contains));
|
||||
}
|
||||
|
||||
public void requireBillAccessible(Long arBillId) {
|
||||
ArBillRowScope scope = resolve();
|
||||
if (arBillId == null || withoutPagination(() -> mapper.countAccessibleBill(arBillId, scope)) == 0) {
|
||||
throw new ServiceException("账单不存在或无权访问");
|
||||
}
|
||||
}
|
||||
|
||||
public BmsCustomerOption requireCustomerAccessible(Long customerId) {
|
||||
ArBillRowScope scope = resolve();
|
||||
BmsCustomerOption customer = customerId == null ? null
|
||||
: withoutPagination(() -> mapper.selectAccessibleCustomer(customerId, scope));
|
||||
if (customer == null) throw new ServiceException("客户不存在或无权访问");
|
||||
return customer;
|
||||
}
|
||||
|
||||
public List<BmsCustomerOption> selectCustomerOptions(String keywords) {
|
||||
List<String> values = keywords == null ? List.of()
|
||||
: Arrays.stream(keywords.split("\\R")).map(String::trim).filter(s -> !s.isEmpty()).distinct().toList();
|
||||
return mapper.selectCustomerOptions(values, resolve());
|
||||
}
|
||||
|
||||
public void requireCustomerSourceBills(Long customerId, List<String> sourceIds) {
|
||||
if (!sourceIds.isEmpty() && withoutPagination(() -> mapper.countCustomerSourceBills(customerId, sourceIds)) != sourceIds.size()) {
|
||||
throw new ServiceException("所选订单不存在或不属于当前客户");
|
||||
}
|
||||
}
|
||||
|
||||
/** 资源预校验不能消耗 Controller 已设置的分页,最终业务查询仍使用原分页对象。 */
|
||||
private <T> T withoutPagination(Supplier<T> query) {
|
||||
Page<?> page = PageHelper.getLocalPage();
|
||||
PageHelper.clearPage();
|
||||
try {
|
||||
return query.get();
|
||||
} finally {
|
||||
if (page != null) PageHelper.setLocalPage(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -23,6 +23,7 @@ import com.bms.ar.enums.ArLogOperationType;
|
||||
import com.bms.ar.enums.ArLogResult;
|
||||
import com.bms.ar.mapper.ArBalanceWriteoffMapper;
|
||||
import com.bms.ar.mapper.ArBillMapper;
|
||||
import com.bms.ar.security.ArBillScopeResolver;
|
||||
import com.bms.ar.service.IArBalanceWriteoffService;
|
||||
import com.bms.ar.service.IArBillLogService;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
@@ -46,6 +47,8 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService {
|
||||
|
||||
@Autowired
|
||||
private ArBillMapper billMapper;
|
||||
@Autowired
|
||||
private ArBillScopeResolver billScope;
|
||||
|
||||
@Autowired
|
||||
private ArBalanceWriteoffMapper writeoffMapper;
|
||||
@@ -67,7 +70,7 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService {
|
||||
if (customerId == null) {
|
||||
return List.of();
|
||||
}
|
||||
return billMapper.selectUnsettledByCustomer(customerId);
|
||||
return billMapper.selectUnsettledByCustomer(customerId, billScope.resolve());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,7 +78,7 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService {
|
||||
*/
|
||||
@Override
|
||||
public List<ArBalanceWriteoff> selectWriteoffList(ArBalanceWriteoff query) {
|
||||
return writeoffMapper.selectWriteoffList(query);
|
||||
return writeoffMapper.selectWriteoffList(query, billScope.resolve());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,6 +106,7 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService {
|
||||
if (bill == null) {
|
||||
throw new ServiceException("应收账单不存在");
|
||||
}
|
||||
billScope.requireBillAccessible(bill.getArBillId());
|
||||
ArBillStatus status = ArBillStatus.of(bill.getStatus());
|
||||
if (status == null || !status.canWriteOff()) {
|
||||
throw new ServiceException("当前账单状态不允许核销:" + (status == null ? bill.getStatus() : status.getLabel()));
|
||||
@@ -170,6 +174,7 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService {
|
||||
if (writeoff == null) {
|
||||
throw new ServiceException("核销记录不存在");
|
||||
}
|
||||
billScope.requireBillAccessible(writeoff.getArBillId());
|
||||
if (!ArBalanceWriteoffStatus.EFFECTIVE.getCode().equals(writeoff.getStatus())) {
|
||||
throw new ServiceException("只有有效状态的核销记录可以反核销");
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.bms.ar.enums.ArLogOperationSource;
|
||||
import com.bms.ar.enums.ArLogOperationType;
|
||||
import com.bms.ar.enums.ArLogResult;
|
||||
import com.bms.ar.mapper.ArBillLogMapper;
|
||||
import com.bms.ar.security.ArBillScopeResolver;
|
||||
import com.bms.ar.service.IArBillLogService;
|
||||
import com.bms.common.utils.uuid.IdUtils;
|
||||
|
||||
@@ -19,10 +20,12 @@ import com.bms.common.utils.uuid.IdUtils;
|
||||
public class ArBillLogServiceImpl implements IArBillLogService {
|
||||
@Autowired
|
||||
private ArBillLogMapper logMapper;
|
||||
@Autowired
|
||||
private ArBillScopeResolver billScope;
|
||||
|
||||
@Override
|
||||
public List<ArBillLog> selectLogList(ArBillLog query) {
|
||||
return logMapper.selectList(query);
|
||||
return logMapper.selectList(query, billScope.resolve());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -70,6 +70,8 @@ import com.bms.ar.mapper.ArBillDetailMapper;
|
||||
import com.bms.ar.mapper.ArBillExportMapper;
|
||||
import com.bms.ar.mapper.ArBillLogMapper;
|
||||
import com.bms.ar.mapper.ArBillMapper;
|
||||
import com.bms.ar.security.ArBillScopeResolver;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerOption;
|
||||
import com.bms.ar.mapper.ArBillGenerateTaskMapper;
|
||||
import com.bms.ar.mapper.ArBankAccountMapper;
|
||||
import com.bms.ar.mapper.ArExportTemplateMapper;
|
||||
@@ -182,6 +184,8 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
@Autowired
|
||||
private ArBillMapper billMapper;
|
||||
@Autowired
|
||||
private ArBillScopeResolver billScope;
|
||||
@Autowired
|
||||
private ArBillGenerateTaskMapper generateTaskMapper;
|
||||
@Autowired
|
||||
private ArBankAccountMapper bankAccountMapper;
|
||||
@@ -214,7 +218,8 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
@Override
|
||||
public List<ArBill> selectArBillList(ArBillQuery query) {
|
||||
long startedAt = System.nanoTime();
|
||||
List<ArBill> bills = billMapper.selectArBillList(query);
|
||||
query = query == null ? new ArBillQuery() : query;
|
||||
List<ArBill> bills = billMapper.selectArBillList(query, billScope.resolve());
|
||||
for (ArBill bill : bills) {
|
||||
bill.setBankAccountNo(maskAccount(bill.getBankAccountNo()));
|
||||
}
|
||||
@@ -229,7 +234,7 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
*/
|
||||
@Override
|
||||
public ArBillSummary selectArBillSummary(ArBillQuery query) {
|
||||
return billMapper.selectArBillSummary(query);
|
||||
return billMapper.selectArBillSummary(query == null ? new ArBillQuery() : query, billScope.resolve());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,9 +243,9 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
@Override
|
||||
public ArBill selectArBillById(Long arBillId) {
|
||||
long startedAt = System.nanoTime();
|
||||
ArBill bill = billMapper.selectArBillById(arBillId);
|
||||
ArBill bill = billMapper.selectArBillByIdScoped(arBillId, billScope.resolve());
|
||||
if (bill == null) {
|
||||
throw new ServiceException("应收账单不存在");
|
||||
throw new ServiceException("账单不存在或无权访问");
|
||||
}
|
||||
bill.setBankAccountNo(maskAccount(bill.getBankAccountNo()));
|
||||
bill.setExports(exportMapper.selectByBillId(arBillId));
|
||||
@@ -256,7 +261,7 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
@Override
|
||||
public List<ArBillDetail> selectArBillDetails(Long arBillId) {
|
||||
long startedAt = System.nanoTime();
|
||||
List<ArBillDetail> details = detailMapper.selectByBillId(arBillId);
|
||||
List<ArBillDetail> details = detailMapper.selectByBillIdScoped(arBillId, billScope.resolve());
|
||||
log.debug("应收账单明细查询完成,arBillId={}, resultCount={}, costMs={}",
|
||||
arBillId, details.size(), elapsedMillis(startedAt));
|
||||
return details;
|
||||
@@ -268,7 +273,7 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
@Override
|
||||
public List<ArBillDetailFee> selectArBillDetailFees(Long arBillId, Long detailId) {
|
||||
long startedAt = System.nanoTime();
|
||||
List<ArBillDetailFee> fees = detailFeeMapper.selectByBillIdAndDetailId(arBillId, detailId);
|
||||
List<ArBillDetailFee> fees = detailFeeMapper.selectByBillIdAndDetailIdScoped(arBillId, detailId, billScope.resolve());
|
||||
log.debug("应收账单费用明细查询完成,arBillId={}, detailId={}, resultCount={}, costMs={}",
|
||||
arBillId, detailId, fees.size(), elapsedMillis(startedAt));
|
||||
return fees;
|
||||
@@ -308,6 +313,8 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
public List<PrcOrderBill> previewBill(ArBillGenerateQuery query) {
|
||||
long startedAt = System.nanoTime();
|
||||
validatePreviewQuery(query);
|
||||
BmsCustomerOption customer = billScope.requireCustomerAccessible(query.getCustomerId());
|
||||
query.setCustomerCode(customer.getCustomerCode());
|
||||
List<String> orderIdentifiers = normalizeOrderIdentifiers(query.getOrderOrTrackingNo());
|
||||
List<String> excludeOrderIdentifiers = normalizeOrderIdentifiers(query.getExcludeOrderOrTrackingNo());
|
||||
List<PrcOrderBill> result = sourceBillMapper.selectPrcOrderBillByCustomerPeriod(query.getCustomerId(),
|
||||
@@ -336,6 +343,8 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
public BigDecimal sumPreviewBill(ArBillGenerateQuery query) {
|
||||
long startedAt = System.nanoTime();
|
||||
validatePreviewQuery(query);
|
||||
BmsCustomerOption customer = billScope.requireCustomerAccessible(query.getCustomerId());
|
||||
query.setCustomerCode(customer.getCustomerCode());
|
||||
List<String> orderIdentifiers = normalizeOrderIdentifiers(query.getOrderOrTrackingNo());
|
||||
List<String> excludeOrderIdentifiers = normalizeOrderIdentifiers(query.getExcludeOrderOrTrackingNo());
|
||||
BigDecimal total = nvl(sourceBillMapper.sumPrcOrderBillIncomeByCustomerPeriod(query.getCustomerId(),
|
||||
@@ -359,8 +368,12 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
long startedAt = System.nanoTime();
|
||||
Long createUserId = SecurityUtils.getUserId();
|
||||
validatePeriod(request);
|
||||
BmsCustomerOption customer = billScope.requireCustomerAccessible(request.getCustomerId());
|
||||
request.setCustomerCode(customer.getCustomerCode());
|
||||
request.setCustomerName(customer.getCustomerName());
|
||||
ArBillGenerateTask existed = generateTaskMapper.selectByRequestToken(requestId);
|
||||
if (existed != null) {
|
||||
billScope.requireBillAccessible(existed.getArBillId());
|
||||
log.info("应收账单生成请求命中幂等记录,operator={}, requestId={}, taskId={}, status={}, costMs={}",
|
||||
safeLogValue(operator), safeLogValue(requestId), existed.getTaskId(), existed.getStatus(),
|
||||
elapsedMillis(startedAt));
|
||||
@@ -401,6 +414,7 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
validateOrderIdentifierParameterCount(
|
||||
orderIdentifierCount,
|
||||
allMatched ? excludedSourceBillIds.size() : sourceBillIds.size());
|
||||
billScope.requireCustomerSourceBills(request.getCustomerId(), allMatched ? excludedSourceBillIds : sourceBillIds);
|
||||
request.setSelectionMode(selectionMode);
|
||||
|
||||
String currency = StringUtils.isEmpty(request.getCurrency()) ? "CNY" : request.getCurrency().trim();
|
||||
@@ -478,8 +492,9 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
long startedAt = System.nanoTime();
|
||||
ArBillGenerateTask task = generateTaskMapper.selectById(taskId);
|
||||
if (task == null) {
|
||||
throw new ServiceException("账单生成任务不存在");
|
||||
throw new ServiceException("账单不存在或无权访问");
|
||||
}
|
||||
billScope.requireBillAccessible(task.getArBillId());
|
||||
log.debug("应收账单生成任务状态查询,taskId={}, status={}, processedCount={}, totalCount={}, costMs={}",
|
||||
taskId, task.getStatus(), task.getProcessedCount(), task.getTotalCount(), elapsedMillis(startedAt));
|
||||
return task;
|
||||
@@ -491,7 +506,7 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
@Override
|
||||
public List<ArBillGenerateTask> selectActiveGenerateTasks(String operator) {
|
||||
long startedAt = System.nanoTime();
|
||||
List<ArBillGenerateTask> tasks = generateTaskMapper.selectActiveByCreateBy(operator);
|
||||
List<ArBillGenerateTask> tasks = generateTaskMapper.selectActiveByCreateByScoped(operator, billScope.resolve());
|
||||
log.debug("当前用户应收账单生成任务查询,operator={}, taskCount={}, costMs={}",
|
||||
safeLogValue(operator), tasks.size(), elapsedMillis(startedAt));
|
||||
return tasks;
|
||||
@@ -504,7 +519,9 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ArBillGenerateTask cancelGenerateTask(Long taskId, String operator, String requestId) {
|
||||
long startedAt = System.nanoTime();
|
||||
ArBillGenerateTask task = selectGenerateTask(taskId);
|
||||
ArBillGenerateTask task = generateTaskMapper.selectById(taskId);
|
||||
if (task == null) throw new ServiceException("账单不存在或无权访问");
|
||||
billScope.requireBillAccessible(task.getArBillId());
|
||||
if (!operator.equals(task.getCreateBy())) {
|
||||
throw new ServiceException("只能取消自己创建的账单生成任务");
|
||||
}
|
||||
@@ -533,7 +550,9 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ArBillGenerateTask retryGenerateTask(Long taskId, String operator, String requestId) {
|
||||
long startedAt = System.nanoTime();
|
||||
ArBillGenerateTask task = selectGenerateTask(taskId);
|
||||
ArBillGenerateTask task = generateTaskMapper.selectById(taskId);
|
||||
if (task == null) throw new ServiceException("账单不存在或无权访问");
|
||||
billScope.requireBillAccessible(task.getArBillId());
|
||||
if (!operator.equals(task.getCreateBy())) {
|
||||
throw new ServiceException("只能重新生成自己创建的账单任务");
|
||||
}
|
||||
@@ -923,6 +942,7 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
public ArBill confirmBill(Long arBillId, ArBillActionRequest request, String operator, String requestId) {
|
||||
long startedAt = System.nanoTime();
|
||||
ArBill bill = billMapper.selectArBillByIdForUpdate(arBillId);
|
||||
billScope.requireBillAccessible(arBillId);
|
||||
if (bill == null) {
|
||||
throw failure(arBillId, ArLogObjectType.BILL, String.valueOf(arBillId), ArLogOperationType.CONFIRM,
|
||||
null, "应收账单不存在", operator, requestId);
|
||||
@@ -972,6 +992,7 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
null, "作废必须填写原因", operator, requestId);
|
||||
}
|
||||
ArBill bill = billMapper.selectArBillByIdForUpdate(arBillId);
|
||||
billScope.requireBillAccessible(arBillId);
|
||||
if (bill == null) {
|
||||
throw failure(arBillId, ArLogObjectType.BILL, String.valueOf(arBillId), ArLogOperationType.VOID,
|
||||
null, "应收账单不存在", operator, requestId);
|
||||
@@ -1011,11 +1032,13 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
public ArBillExport exportBill(Long arBillId, String operator, String requestId) {
|
||||
long startedAt = System.nanoTime();
|
||||
Long exportUserId = SecurityUtils.getUserId();
|
||||
billScope.requireBillAccessible(arBillId);
|
||||
ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, ArLogObjectType.EXPORT.getCode(),
|
||||
ArLogResult.SUCCESS.getCode());
|
||||
if (previous != null && isNumber(previous.getObjectId())) {
|
||||
ArBillExport previousExport = exportMapper.selectById(Long.valueOf(previous.getObjectId()));
|
||||
if (previousExport != null) {
|
||||
billScope.requireBillAccessible(previousExport.getArBillId());
|
||||
log.info("应收账单导出请求命中幂等记录,operator={}, requestId={}, arBillId={}, exportId={}, status={}, costMs={}",
|
||||
safeLogValue(operator), safeLogValue(requestId), arBillId, previousExport.getExportId(),
|
||||
previousExport.getExportStatus(), elapsedMillis(startedAt));
|
||||
@@ -1084,9 +1107,8 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
public ArBillExport selectExport(Long exportId) {
|
||||
long startedAt = System.nanoTime();
|
||||
ArBillExport export = exportMapper.selectById(exportId);
|
||||
if (export == null) {
|
||||
throw new ServiceException("导出任务不存在");
|
||||
}
|
||||
if (export == null) throw new ServiceException("账单不存在或无权访问");
|
||||
billScope.requireBillAccessible(export.getArBillId());
|
||||
log.debug("应收账单导出任务状态查询,exportId={}, status={}, costMs={}",
|
||||
exportId, export.getExportStatus(), elapsedMillis(startedAt));
|
||||
return export;
|
||||
@@ -1167,6 +1189,7 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
@Override
|
||||
public List<ArBillExport> selectExports(Long arBillId) {
|
||||
long startedAt = System.nanoTime();
|
||||
billScope.requireBillAccessible(arBillId);
|
||||
List<ArBillExport> exports = exportMapper.selectByBillId(arBillId);
|
||||
log.debug("应收账单导出记录查询,arBillId={}, exportCount={}, costMs={}",
|
||||
arBillId, exports.size(), elapsedMillis(startedAt));
|
||||
@@ -1177,7 +1200,11 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
public void downloadExport(Long exportId, HttpServletResponse response) throws Exception {
|
||||
long startedAt = System.nanoTime();
|
||||
ArBillExport export = exportMapper.selectById(exportId);
|
||||
if (export == null || !ArExportStatus.SUCCESS.getCode().equals(export.getExportStatus()) || StringUtils.isEmpty(export.getStorageKey())) {
|
||||
if (export == null) {
|
||||
throw new ServiceException("账单不存在或无权访问");
|
||||
}
|
||||
billScope.requireBillAccessible(export.getArBillId());
|
||||
if (!ArExportStatus.SUCCESS.getCode().equals(export.getExportStatus()) || StringUtils.isEmpty(export.getStorageKey())) {
|
||||
throw new ServiceException("导出文件不存在或尚未生成成功");
|
||||
}
|
||||
String resource = export.getStorageKey();
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.bms.customer.customerinfo.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 客户下拉选项。
|
||||
* 只携带下拉展示和回填需要的字段,不返回额度、账期等客户档案内容。
|
||||
*
|
||||
* @author bms
|
||||
*/
|
||||
@Data
|
||||
public class BmsCustomerOption
|
||||
{
|
||||
/** 客户ID。收款、账单等已有下拉按主键选中,需一并返回 */
|
||||
private Long customerId;
|
||||
|
||||
/** 客户编码 */
|
||||
private String customerCode;
|
||||
|
||||
/** 客户名称 */
|
||||
private String customerName;
|
||||
}
|
||||
+15
-4
@@ -1,10 +1,9 @@
|
||||
package com.bms.customer.customerinfo.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomer;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerAttachment;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerCompany;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerContact;
|
||||
|
||||
import com.bms.customer.customerinfo.domain.*;
|
||||
import com.bms.customer.customerinfo.vo.UnlinkedSalesCustomerVO;
|
||||
import io.lettuce.core.dynamic.annotation.Param;
|
||||
|
||||
/**
|
||||
@@ -34,6 +33,18 @@ public interface BmsCustomerMapper
|
||||
*/
|
||||
public List<BmsCustomer> selectBmsCustomerList(BmsCustomer bmsCustomer);
|
||||
|
||||
/**
|
||||
* 查询客户下拉选项。
|
||||
* 按客户名称、客户编码、对外代码模糊匹配,只返回下拉所需字段。
|
||||
*
|
||||
* @param query 下拉查询条件
|
||||
* @return 客户下拉选项
|
||||
*/
|
||||
public List<BmsCustomerOption> selectCustomerOptions(BmsCustomer query);
|
||||
|
||||
/** 独立分页查询未关联业务员的客户,仅按 sales_user_id 为空筛选。 */
|
||||
public List<UnlinkedSalesCustomerVO> selectUnlinkedSalesUserCustomerList(BmsCustomer query);
|
||||
|
||||
/** 校验业务员用户ID仍对应未删除的系统用户。 */
|
||||
public int countValidSalesUserId(@org.apache.ibatis.annotations.Param("salesUserId") String salesUserId);
|
||||
|
||||
|
||||
+14
@@ -2,10 +2,12 @@ package com.bms.customer.customerinfo.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomer;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerOption;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerAttachment;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerCompany;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerContact;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerLog;
|
||||
import com.bms.customer.customerinfo.vo.UnlinkedSalesCustomerVO;
|
||||
|
||||
/**
|
||||
* 客户主Service接口
|
||||
@@ -39,6 +41,18 @@ public interface IBmsCustomerService
|
||||
*/
|
||||
public List<BmsCustomer> selectBmsCustomerList(BmsCustomer bmsCustomer);
|
||||
|
||||
/** 独立查询未关联业务员的客户。 */
|
||||
public List<UnlinkedSalesCustomerVO> selectUnlinkedSalesUserCustomerList(BmsCustomer query);
|
||||
|
||||
/**
|
||||
* 查询客户下拉选项。
|
||||
* 只返回客户ID、客户编码、客户名称;keywords 按客户名称、客户编码、对外代码模糊查询。
|
||||
*
|
||||
* @param query 下拉查询条件
|
||||
* @return 客户下拉选项
|
||||
*/
|
||||
public List<BmsCustomerOption> selectCustomerOptions(BmsCustomer query);
|
||||
|
||||
/**
|
||||
* 查询客户附件列表
|
||||
*
|
||||
|
||||
+9
-16
@@ -73,14 +73,6 @@ public class BmsCustomerPriceSetServiceImpl implements IBmsCustomerPriceSetServi
|
||||
{
|
||||
throw new ServiceException("仅生效中的套餐允许绑定,当前套餐状态不可用");
|
||||
}
|
||||
if (bind.getPrBegindate() == null)
|
||||
{
|
||||
throw new ServiceException("绑定生效日期不能为空");
|
||||
}
|
||||
if (bind.getPrEnddate() != null && bind.getPrBegindate().after(bind.getPrEnddate()))
|
||||
{
|
||||
throw new ServiceException("失效日期不能早于生效日期");
|
||||
}
|
||||
if (bind.getBindPriority() == null || bind.getBindPriority() < 1)
|
||||
{
|
||||
bind.setBindPriority(1);
|
||||
@@ -105,6 +97,15 @@ public class BmsCustomerPriceSetServiceImpl implements IBmsCustomerPriceSetServi
|
||||
if (existing != null)
|
||||
{
|
||||
bind.setCustomerPricesetId(existing.getCustomerPricesetId());
|
||||
// 弹框已不再采集生效/失效日期,未传入时保留库中原值
|
||||
if (bind.getPrBegindate() == null)
|
||||
{
|
||||
bind.setPrBegindate(existing.getPrBegindate());
|
||||
}
|
||||
if (bind.getPrEnddate() == null)
|
||||
{
|
||||
bind.setPrEnddate(existing.getPrEnddate());
|
||||
}
|
||||
rows = bindMapper.updateBind(bind);
|
||||
}
|
||||
else
|
||||
@@ -144,14 +145,6 @@ public class BmsCustomerPriceSetServiceImpl implements IBmsCustomerPriceSetServi
|
||||
{
|
||||
throw new ServiceException("仅生效中的套餐允许绑定,当前套餐状态不可用");
|
||||
}
|
||||
if (req.getPrBegindate() == null)
|
||||
{
|
||||
throw new ServiceException("绑定生效日期不能为空");
|
||||
}
|
||||
if (req.getPrEnddate() != null && req.getPrBegindate().after(req.getPrEnddate()))
|
||||
{
|
||||
throw new ServiceException("失效日期不能早于生效日期");
|
||||
}
|
||||
Integer priority = (req.getBindPriority() == null || req.getBindPriority() < 1) ? 1 : req.getBindPriority();
|
||||
boolean overwrite = Boolean.TRUE.equals(req.getOverwriteExisting());
|
||||
|
||||
|
||||
+20
@@ -17,7 +17,9 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.bms.customer.customerinfo.mapper.BmsCustomerMapper;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomer;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerOption;
|
||||
import com.bms.customer.customerinfo.domain.BmsCustomerLog;
|
||||
import com.bms.customer.customerinfo.vo.UnlinkedSalesCustomerVO;
|
||||
import com.bms.customer.customerinfo.service.IBmsCustomerLogService;
|
||||
import com.bms.customer.customerinfo.service.IBmsCustomerService;
|
||||
import com.bms.customer.customermoney.service.ICustomerMoneyService;
|
||||
@@ -89,6 +91,24 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
|
||||
return bmsCustomerMapper.selectBmsCustomerList(bmsCustomer);
|
||||
}
|
||||
|
||||
/** 查询未关联业务员客户,保留客户业务员维度的数据范围。 */
|
||||
@Override
|
||||
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id")
|
||||
public List<UnlinkedSalesCustomerVO> selectUnlinkedSalesUserCustomerList(BmsCustomer query)
|
||||
{
|
||||
return bmsCustomerMapper.selectUnlinkedSalesUserCustomerList(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户下拉选项。数据权限与客户列表一致,只查下拉需要的字段。
|
||||
*/
|
||||
@Override
|
||||
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id")
|
||||
public List<BmsCustomerOption> selectCustomerOptions(BmsCustomer query)
|
||||
{
|
||||
return bmsCustomerMapper.selectCustomerOptions(query);
|
||||
}
|
||||
|
||||
//查询附件子表数据
|
||||
@Override
|
||||
public List<BmsCustomerAttachment> selectBmsCustomerAttachmentList(BmsCustomerAttachment bmsCustomerAttachment) {
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.bms.customer.customerinfo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/** 未关联业务员客户弹窗专用列表响应。 */
|
||||
@Data
|
||||
public class UnlinkedSalesCustomerVO
|
||||
{
|
||||
private Long customerId;
|
||||
private String customerCode;
|
||||
private String customerName;
|
||||
private String customerCodeDw;
|
||||
private String customerType;
|
||||
private String isEnable;
|
||||
private String sourceSalesCode;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.bms.masterdata.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.bms.common.annotation.Excel;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
@@ -17,53 +18,98 @@ public class ServiceChannel
|
||||
private String id;
|
||||
|
||||
/** 渠道编码 */
|
||||
@Excel(name = "渠道编码")
|
||||
private String channelCode;
|
||||
|
||||
/** 渠道名称 */
|
||||
@Excel(name = "渠道名称")
|
||||
private String channelName;
|
||||
|
||||
/** 所属服务商编码 */
|
||||
@Excel(name = "服务商编码")
|
||||
private String serviceCode;
|
||||
|
||||
/** 所属服务商名称 */
|
||||
@Excel(name = "服务商名称")
|
||||
private String serviceName;
|
||||
|
||||
/** 是否启用(1启用 0停用) */
|
||||
@Excel(name = "是否启用", readConverterExp = "1=启用,0=停用")
|
||||
private String blOpen;
|
||||
|
||||
/** 是否支持多票(1是 0否) */
|
||||
@Excel(name = "是否多票", readConverterExp = "1=是,0=否")
|
||||
private String blMultiple;
|
||||
|
||||
@Excel(name = "货物类型")
|
||||
private String cargoType;
|
||||
|
||||
@Excel(name = "付款方式")
|
||||
private String payType;
|
||||
|
||||
@Excel(name = "发货人名称")
|
||||
private String shipperName;
|
||||
|
||||
@Excel(name = "发货人ID")
|
||||
private String shipperId;
|
||||
|
||||
@Excel(name = "轨迹查询类型")
|
||||
private String queryType;
|
||||
|
||||
@Excel(name = "轨迹编码")
|
||||
private String trajectoryCode;
|
||||
|
||||
@Excel(name = "末端服务商")
|
||||
private String finalServiceName;
|
||||
|
||||
@Excel(name = "目的港/目的地区域")
|
||||
private String desPort;
|
||||
|
||||
/** 重量类型 */
|
||||
@Excel(name = "重量类型")
|
||||
private String weightType;
|
||||
|
||||
/** 体积除数(抛货系数) */
|
||||
@Excel(name = "抛货系数")
|
||||
private Long dimDivisor;
|
||||
|
||||
@Excel(name = "计重规则")
|
||||
private String weightRule;
|
||||
|
||||
@Excel(name = "发货类型")
|
||||
private String shipmentType;
|
||||
|
||||
@Excel(name = "数据来源")
|
||||
private String dataFrom;
|
||||
|
||||
@Excel(name = "备注")
|
||||
private String remarks;
|
||||
|
||||
@Excel(name = "创建人")
|
||||
private String createName;
|
||||
|
||||
private String createManCode;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Excel(name = "创建时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createDate;
|
||||
|
||||
@Excel(name = "修改人")
|
||||
private String modifier;
|
||||
|
||||
private String modifierCode;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Excel(name = "修改时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date modifyDate;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date gmtCreate;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date gmtModified;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Excel(name = "同步时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date syncTime;
|
||||
}
|
||||
|
||||
+43
-2
@@ -4,7 +4,7 @@ import java.util.List;
|
||||
import com.bms.masterdata.domain.ServiceChannel;
|
||||
|
||||
/**
|
||||
* 服务渠道镜像表查询(MASTER 数据源)
|
||||
* 服务渠道 srv_service_channel
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
@@ -12,7 +12,48 @@ import com.bms.masterdata.domain.ServiceChannel;
|
||||
public interface ServiceChannelMapper
|
||||
{
|
||||
/**
|
||||
* 查询服务渠道列表(支持渠道编码/名称/服务商编码/启用状态过滤,非分页)
|
||||
* 查询服务渠道列表。
|
||||
* 渠道名称、服务商名称、目的港/目的地区域、发货人名称为模糊匹配;编码同样按包含匹配,便于列表检索。
|
||||
*/
|
||||
List<ServiceChannel> selectServiceChannelList(ServiceChannel query);
|
||||
|
||||
/**
|
||||
* 按主键查询渠道
|
||||
*/
|
||||
ServiceChannel selectServiceChannelById(String id);
|
||||
|
||||
/**
|
||||
* 按渠道编码精确查询(唯一性校验)
|
||||
*/
|
||||
ServiceChannel selectServiceChannelByCode(String channelCode);
|
||||
|
||||
/**
|
||||
* 产品渠道关系中引用该渠道编码的数量
|
||||
*/
|
||||
int countProductChannelByCode(String channelCode);
|
||||
|
||||
/**
|
||||
* 供应商绑定中引用该渠道编码的数量
|
||||
*/
|
||||
int countSupplierByChannelCode(String channelCode);
|
||||
|
||||
/**
|
||||
* 新增渠道
|
||||
*/
|
||||
int insertServiceChannel(ServiceChannel channel);
|
||||
|
||||
/**
|
||||
* 修改渠道
|
||||
*/
|
||||
int updateServiceChannel(ServiceChannel channel);
|
||||
|
||||
/**
|
||||
* 只更新启用状态
|
||||
*/
|
||||
int updateServiceChannelStatus(ServiceChannel channel);
|
||||
|
||||
/**
|
||||
* 批量删除渠道
|
||||
*/
|
||||
int deleteServiceChannelByIds(String[] ids);
|
||||
}
|
||||
|
||||
+27
-2
@@ -4,7 +4,7 @@ import java.util.List;
|
||||
import com.bms.masterdata.domain.ServiceChannel;
|
||||
|
||||
/**
|
||||
* 服务渠道镜像 服务层
|
||||
* 服务渠道 服务层
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
@@ -12,7 +12,32 @@ import com.bms.masterdata.domain.ServiceChannel;
|
||||
public interface IServiceChannelService
|
||||
{
|
||||
/**
|
||||
* 查询服务渠道列表(非分页,供供应商绑定渠道等下拉选择场景)
|
||||
* 查询服务渠道列表(分页由调用方 startPage 控制;下拉场景不传分页)
|
||||
*/
|
||||
List<ServiceChannel> selectServiceChannelList(ServiceChannel query);
|
||||
|
||||
/**
|
||||
* 查询渠道详情
|
||||
*/
|
||||
ServiceChannel selectServiceChannelById(String id);
|
||||
|
||||
/**
|
||||
* 新增渠道
|
||||
*/
|
||||
int insertServiceChannel(ServiceChannel channel);
|
||||
|
||||
/**
|
||||
* 修改渠道
|
||||
*/
|
||||
int updateServiceChannel(ServiceChannel channel);
|
||||
|
||||
/**
|
||||
* 启用或停用渠道(只改 bl_open)
|
||||
*/
|
||||
int updateServiceChannelStatus(ServiceChannel channel);
|
||||
|
||||
/**
|
||||
* 批量删除渠道。已被产品渠道或供应商引用时拒绝删除。
|
||||
*/
|
||||
int deleteServiceChannelByIds(String[] ids);
|
||||
}
|
||||
|
||||
+169
-1
@@ -3,12 +3,15 @@ package com.bms.masterdata.service.impl;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.common.utils.uuid.IdUtils;
|
||||
import com.bms.masterdata.domain.ServiceChannel;
|
||||
import com.bms.masterdata.mapper.ServiceChannelMapper;
|
||||
import com.bms.masterdata.service.IServiceChannelService;
|
||||
|
||||
/**
|
||||
* 服务渠道镜像 服务实现
|
||||
* 服务渠道 服务实现
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
@@ -24,4 +27,169 @@ public class ServiceChannelServiceImpl implements IServiceChannelService
|
||||
{
|
||||
return serviceChannelMapper.selectServiceChannelList(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceChannel selectServiceChannelById(String id)
|
||||
{
|
||||
return serviceChannelMapper.selectServiceChannelById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertServiceChannel(ServiceChannel channel)
|
||||
{
|
||||
prepare(channel, true);
|
||||
assertCodeUnique(channel);
|
||||
channel.setId(IdUtils.fastSimpleUUID());
|
||||
if (StringUtils.isEmpty(channel.getDataFrom()))
|
||||
{
|
||||
channel.setDataFrom("BMS");
|
||||
}
|
||||
return serviceChannelMapper.insertServiceChannel(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateServiceChannel(ServiceChannel channel)
|
||||
{
|
||||
if (channel == null || StringUtils.isEmpty(channel.getId()))
|
||||
{
|
||||
throw new ServiceException("渠道ID不能为空");
|
||||
}
|
||||
ServiceChannel existing = serviceChannelMapper.selectServiceChannelById(channel.getId());
|
||||
if (existing == null)
|
||||
{
|
||||
throw new ServiceException("渠道不存在或已被删除");
|
||||
}
|
||||
prepare(channel, false);
|
||||
assertCodeUnique(channel);
|
||||
if (!StringUtils.equals(existing.getChannelCode(), channel.getChannelCode())
|
||||
&& countUsage(existing.getChannelCode()) > 0)
|
||||
{
|
||||
throw new ServiceException("渠道「" + existing.getChannelName() + "」已被产品渠道或供应商引用,不能修改渠道编码");
|
||||
}
|
||||
return serviceChannelMapper.updateServiceChannel(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateServiceChannelStatus(ServiceChannel channel)
|
||||
{
|
||||
if (channel == null || StringUtils.isEmpty(channel.getId()))
|
||||
{
|
||||
throw new ServiceException("渠道ID不能为空");
|
||||
}
|
||||
if (!"0".equals(channel.getBlOpen()) && !"1".equals(channel.getBlOpen()))
|
||||
{
|
||||
throw new ServiceException("启用状态只能是启用或停用");
|
||||
}
|
||||
ServiceChannel existing = serviceChannelMapper.selectServiceChannelById(channel.getId());
|
||||
if (existing == null)
|
||||
{
|
||||
throw new ServiceException("渠道不存在或已被删除");
|
||||
}
|
||||
return serviceChannelMapper.updateServiceChannelStatus(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteServiceChannelByIds(String[] ids)
|
||||
{
|
||||
if (ids == null || ids.length == 0)
|
||||
{
|
||||
throw new ServiceException("请选择要删除的渠道");
|
||||
}
|
||||
for (String id : ids)
|
||||
{
|
||||
if (StringUtils.isEmpty(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ServiceChannel existing = serviceChannelMapper.selectServiceChannelById(id);
|
||||
if (existing == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (countUsage(existing.getChannelCode()) > 0)
|
||||
{
|
||||
throw new ServiceException("渠道「" + existing.getChannelName() + "」已被产品渠道或供应商引用,不能删除,请改为停用");
|
||||
}
|
||||
}
|
||||
return serviceChannelMapper.deleteServiceChannelByIds(ids);
|
||||
}
|
||||
|
||||
private void prepare(ServiceChannel channel, boolean creating)
|
||||
{
|
||||
if (channel == null)
|
||||
{
|
||||
throw new ServiceException("渠道信息不能为空");
|
||||
}
|
||||
channel.setChannelCode(StringUtils.trim(channel.getChannelCode()));
|
||||
channel.setChannelName(StringUtils.trim(channel.getChannelName()));
|
||||
channel.setServiceCode(trimToNull(channel.getServiceCode()));
|
||||
channel.setServiceName(trimToNull(channel.getServiceName()));
|
||||
channel.setShipperName(trimToNull(channel.getShipperName()));
|
||||
channel.setDesPort(trimToNull(channel.getDesPort()));
|
||||
if (StringUtils.isEmpty(channel.getChannelCode()))
|
||||
{
|
||||
throw new ServiceException("渠道编码不能为空");
|
||||
}
|
||||
if (channel.getChannelCode().length() > 64)
|
||||
{
|
||||
throw new ServiceException("渠道编码不能超过64个字符");
|
||||
}
|
||||
if (StringUtils.isEmpty(channel.getChannelName()))
|
||||
{
|
||||
throw new ServiceException("渠道名称不能为空");
|
||||
}
|
||||
if (channel.getChannelName().length() > 200)
|
||||
{
|
||||
throw new ServiceException("渠道名称不能超过200个字符");
|
||||
}
|
||||
if (StringUtils.isEmpty(channel.getBlOpen()))
|
||||
{
|
||||
if (creating)
|
||||
{
|
||||
channel.setBlOpen("1");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ServiceException("请选择启用状态");
|
||||
}
|
||||
}
|
||||
if (!"0".equals(channel.getBlOpen()) && !"1".equals(channel.getBlOpen()))
|
||||
{
|
||||
throw new ServiceException("启用状态只能是启用或停用");
|
||||
}
|
||||
if (StringUtils.isNotEmpty(channel.getBlMultiple())
|
||||
&& !"0".equals(channel.getBlMultiple()) && !"1".equals(channel.getBlMultiple()))
|
||||
{
|
||||
throw new ServiceException("是否多票只能是是或否");
|
||||
}
|
||||
if (channel.getDimDivisor() != null && channel.getDimDivisor() < 0)
|
||||
{
|
||||
throw new ServiceException("抛货系数不能为负数");
|
||||
}
|
||||
}
|
||||
|
||||
private void assertCodeUnique(ServiceChannel channel)
|
||||
{
|
||||
ServiceChannel same = serviceChannelMapper.selectServiceChannelByCode(channel.getChannelCode());
|
||||
if (same != null && !StringUtils.equals(same.getId(), channel.getId()))
|
||||
{
|
||||
throw new ServiceException("渠道编码「" + channel.getChannelCode() + "」已存在");
|
||||
}
|
||||
}
|
||||
|
||||
private int countUsage(String channelCode)
|
||||
{
|
||||
if (StringUtils.isEmpty(channelCode))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return serviceChannelMapper.countProductChannelByCode(channelCode)
|
||||
+ serviceChannelMapper.countSupplierByChannelCode(channelCode);
|
||||
}
|
||||
|
||||
private String trimToNull(String value)
|
||||
{
|
||||
String text = StringUtils.trim(value);
|
||||
return StringUtils.isEmpty(text) ? null : text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,10 @@ public class PrcPriceSetVersion extends BaseEntity
|
||||
/** 0 draft / 3 pending / 1 active / 2 expired */
|
||||
private String status;
|
||||
/**
|
||||
* Create-price mode for this version:
|
||||
* PULL = from product library,
|
||||
* IMPORT = Excel with prior version preload,
|
||||
* IMPORT_FRESH = Excel without prior version data.
|
||||
* Column: price_build_mode
|
||||
* Create-price mode for this version (column: price_build_mode).
|
||||
* SALES: PULL = product library; IMPORT = Excel + prior version; IMPORT_FRESH = Excel blank.
|
||||
* PRODUCT/REDLINE: IMPORT = copy from effect version; IMPORT_FRESH = rebuild (Excel).
|
||||
* Legacy catalog: COPY → IMPORT, old IMPORT → IMPORT_FRESH (see migrate SQL).
|
||||
*/
|
||||
private String priceBuildMode;
|
||||
/** 0 normal / 1 soft-deleted */
|
||||
|
||||
@@ -30,6 +30,9 @@ public interface BmsProductMapper
|
||||
|
||||
public List<BmsProduct> selectBmsProductList(BmsProduct product);
|
||||
|
||||
/** Dropdown options: slim columns, no country/surcharge count subqueries */
|
||||
public List<BmsProduct> selectBmsProductOptions(BmsProduct product);
|
||||
|
||||
public int insertBmsProduct(BmsProduct product);
|
||||
|
||||
public int updateBmsProduct(BmsProduct product);
|
||||
|
||||
@@ -19,6 +19,9 @@ public interface IBmsProductService
|
||||
|
||||
public List<BmsProduct> selectBmsProductList(BmsProduct product);
|
||||
|
||||
/** Product dropdown options (no status refresh, no count subqueries). */
|
||||
public List<BmsProduct> selectBmsProductOptions(BmsProduct product);
|
||||
|
||||
public int insertBmsProduct(BmsProduct product);
|
||||
|
||||
public int updateBmsProduct(BmsProduct product);
|
||||
|
||||
@@ -71,6 +71,11 @@ public interface IPrcPriceSetService
|
||||
*/
|
||||
public List<PrcPriceSetDetail> selectDetailListByProduct(Long psvId, String productCode);
|
||||
|
||||
/**
|
||||
* Load all detail rows for a version (no pagination). For batch prefetch vs N byProduct calls.
|
||||
*/
|
||||
public List<PrcPriceSetDetail> selectDetailListByVersion(Long psvId);
|
||||
|
||||
/**
|
||||
* 版本明细搜索下拉选项:按版本去重后的 产品/国家/分区 列表。
|
||||
* 返回 { products: [{code,name}], countries: [{code,name}], zones: [{code,name}] }
|
||||
|
||||
+89
-2
@@ -129,8 +129,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
@Override
|
||||
public List<BmsProduct> selectBmsProductList(BmsProduct product)
|
||||
{
|
||||
productMapper.refreshStatusByDates();
|
||||
countryMapper.refreshStatusByDates();
|
||||
// Status refresh is done on activate / scheduled task; skip UPDATE on every list read.
|
||||
if (product != null)
|
||||
{
|
||||
product.setStatus(normalizeStatusCode(product.getStatus()));
|
||||
@@ -139,6 +138,17 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
return productMapper.selectBmsProductList(product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BmsProduct> selectBmsProductOptions(BmsProduct product)
|
||||
{
|
||||
if (product != null)
|
||||
{
|
||||
product.setStatus(normalizeStatusCode(product.getStatus()));
|
||||
product.setEffectStatus(normalizeStatusCode(product.getEffectStatus()));
|
||||
}
|
||||
return productMapper.selectBmsProductOptions(product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertBmsProduct(BmsProduct product)
|
||||
{
|
||||
@@ -378,6 +388,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.sheetNotFound"));
|
||||
}
|
||||
assertNotSingleCountryQuoteForMultiImport(workbook);
|
||||
|
||||
List<BmsProduct> products = parseCatalog(catalog);
|
||||
if (products.isEmpty())
|
||||
@@ -678,6 +689,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.sheetNotFound"));
|
||||
}
|
||||
assertNotSingleCountryQuoteForMultiImport(workbook);
|
||||
List<BmsProduct> products = dedupeCatalogProducts(parseCatalog(catalog));
|
||||
if (products.isEmpty())
|
||||
{
|
||||
@@ -763,6 +775,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
if (out.isEmpty())
|
||||
{
|
||||
assertNotSingleCountryQuoteForMultiImport(workbook);
|
||||
throw new ServiceException(MessageUtils.message("price.set.import.noPriceRows"));
|
||||
}
|
||||
QuoteWorkbook parsed = new QuoteWorkbook();
|
||||
@@ -778,6 +791,80 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
private Map<String, List<BmsProductCountry>> byCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-country import expects price sheets with a "country" column.
|
||||
* Single-country dedicated-line sheets use product-name + weight blocks instead.
|
||||
*/
|
||||
private void assertNotSingleCountryQuoteForMultiImport(Workbook workbook)
|
||||
{
|
||||
if (looksLikeSingleCountryQuote(workbook))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.singleCountryAsMulti"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Detect single-country cost-sheet layout (no country column on price sheets). */
|
||||
private boolean looksLikeSingleCountryQuote(Workbook workbook)
|
||||
{
|
||||
if (workbook == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
boolean sawCountryCol = false;
|
||||
boolean sawCountryPriceBlock = false;
|
||||
for (int i = 0; i < workbook.getNumberOfSheets(); i++)
|
||||
{
|
||||
String sheetName = workbook.getSheetName(i);
|
||||
if (shouldSkipCountryPriceSheet(sheetName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Sheet sheet = workbook.getSheetAt(i);
|
||||
if (sheet == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (findHeaderCell(sheet, "\u56fd\u5bb6") != null)
|
||||
{
|
||||
sawCountryCol = true;
|
||||
break;
|
||||
}
|
||||
int[] nameHeader = findHeaderCell(sheet, "\u4ea7\u54c1\u540d\u79f0");
|
||||
if (nameHeader == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Row head = sheet.getRow(nameHeader[0]);
|
||||
if (head == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (findColByHeader(head, "\u91cd\u91cf", 0) >= 0)
|
||||
{
|
||||
sawCountryPriceBlock = true;
|
||||
}
|
||||
}
|
||||
if (sawCountryCol)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (sawCountryPriceBlock)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
Sheet catalog = workbook.getSheet(CATALOG_SHEET);
|
||||
if (catalog == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (findHeaderCell(catalog, "\u5bf9\u63a5\u4ee3\u7801") != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return findHeaderCell(catalog, "\u4ea7\u54c1\u540d\u79f0") != null
|
||||
|| findHeaderCell(catalog, "\u4ea7\u54c1ID") != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<BmsProductCountry>> parseCountryQuoteExcelByProduct(byte[] bytes, String filename,
|
||||
String countryCode) throws Exception
|
||||
|
||||
+12
@@ -881,6 +881,18 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
return selectDetailList(q);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrcPriceSetDetail> selectDetailListByVersion(Long psvId)
|
||||
{
|
||||
if (psvId == null)
|
||||
{
|
||||
return new ArrayList<PrcPriceSetDetail>();
|
||||
}
|
||||
PrcPriceSetDetail q = new PrcPriceSetDetail();
|
||||
q.setPsvId(psvId);
|
||||
return selectDetailList(q);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> selectDetailOptions(Long psvId)
|
||||
{
|
||||
|
||||
+163
-30
@@ -102,7 +102,8 @@ public class CatalogVersionSupport
|
||||
nv.setVersionNo("V" + next);
|
||||
nv.setEffectiveTime(effectiveTime);
|
||||
nv.setStatus(STATUS_DRAFT);
|
||||
nv.setPriceBuildMode("IMPORT");
|
||||
// IMPORT = inherit effect version; IMPORT_FRESH = empty rebuild (Excel)
|
||||
nv.setPriceBuildMode(copyFromLatest ? "IMPORT" : "IMPORT_FRESH");
|
||||
nv.setCreateBy("system");
|
||||
versionMapper.insertPrcPriceSetVersion(nv);
|
||||
|
||||
@@ -122,7 +123,11 @@ public class CatalogVersionSupport
|
||||
return nv;
|
||||
}
|
||||
|
||||
/** Activate draft: set EFFECT, chain expire on previous EFFECT versions of same type. */
|
||||
/**
|
||||
* Activate draft/pending catalog version.
|
||||
* IMPORT_FRESH (align sales): may start inside a prior window; forbid equal to any other
|
||||
* version's effective/expire of the same price_type; then chain expire times and sync children.
|
||||
*/
|
||||
public void activateVersion(Long psvId, Date effectiveTime)
|
||||
{
|
||||
PrcPriceSetVersion ver = versionMapper.selectPrcPriceSetVersionById(psvId);
|
||||
@@ -140,39 +145,165 @@ public class CatalogVersionSupport
|
||||
{
|
||||
throw new ServiceException("请选择生效时间");
|
||||
}
|
||||
Date prevEff = findPrevEffectiveTime(ver);
|
||||
if (prevEff != null && eff.before(prevEff))
|
||||
boolean importFresh = "IMPORT_FRESH".equalsIgnoreCase(StringUtils.trim(ver.getPriceBuildMode()));
|
||||
if (importFresh)
|
||||
{
|
||||
throw new ServiceException("生效时间不能早于上一版本");
|
||||
validateImportFreshActivateTime(ver, eff);
|
||||
}
|
||||
|
||||
boolean future = eff.after(new Date());
|
||||
if (!future)
|
||||
else
|
||||
{
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceType(ver.getPriceType());
|
||||
q.setStatus(STATUS_EFFECT);
|
||||
List<PrcPriceSetVersion> prevList = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (prevList != null)
|
||||
Date prevEff = findPrevEffectiveTime(ver);
|
||||
if (prevEff != null && eff.before(prevEff))
|
||||
{
|
||||
for (PrcPriceSetVersion prev : prevList)
|
||||
{
|
||||
if (prev.getPsvId().equals(psvId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
prev.setExpireTime(eff);
|
||||
prev.setStatus(STATUS_EXPIRED);
|
||||
versionMapper.updatePrcPriceSetVersion(prev);
|
||||
}
|
||||
throw new ServiceException("生效时间不能早于上一版本");
|
||||
}
|
||||
}
|
||||
|
||||
ver.setEffectiveTime(eff);
|
||||
ver.setExpireTime(null);
|
||||
ver.setStatus(future ? STATUS_PENDING : STATUS_EFFECT);
|
||||
versionMapper.updatePrcPriceSetVersion(ver);
|
||||
refreshCatalogStatuses(ver.getPriceType());
|
||||
// Chain B.expire=A.eff, A.expire=next/B.oldExpire; refresh status + product/detail effect
|
||||
chainAndRefreshCatalogStatuses(ver.getPriceType());
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPORT_FRESH: may be earlier than prev-by-seq; must not equal any other version's eff/exp
|
||||
* within the same PRODUCT/REDLINE type.
|
||||
*/
|
||||
private void validateImportFreshActivateTime(PrcPriceSetVersion version, Date effectiveTime)
|
||||
{
|
||||
if (version == null || effectiveTime == null || StringUtils.isEmpty(version.getPriceType()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceType(version.getPriceType());
|
||||
List<PrcPriceSetVersion> list = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (list == null || list.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (PrcPriceSetVersion v : list)
|
||||
{
|
||||
if (v == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (version.getPsvId() != null && version.getPsvId().equals(v.getPsvId()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (sameTime(v.getEffectiveTime(), effectiveTime) || sameTime(v.getExpireTime(), effectiveTime))
|
||||
{
|
||||
throw new ServiceException("生效时间不能与已有版本的生效时间或失效时间相同");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort timed versions by effective_time, chain expire_time = next start, then set status
|
||||
* and sync product/detail effect fields (same idea as sales refreshVersionStatus).
|
||||
*/
|
||||
private void chainAndRefreshCatalogStatuses(String priceType)
|
||||
{
|
||||
assertCatalogType(priceType);
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceType(priceType);
|
||||
List<PrcPriceSetVersion> versions = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (versions == null || versions.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
versions.sort((a, b) -> {
|
||||
Date ta = a.getEffectiveTime();
|
||||
Date tb = b.getEffectiveTime();
|
||||
if (ta == null && tb == null)
|
||||
{
|
||||
return Integer.compare(nullToZero(a.getVersionSeq()), nullToZero(b.getVersionSeq()));
|
||||
}
|
||||
if (ta == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (tb == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
int c = ta.compareTo(tb);
|
||||
if (c != 0)
|
||||
{
|
||||
return c;
|
||||
}
|
||||
return Integer.compare(nullToZero(a.getVersionSeq()), nullToZero(b.getVersionSeq()));
|
||||
});
|
||||
Date now = new Date();
|
||||
Long effectPsvId = null;
|
||||
for (int i = 0; i < versions.size(); i++)
|
||||
{
|
||||
PrcPriceSetVersion v = versions.get(i);
|
||||
Date nextStart = null;
|
||||
for (int j = i + 1; j < versions.size(); j++)
|
||||
{
|
||||
Date t = versions.get(j).getEffectiveTime();
|
||||
if (t != null)
|
||||
{
|
||||
nextStart = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
v.setExpireTime(nextStart);
|
||||
if (v.getEffectiveTime() == null)
|
||||
{
|
||||
v.setStatus(STATUS_DRAFT);
|
||||
continue;
|
||||
}
|
||||
if (v.getEffectiveTime().getTime() > now.getTime())
|
||||
{
|
||||
v.setStatus(STATUS_PENDING);
|
||||
continue;
|
||||
}
|
||||
if (nextStart != null && nextStart.getTime() <= now.getTime())
|
||||
{
|
||||
v.setStatus(STATUS_EXPIRED);
|
||||
continue;
|
||||
}
|
||||
v.setStatus(STATUS_EFFECT);
|
||||
effectPsvId = v.getPsvId();
|
||||
}
|
||||
if (effectPsvId != null)
|
||||
{
|
||||
for (PrcPriceSetVersion v : versions)
|
||||
{
|
||||
if (STATUS_EFFECT.equals(v.getStatus())
|
||||
&& v.getPsvId() != null
|
||||
&& !v.getPsvId().equals(effectPsvId))
|
||||
{
|
||||
v.setStatus(STATUS_EXPIRED);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PrcPriceSetVersion v : versions)
|
||||
{
|
||||
PrcPriceSetVersion db = versionMapper.selectPrcPriceSetVersionById(v.getPsvId());
|
||||
if (db == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
boolean statusChanged = !String.valueOf(v.getStatus()).equals(String.valueOf(db.getStatus()));
|
||||
boolean expireChanged = !sameTime(v.getExpireTime(), db.getExpireTime());
|
||||
boolean effChanged = !sameTime(v.getEffectiveTime(), db.getEffectiveTime());
|
||||
if (statusChanged || expireChanged || effChanged)
|
||||
{
|
||||
versionMapper.updatePrcPriceSetVersion(v);
|
||||
}
|
||||
}
|
||||
productMapper.syncEffectByPriceType(priceType);
|
||||
detailMapper.syncEffectByPriceType(priceType);
|
||||
}
|
||||
|
||||
private static int nullToZero(Integer v)
|
||||
{
|
||||
return v == null ? 0 : v.intValue();
|
||||
}
|
||||
|
||||
public List<PrcPriceSetVersion> listVersions(String priceType, String status)
|
||||
@@ -345,14 +476,16 @@ public class CatalogVersionSupport
|
||||
|
||||
/**
|
||||
* Prepare draft for catalog price create page.
|
||||
* copyFromLatest=true: copy from current EFFECT version when draft is empty.
|
||||
* copyFromLatest=false: empty draft (Excel import / rebuild).
|
||||
* copyFromLatest=true → priceBuildMode=IMPORT: copy from current EFFECT version when draft is empty.
|
||||
* copyFromLatest=false → priceBuildMode=IMPORT_FRESH: empty draft (Excel import / rebuild).
|
||||
* Legacy values COPY/IMPORT are migrated by SQL to IMPORT/IMPORT_FRESH.
|
||||
*/
|
||||
public PrcPriceSetVersion prepareDraftVersion(String priceType, boolean copyFromLatest)
|
||||
{
|
||||
assertCatalogType(priceType);
|
||||
List<PrcPriceSetVersion> drafts = listVersions(priceType, STATUS_DRAFT);
|
||||
PrcPriceSetVersion draft;
|
||||
String mode = copyFromLatest ? "IMPORT" : "IMPORT_FRESH";
|
||||
if (drafts.isEmpty())
|
||||
{
|
||||
if (copyFromLatest)
|
||||
@@ -364,7 +497,7 @@ public class CatalogVersionSupport
|
||||
}
|
||||
}
|
||||
draft = ensureDraftVersion(priceType, null, copyFromLatest);
|
||||
draft.setPriceBuildMode(copyFromLatest ? "COPY" : "IMPORT");
|
||||
draft.setPriceBuildMode(mode);
|
||||
versionMapper.updatePrcPriceSetVersion(draft);
|
||||
return reloadCatalogVersion(draft.getPsvId(), priceType);
|
||||
}
|
||||
@@ -386,12 +519,12 @@ public class CatalogVersionSupport
|
||||
productMapper.updatePriceTypeByPsvId(draft.getPsvId(), priceType);
|
||||
detailMapper.updatePriceTypeByPsvId(draft.getPsvId(), priceType);
|
||||
}
|
||||
draft.setPriceBuildMode("COPY");
|
||||
draft.setPriceBuildMode(mode);
|
||||
versionMapper.updatePrcPriceSetVersion(draft);
|
||||
}
|
||||
else if (count == 0)
|
||||
{
|
||||
draft.setPriceBuildMode("IMPORT");
|
||||
draft.setPriceBuildMode(mode);
|
||||
versionMapper.updatePrcPriceSetVersion(draft);
|
||||
}
|
||||
return reloadCatalogVersion(draft.getPsvId(), priceType);
|
||||
|
||||
+12
@@ -117,6 +117,11 @@ public class BmsRegionSyncServiceImpl implements IBmsRegionSyncService
|
||||
if (existing != null)
|
||||
{
|
||||
region.setRegionId(existing.getRegionId());
|
||||
// 已绑定到 OSS 的国旗不随外部图片地址覆盖,避免同步后国旗错位或丢失
|
||||
if (isOwnFlag(existing.getFlagUrl()))
|
||||
{
|
||||
region.setFlagUrl(existing.getFlagUrl());
|
||||
}
|
||||
regionMapper.updateRegion(region);
|
||||
updateCount++;
|
||||
}
|
||||
@@ -261,4 +266,11 @@ public class BmsRegionSyncServiceImpl implements IBmsRegionSyncService
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
/** 已经上传到本系统 OSS 的国旗地址,同步时保留。 */
|
||||
private boolean isOwnFlag(String flagUrl)
|
||||
{
|
||||
return StringUtils.isNotEmpty(flagUrl)
|
||||
&& (flagUrl.startsWith("oss://") || flagUrl.contains("/region/flag/"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,22 +77,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
where writeoff_id = #{writeoffId} and status = #{effectiveStatus}
|
||||
</update>
|
||||
|
||||
<select id="selectWriteoffList" parameterType="ArBalanceWriteoff" resultMap="ArBalanceWriteoffResult">
|
||||
<select id="selectWriteoffList" resultMap="ArBalanceWriteoffResult">
|
||||
<include refid="selectColumns"/>
|
||||
<where>
|
||||
<if test="customerId != null">and w.customer_id = #{customerId}</if>
|
||||
<if test="arBillId != null">and w.ar_bill_id = #{arBillId}</if>
|
||||
<if test="status != null and status != ''">and w.status = #{status}</if>
|
||||
<if test="currency != null and currency != ''">and w.currency = #{currency}</if>
|
||||
<if test="writeoffNo != null and writeoffNo != ''">and w.writeoff_no like concat('%', #{writeoffNo}, '%')</if>
|
||||
<if test="billNo != null and billNo != ''">and w.bill_no like concat('%', #{billNo}, '%')</if>
|
||||
<if test="receiptId != null">and w.receipt_id = #{receiptId}</if>
|
||||
<if test="params != null and params.beginTime != null and params.beginTime != ''">
|
||||
and date_format(w.writeoff_date,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
<if test="query.customerId != null">and w.customer_id = #{query.customerId}</if>
|
||||
<if test="query.arBillId != null">and w.ar_bill_id = #{query.arBillId}</if>
|
||||
<if test="query.status != null and query.status != ''">and w.status = #{query.status}</if>
|
||||
<if test="query.currency != null and query.currency != ''">and w.currency = #{query.currency}</if>
|
||||
<if test="query.writeoffNo != null and query.writeoffNo != ''">and w.writeoff_no like concat('%', #{query.writeoffNo}, '%')</if>
|
||||
<if test="query.billNo != null and query.billNo != ''">and w.bill_no like concat('%', #{query.billNo}, '%')</if>
|
||||
<if test="query.receiptId != null">and w.receipt_id = #{query.receiptId}</if>
|
||||
<if test="query.params != null and query.params.beginTime != null and query.params.beginTime != ''">
|
||||
and date_format(w.writeoff_date,'%y%m%d') >= date_format(#{query.params.beginTime},'%y%m%d')
|
||||
</if>
|
||||
<if test="params != null and params.endTime != null and params.endTime != ''">
|
||||
and date_format(w.writeoff_date,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
<if test="query.params != null and query.params.endTime != null and query.params.endTime != ''">
|
||||
and date_format(w.writeoff_date,'%y%m%d') <= date_format(#{query.params.endTime},'%y%m%d')
|
||||
</if>
|
||||
<include refid="com.bms.ar.mapper.ArBillScopeMapper.billScope"/>
|
||||
</where>
|
||||
order by w.create_time desc, w.writeoff_id desc
|
||||
</select>
|
||||
|
||||
@@ -78,6 +78,34 @@
|
||||
order by f.fee_no, f.detail_fee_id
|
||||
</select>
|
||||
|
||||
<select id="selectByBillIdAndDetailIdScoped" resultMap="ArBillDetailFeeResult">
|
||||
select f.detail_fee_id,
|
||||
f.detail_id,
|
||||
f.prc_income_bill_fee_id,
|
||||
f.fee_no,
|
||||
f.fee_type,
|
||||
f.fee_name,
|
||||
f.sc_id,
|
||||
f.quantity,
|
||||
f.unit,
|
||||
f.unit_price,
|
||||
f.amount,
|
||||
f.product_amount,
|
||||
f.redline_amount,
|
||||
f.currency,
|
||||
f.calculation_formula,
|
||||
f.round_mode,
|
||||
f.remark,
|
||||
f.create_by,
|
||||
f.create_time
|
||||
from ar_bill_detail_fee f
|
||||
inner join ar_bill_detail d on d.detail_id = f.detail_id
|
||||
where d.ar_bill_id = #{arBillId}
|
||||
and f.detail_id = #{detailId}
|
||||
AND EXISTS (SELECT 1 FROM ar_bill b WHERE b.ar_bill_id = d.ar_bill_id <include refid="com.bms.ar.mapper.ArBillScopeMapper.billScope"/>)
|
||||
order by f.fee_no, f.detail_fee_id
|
||||
</select>
|
||||
|
||||
<select id="selectByBillId" parameterType="long" resultMap="ArBillDetailFeeResult">
|
||||
select detail_fee_id,
|
||||
detail_id,
|
||||
|
||||
@@ -46,6 +46,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
from ar_bill_detail where ar_bill_id = #{arBillId} order by detail_no
|
||||
</select>
|
||||
|
||||
<select id="selectByBillIdScoped" resultMap="ArBillDetailResult">
|
||||
select detail_id, ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, transfer_no, receive_time,
|
||||
product_code, product_name, goods_description, country_code, country_name, dest_postcode,
|
||||
actual_weight, receive_weight, volume_weight, charge_weight, freight_rate, registration_fee, pkg_length, pkg_width, pkg_height,
|
||||
currency, total_amount, product_amount, redline_amount, billing_fail_status, remark, create_by, create_time
|
||||
from ar_bill_detail where ar_bill_id = #{arBillId} AND EXISTS (SELECT 1 FROM ar_bill b WHERE b.ar_bill_id = ar_bill_detail.ar_bill_id <include refid="com.bms.ar.mapper.ArBillScopeMapper.billScope"/>)
|
||||
order by detail_no
|
||||
</select>
|
||||
|
||||
<select id="selectByBillIdAndSourceIds" resultMap="ArBillDetailResult">
|
||||
select detail_id, ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, transfer_no, receive_time,
|
||||
product_code, product_name, goods_description, country_code, country_name, dest_postcode,
|
||||
|
||||
@@ -69,6 +69,15 @@
|
||||
order by create_time desc, task_id desc
|
||||
</select>
|
||||
|
||||
<select id="selectActiveByCreateByScoped" resultMap="ArBillGenerateTaskResult">
|
||||
<include refid="columns"/>
|
||||
where create_by = #{createBy}
|
||||
and status in ('WAITING', 'RUNNING', 'CANCEL_REQUESTED')
|
||||
AND EXISTS (SELECT 1 FROM ar_bill b WHERE b.ar_bill_id = ar_bill_generate_task.ar_bill_id
|
||||
<include refid="com.bms.ar.mapper.ArBillScopeMapper.billScope"/>)
|
||||
order by create_time desc, task_id desc
|
||||
</select>
|
||||
|
||||
<!-- 定时调度器按创建时间顺序取待处理任务。 -->
|
||||
<select id="selectWaiting" resultMap="ArBillGenerateTaskResult">
|
||||
<include refid="columns"/>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<result property="createTime" column="create_time"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectList" parameterType="ArBillLog" resultMap="ArBillLogResult">
|
||||
<select id="selectList" resultMap="ArBillLogResult">
|
||||
select l.log_id, l.ar_bill_id, l.object_type, l.object_id, l.operation_type, l.operation_desc,
|
||||
case l.object_type
|
||||
when 'BILL' then b.bill_no
|
||||
@@ -43,14 +43,15 @@
|
||||
left join ar_balance_writeoff bw on bw.writeoff_id = case when l.object_type = 'BALANCE_WRITEOFF' then
|
||||
cast(l.object_id as unsigned) end
|
||||
<where>
|
||||
<if test="arBillId != null">and l.ar_bill_id = #{arBillId}</if>
|
||||
<if test="objectType != null and objectType != ''">and l.object_type = #{objectType}</if>
|
||||
<if test="objectId != null and objectId != ''">and l.object_id = #{objectId}</if>
|
||||
<if test="operationType != null and operationType != ''">and l.operation_type = #{operationType}</if>
|
||||
<if test="operationSource != null and operationSource != ''">and l.operation_source = #{operationSource}
|
||||
<if test="query.arBillId != null">and l.ar_bill_id = #{query.arBillId}</if>
|
||||
<if test="query.objectType != null and query.objectType != ''">and l.object_type = #{query.objectType}</if>
|
||||
<if test="query.objectId != null and query.objectId != ''">and l.object_id = #{query.objectId}</if>
|
||||
<if test="query.operationType != null and query.operationType != ''">and l.operation_type = #{query.operationType}</if>
|
||||
<if test="query.operationSource != null and query.operationSource != ''">and l.operation_source = #{query.operationSource}
|
||||
</if>
|
||||
<if test="requestId != null and requestId != ''">and l.request_id = #{requestId}</if>
|
||||
<if test="result != null and result != ''">and l.result = #{result}</if>
|
||||
<if test="query.requestId != null and query.requestId != ''">and l.request_id = #{query.requestId}</if>
|
||||
<if test="query.result != null and query.result != ''">and l.result = #{query.result}</if>
|
||||
<include refid="com.bms.ar.mapper.ArBillScopeMapper.billScope"/>
|
||||
</where>
|
||||
order by l.create_time desc, l.log_id desc
|
||||
</select>
|
||||
|
||||
@@ -76,34 +76,35 @@
|
||||
<sql id="arBillListFilter">
|
||||
<where>
|
||||
and (generation_status is null or generation_status = 'READY')
|
||||
<if test="billNo != null and billNo != ''">and bill_no like concat('%', #{billNo}, '%')</if>
|
||||
<if test="billName != null and billName != ''">and bill_name like concat('%', #{billName}, '%')</if>
|
||||
<if test="customerCode != null and customerCode != ''">and customer_code like concat('%', #{customerCode},
|
||||
<if test="query.billNo != null and query.billNo != ''">and bill_no like concat('%', #{query.billNo}, '%')</if>
|
||||
<if test="query.billName != null and query.billName != ''">and bill_name like concat('%', #{query.billName}, '%')</if>
|
||||
<if test="query.customerCode != null and query.customerCode != ''">and customer_code like concat('%', #{query.customerCode},
|
||||
'%')
|
||||
</if>
|
||||
<if test="customerName != null and customerName != ''">and customer_name like concat('%', #{customerName},
|
||||
<if test="query.customerName != null and query.customerName != ''">and customer_name like concat('%', #{query.customerName},
|
||||
'%')
|
||||
</if>
|
||||
<if test="customerId != null">and customer_id = #{customerId}</if>
|
||||
<if test="status != null and status != ''">and status = #{status}</if>
|
||||
<if test="currency != null and currency != ''">and currency = #{currency}</if>
|
||||
<if test="billDateStart != null">and bill_date >= #{billDateStart}</if>
|
||||
<if test="billDateEnd != null">and bill_date <= #{billDateEnd}</if>
|
||||
<if test="billPeriodEnd != null">and bill_period_end = #{billPeriodEnd}</if>
|
||||
<if test="query.customerId != null">and customer_id = #{query.customerId}</if>
|
||||
<if test="query.status != null and query.status != ''">and status = #{query.status}</if>
|
||||
<if test="query.currency != null and query.currency != ''">and currency = #{query.currency}</if>
|
||||
<if test="query.billDateStart != null">and bill_date >= #{query.billDateStart}</if>
|
||||
<if test="query.billDateEnd != null">and bill_date <= #{query.billDateEnd}</if>
|
||||
<if test="query.billPeriodEnd != null">and bill_period_end = #{query.billPeriodEnd}</if>
|
||||
<include refid="com.bms.ar.mapper.ArBillScopeMapper.billScope"/>
|
||||
</where>
|
||||
</sql>
|
||||
|
||||
<select id="selectArBillList" parameterType="com.bms.ar.domain.vo.ArBillQuery" resultMap="ArBillResult">
|
||||
<include refid="selectArBillColumns"/>
|
||||
<select id="selectArBillList" resultMap="ArBillResult">
|
||||
select b.* from ar_bill b
|
||||
<include refid="arBillListFilter"/>
|
||||
order by create_time desc, ar_bill_id desc
|
||||
</select>
|
||||
|
||||
<select id="selectArBillSummary" parameterType="com.bms.ar.domain.vo.ArBillQuery" resultMap="ArBillSummaryResult">
|
||||
<select id="selectArBillSummary" resultMap="ArBillSummaryResult">
|
||||
select coalesce(sum(total_amount), 0) as total_amount,
|
||||
coalesce(sum(written_off_amount), 0) as written_off_amount,
|
||||
coalesce(sum(unwritten_amount), 0) as unwritten_amount
|
||||
from ar_bill
|
||||
from ar_bill b
|
||||
<include refid="arBillListFilter"/>
|
||||
</select>
|
||||
|
||||
@@ -112,6 +113,11 @@
|
||||
where ar_bill_id = #{arBillId}
|
||||
</select>
|
||||
|
||||
<select id="selectArBillByIdScoped" resultMap="ArBillResult">
|
||||
select b.* from ar_bill b where b.ar_bill_id = #{arBillId}
|
||||
<include refid="com.bms.ar.mapper.ArBillScopeMapper.billScope"/>
|
||||
</select>
|
||||
|
||||
<select id="selectArBillByBillNo" parameterType="string" resultMap="ArBillResult">
|
||||
<include refid="selectArBillColumns"/>
|
||||
where bill_no = #{billNo}
|
||||
@@ -144,11 +150,12 @@
|
||||
</select>
|
||||
|
||||
<select id="selectUnsettledByCustomer" resultMap="ArBillResult">
|
||||
<include refid="selectArBillColumns"/>
|
||||
select b.* from ar_bill b
|
||||
where customer_id = #{customerId}
|
||||
and status in ('CONFIRMED', 'PARTIAL_WRITTEN_OFF')
|
||||
and (generation_status is null or generation_status = 'READY')
|
||||
and unwritten_amount > 0
|
||||
<include refid="com.bms.ar.mapper.ArBillScopeMapper.billScope"/>
|
||||
order by bill_date asc, ar_bill_id asc
|
||||
</select>
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.bms.ar.mapper.ArBillScopeMapper">
|
||||
<resultMap id="CustomerOption" type="com.bms.customer.customerinfo.domain.BmsCustomerOption">
|
||||
<id property="customerId" column="customer_id"/>
|
||||
<result property="customerCode" column="customer_code"/>
|
||||
<result property="customerName" column="customer_name"/>
|
||||
</resultMap>
|
||||
<select id="countCustomerSourceBills" resultType="int">
|
||||
SELECT COUNT(*) FROM prc_order_bill WHERE customer_id = #{customerId}
|
||||
AND bill_id IN
|
||||
<foreach collection="sourceIds" item="sourceId" open="(" separator="," close=")">#{sourceId}</foreach>
|
||||
</select>
|
||||
<!-- c 固定为客户表别名;无部门层级扩展,不按角色管理 data_scope 过滤。 -->
|
||||
<sql id="customerScope">
|
||||
<choose>
|
||||
<when test="scope != null and scope.all"/>
|
||||
<when test="scope != null and (scope.self or scope.department)">
|
||||
AND c.del_flag = '0'
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM sys_user su
|
||||
WHERE CAST(su.user_id AS CHAR) = c.sales_user_id AND su.del_flag = '0'
|
||||
AND (1 = 0
|
||||
<if test="scope.self">OR su.user_id = #{scope.userId}</if>
|
||||
<if test="scope.department">OR su.dept_id = #{scope.deptId}</if>
|
||||
)
|
||||
)
|
||||
</when>
|
||||
<otherwise>AND 1 = 0</otherwise>
|
||||
</choose>
|
||||
</sql>
|
||||
<!-- b 固定为账单表别名;ALL 无需关联客户,保留归属异常的历史账单。 -->
|
||||
<sql id="billScope">
|
||||
<choose>
|
||||
<when test="scope != null and scope.all"/>
|
||||
<otherwise>
|
||||
AND EXISTS (SELECT 1 FROM ctr_customer c WHERE c.customer_id = b.customer_id
|
||||
<include refid="com.bms.ar.mapper.ArBillScopeMapper.customerScope"/>
|
||||
)
|
||||
</otherwise>
|
||||
</choose>
|
||||
</sql>
|
||||
<select id="countAccessibleBill" resultType="int">
|
||||
SELECT COUNT(*) FROM ar_bill b WHERE b.ar_bill_id = #{arBillId}
|
||||
<include refid="billScope"/>
|
||||
</select>
|
||||
<select id="selectAccessibleCustomer" resultMap="CustomerOption">
|
||||
SELECT c.customer_id, c.customer_code, c.customer_name FROM ctr_customer c
|
||||
WHERE c.customer_id = #{customerId} AND c.del_flag = '0'
|
||||
<include refid="customerScope"/>
|
||||
</select>
|
||||
<select id="selectCustomerOptions" resultMap="CustomerOption">
|
||||
SELECT c.customer_id, c.customer_code, c.customer_name FROM ctr_customer c WHERE c.del_flag = '0'
|
||||
<include refid="customerScope"/>
|
||||
<if test="keywords != null and !keywords.isEmpty()">
|
||||
AND (<foreach collection="keywords" item="keyword" separator=" OR ">
|
||||
(c.customer_name LIKE CONCAT('%', #{keyword}, '%') OR c.customer_code LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR c.customer_code_dw LIKE CONCAT('%', #{keyword}, '%'))
|
||||
</foreach>)
|
||||
</if>
|
||||
ORDER BY c.customer_id DESC
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -112,6 +112,55 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
order by c.customer_id desc
|
||||
</select>
|
||||
|
||||
<select id="selectUnlinkedSalesUserCustomerList"
|
||||
parameterType="com.bms.customer.customerinfo.domain.BmsCustomer"
|
||||
resultType="com.bms.customer.customerinfo.vo.UnlinkedSalesCustomerVO">
|
||||
select c.customer_id as customerId,
|
||||
c.customer_code as customerCode,
|
||||
c.customer_name as customerName,
|
||||
c.customer_code_dw as customerCodeDw,
|
||||
c.customer_type as customerType,
|
||||
c.is_enable as isEnable,
|
||||
c.source_sales_code as sourceSalesCode
|
||||
from ctr_customer c
|
||||
left join sys_user su
|
||||
on su.del_flag = '0'
|
||||
and cast(su.user_id as char) = c.sales_user_id
|
||||
where c.del_flag = '0'
|
||||
and (c.sales_user_id is null or trim(c.sales_user_id) = '')
|
||||
${params.dataScope}
|
||||
order by c.customer_id desc
|
||||
</select>
|
||||
|
||||
<resultMap type="BmsCustomerOption" id="BmsCustomerOptionResult">
|
||||
<result property="customerId" column="customer_id" />
|
||||
<result property="customerCode" column="customer_code" />
|
||||
<result property="customerName" column="customer_name" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 客户下拉:不关联账户、套餐,只取名称和编码;对外代码仅参与模糊条件 -->
|
||||
<select id="selectCustomerOptions" parameterType="BmsCustomer" resultMap="BmsCustomerOptionResult">
|
||||
select c.customer_id, c.customer_code, c.customer_name
|
||||
from ctr_customer c
|
||||
left join sys_user su
|
||||
on su.del_flag = '0'
|
||||
and cast(su.user_id as char) = c.sales_user_id
|
||||
<where>
|
||||
c.del_flag = '0'
|
||||
<if test="keywordList != null and keywordList.size() > 0">
|
||||
and (
|
||||
<foreach collection="keywordList" item="kw" separator=" or ">
|
||||
(c.customer_name like concat('%', #{kw}, '%')
|
||||
or c.customer_code like concat('%', #{kw}, '%')
|
||||
or c.customer_code_dw like concat('%', #{kw}, '%'))
|
||||
</foreach>
|
||||
)
|
||||
</if>
|
||||
${params.dataScope}
|
||||
</where>
|
||||
order by c.customer_id desc
|
||||
</select>
|
||||
|
||||
<select id="countValidSalesUserId" resultType="int">
|
||||
select count(1)
|
||||
from sys_user
|
||||
|
||||
@@ -34,6 +34,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="modifyDate" column="modify_date"/>
|
||||
<result property="gmtCreate" column="gmt_create"/>
|
||||
<result property="gmtModified" column="gmt_modified"/>
|
||||
<result property="syncTime" column="sync_time"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectChannelVo">
|
||||
@@ -55,15 +56,104 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND channel_name like concat('%', #{channelName}, '%')
|
||||
</if>
|
||||
<if test="serviceCode != null and serviceCode != ''">
|
||||
AND service_code = #{serviceCode}
|
||||
AND service_code like concat('%', #{serviceCode}, '%')
|
||||
</if>
|
||||
<if test="serviceName != null and serviceName != ''">
|
||||
AND service_name like concat('%', #{serviceName}, '%')
|
||||
</if>
|
||||
<if test="desPort != null and desPort != ''">
|
||||
AND des_port like concat('%', #{desPort}, '%')
|
||||
</if>
|
||||
<if test="shipperName != null and shipperName != ''">
|
||||
AND shipper_name like concat('%', #{shipperName}, '%')
|
||||
</if>
|
||||
<if test="blOpen != null and blOpen != ''">
|
||||
AND bl_open = #{blOpen}
|
||||
</if>
|
||||
</where>
|
||||
order by service_code, channel_code
|
||||
</select>
|
||||
|
||||
<select id="selectServiceChannelById" parameterType="String" resultMap="ServiceChannelResult">
|
||||
<include refid="selectChannelVo"/>
|
||||
where id = #{id}
|
||||
</select>
|
||||
|
||||
<select id="selectServiceChannelByCode" parameterType="String" resultMap="ServiceChannelResult">
|
||||
<include refid="selectChannelVo"/>
|
||||
where channel_code = #{channelCode}
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<select id="countProductChannelByCode" parameterType="String" resultType="int">
|
||||
select count(1) from srv_product_channel where channel_code = #{channelCode}
|
||||
</select>
|
||||
|
||||
<select id="countSupplierByChannelCode" parameterType="String" resultType="int">
|
||||
select count(1) from sup_supplier
|
||||
where del_flag = '0'
|
||||
and channel_ids is not null
|
||||
and channel_ids != ''
|
||||
and find_in_set(#{channelCode}, replace(channel_ids, ' ', ''))
|
||||
</select>
|
||||
|
||||
<insert id="insertServiceChannel" parameterType="com.bms.masterdata.domain.ServiceChannel">
|
||||
insert into srv_service_channel (
|
||||
id, channel_code, channel_name, service_code, service_name,
|
||||
bl_open, bl_multiple, cargo_type, pay_type, shipper_name, shipper_id,
|
||||
query_type, trajectory_code, final_service_name, des_port,
|
||||
weight_type, dim_divisor, weight_rule, shipment_type, data_from, remarks,
|
||||
create_name, create_date, modifier, modify_date, gmt_create, gmt_modified
|
||||
) values (
|
||||
#{id}, #{channelCode}, #{channelName}, #{serviceCode}, #{serviceName},
|
||||
#{blOpen}, #{blMultiple}, #{cargoType}, #{payType}, #{shipperName}, #{shipperId},
|
||||
#{queryType}, #{trajectoryCode}, #{finalServiceName}, #{desPort},
|
||||
#{weightType}, #{dimDivisor}, #{weightRule}, #{shipmentType}, #{dataFrom}, #{remarks},
|
||||
#{createName}, sysdate(), #{modifier}, sysdate(), sysdate(), sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateServiceChannel" parameterType="com.bms.masterdata.domain.ServiceChannel">
|
||||
update srv_service_channel
|
||||
set channel_code = #{channelCode},
|
||||
channel_name = #{channelName},
|
||||
service_code = #{serviceCode},
|
||||
service_name = #{serviceName},
|
||||
bl_open = #{blOpen},
|
||||
bl_multiple = #{blMultiple},
|
||||
cargo_type = #{cargoType},
|
||||
pay_type = #{payType},
|
||||
shipper_name = #{shipperName},
|
||||
shipper_id = #{shipperId},
|
||||
query_type = #{queryType},
|
||||
trajectory_code = #{trajectoryCode},
|
||||
final_service_name = #{finalServiceName},
|
||||
des_port = #{desPort},
|
||||
weight_type = #{weightType},
|
||||
dim_divisor = #{dimDivisor},
|
||||
weight_rule = #{weightRule},
|
||||
shipment_type = #{shipmentType},
|
||||
data_from = #{dataFrom},
|
||||
remarks = #{remarks},
|
||||
modifier = #{modifier},
|
||||
modify_date = sysdate(),
|
||||
gmt_modified = sysdate()
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<update id="updateServiceChannelStatus" parameterType="com.bms.masterdata.domain.ServiceChannel">
|
||||
update srv_service_channel
|
||||
set bl_open = #{blOpen},
|
||||
modifier = #{modifier},
|
||||
modify_date = sysdate(),
|
||||
gmt_modified = sysdate()
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<delete id="deleteServiceChannelByIds" parameterType="String">
|
||||
delete from srv_service_channel where id in
|
||||
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
||||
|
||||
@@ -45,14 +45,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
ifnull(p.is_del, '0') as is_del,
|
||||
p.source_sheet, null as remark, null as tms_id,
|
||||
v.created_by, p.create_time, v.updated_by, p.update_time,
|
||||
(select count(distinct ifnull(c.country_code, c.country_name)) from prc_price_set_detail c
|
||||
where ifnull(c.is_del, '0') = '0'
|
||||
and (c.psp_id = p.psp_id or (c.psv_id = p.psv_id and c.product_code = p.product_code))
|
||||
) as country_count,
|
||||
(select count(*) from prc_surcharge_rel r
|
||||
inner join prc_surcharge s on s.sc_id = r.sc_id and ifnull(s.is_del, '0') = '0'
|
||||
where ifnull(r.is_del, '0') = '0'
|
||||
and r.product_id = p.psp_id and r.price_type = 'PRODUCT') as surcharge_count
|
||||
null as country_count, null as surcharge_count
|
||||
from prc_price_set_product p
|
||||
inner join prc_price_set_version v on v.psv_id = p.psv_id
|
||||
</sql>
|
||||
@@ -176,6 +169,66 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</choose>
|
||||
</select>
|
||||
|
||||
<!-- Dropdown: same filters as list, without per-row country/surcharge count scans -->
|
||||
<select id="selectBmsProductOptions" parameterType="BmsProduct" resultMap="BmsProductResult">
|
||||
select p.psp_id as product_id, p.psv_id, p.price_type, p.product_code, p.product_name, p.service_level, p.cargo_attr,
|
||||
p.dest_region, p.tax_mode,
|
||||
p.effective_time as effective_date, p.expire_time as expire_date, p.status,
|
||||
ifnull(p.is_del, '0') as is_del,
|
||||
null as update_note, null as source_sheet, null as remark, null as tms_id,
|
||||
null as created_by, p.create_time, null as updated_by, p.update_time,
|
||||
null as country_count, null as surcharge_count
|
||||
from prc_price_set_product p
|
||||
inner join prc_price_set_version v on v.psv_id = p.psv_id
|
||||
<where>
|
||||
and p.price_type = 'PRODUCT'
|
||||
and ifnull(p.is_del, '0') = '0'
|
||||
<choose>
|
||||
<when test="psvId != null">
|
||||
AND p.psv_id = #{psvId}
|
||||
</when>
|
||||
<when test="statusGroup != null and statusGroup == 'effect'">
|
||||
AND v.price_set_id is null AND v.status = '1'
|
||||
</when>
|
||||
<when test="statusGroup != null and statusGroup == 'pending'">
|
||||
AND v.price_set_id is null AND v.status in ('0', '3')
|
||||
</when>
|
||||
<when test="statusGroup != null and statusGroup == 'expired'">
|
||||
AND v.status = '2'
|
||||
</when>
|
||||
<otherwise>
|
||||
AND p.psv_id = <include refid="latestProductPsv"/>
|
||||
</otherwise>
|
||||
</choose>
|
||||
<if test="productCode != null and productCode != ''">
|
||||
AND p.product_code like concat('%', #{productCode}, '%')
|
||||
</if>
|
||||
<if test="productName != null and productName != ''">
|
||||
AND p.product_name like concat('%', #{productName}, '%')
|
||||
</if>
|
||||
<if test="taxMode != null and taxMode != ''">
|
||||
AND p.tax_mode = #{taxMode}
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
AND v.status = #{status}
|
||||
</if>
|
||||
<if test="effectStatus != null and effectStatus != ''">
|
||||
AND v.status = #{effectStatus}
|
||||
</if>
|
||||
<if test="statusGroup != null and statusGroup == 'active'">
|
||||
AND v.status in ('0', '1')
|
||||
</if>
|
||||
<if test="cargoAttr != null and cargoAttr != ''">
|
||||
AND p.cargo_attr like concat('%', #{cargoAttr}, '%')
|
||||
</if>
|
||||
<if test="params != null and params.keyword != null and params.keyword != ''">
|
||||
AND (p.product_code like concat('%', #{params.keyword}, '%')
|
||||
OR p.product_name like concat('%', #{params.keyword}, '%'))
|
||||
</if>
|
||||
</where>
|
||||
order by p.product_code asc, p.psp_id desc
|
||||
</select>
|
||||
|
||||
<insert id="insertBmsProduct" parameterType="BmsProduct" useGeneratedKeys="true" keyProperty="productId">
|
||||
insert into prc_price_set_product (
|
||||
price_type, psv_id, product_code, product_name, service_level, cargo_attr,
|
||||
|
||||
@@ -1298,7 +1298,7 @@ CREATE TABLE `prc_price_set_version` (
|
||||
`effective_time` datetime DEFAULT NULL COMMENT '生效时间',
|
||||
`expire_time` datetime DEFAULT NULL COMMENT '失效时间(空=长期;下一版本启动时写入该启动时间)',
|
||||
`status` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '0 draft 3 pending 1 effect 2 expired',
|
||||
`price_build_mode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建价格方式 PULL=拉取产品库 IMPORT=Excel继承上一版 IMPORT_FRESH=Excel重新建价',
|
||||
`price_build_mode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建价格方式 SALES:PULL/IMPORT/IMPORT_FRESH; PRODUCT/REDLINE:IMPORT=沿用生效版 IMPORT_FRESH=重新建价',
|
||||
`is_del` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '是否删除 0未删除 1已删除',
|
||||
`created_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
-- 区域国旗全部改为 OSS 地址
|
||||
-- 上次约 104 条能看到图,是因为只有这些行原来写了 flagcdn 地址,其余 flag_url 为空
|
||||
-- 本次只按国家两位码更新,中文名写法不同也不会漏
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ad.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AD';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ae.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/af.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AF';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ag.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ai.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AI';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/al.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AL';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/am.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ao.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AO';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ar.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/as.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AS';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/at.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AT';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/au.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AU';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/aw.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AW';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ax.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AX';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/az.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'AZ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ba.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BA';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bb.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BB';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bd.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BD';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/be.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bf.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BF';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bg.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bh.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BH';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bi.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BI';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bj.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BJ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bl.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BL';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bm.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bn.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BN';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bo.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BO';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bq.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BQ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/br.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bs.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BS';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bt.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BT';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bw.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BW';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/by.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BY';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/bz.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'BZ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ca.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CA';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cc.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CC';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cd.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CD';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cf.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CF';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cg.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ch.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CH';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ci.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CI';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ck.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CK';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cl.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CL';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cm.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cn.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CN';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/co.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CO';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cr.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cu.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CU';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cv.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CV';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cw.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CW';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cx.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CX';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cy.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CY';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/cz.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'CZ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/de.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'DE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/dj.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'DJ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/dk.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'DK';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/dm.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'DM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/do.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'DO';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/dz.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'DZ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ec.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'EC';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ee.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'EE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/eg.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'EG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/eh.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'EH';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/er.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'ER';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/es.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'ES';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/et.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'ET';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/fi.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'FI';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/fj.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'FJ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/fk.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'FK';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/fo.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'FO';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/fr.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'FR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ga.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GA';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gb.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GB';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gd.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GD';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ge.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gf.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GF';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gg.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gh.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GH';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gi.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GI';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gl.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GL';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gm.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gn.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GN';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gp.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GP';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gq.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GQ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gr.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gs.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GS';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gt.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GT';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gu.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GU';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gw.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GW';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/gy.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'GY';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/hk.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'HK';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/hn.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'HN';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/hr.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'HR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ht.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'HT';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/hu.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'HU';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/id.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'ID';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ie.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'IE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/il.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'IL';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/im.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'IM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/in.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'IN';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/io.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'IO';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/iq.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'IQ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ir.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'IR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/is.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'IS';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/it.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'IT';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/je.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'JE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/jm.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'JM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/jo.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'JO';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/jp.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'JP';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ke.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'KE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/kg.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'KG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/kh.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'KH';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ki.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'KI';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/km.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'KM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/kn.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'KN';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/kp.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'KP';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/kr.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'KR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/kw.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'KW';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ky.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'KY';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/kz.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'KZ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/la.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'LA';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/lb.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'LB';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/lc.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'LC';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/li.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'LI';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/lk.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'LK';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/lr.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'LR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ls.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'LS';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/lt.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'LT';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/lu.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'LU';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/lv.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'LV';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ly.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'LY';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ma.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MA';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mc.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MC';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/md.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MD';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/me.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'ME';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mf.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MF';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mg.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mh.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MH';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mk.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MK';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ml.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'ML';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mm.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mn.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MN';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mo.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MO';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mp.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MP';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mq.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MQ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mr.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ms.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MS';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mt.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MT';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mu.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MU';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mv.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MV';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mw.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MW';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mx.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MX';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/my.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MY';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/mz.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'MZ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/na.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'NA';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/nc.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'NC';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ne.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'NE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/nf.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'NF';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ng.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'NG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ni.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'NI';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/nl.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'NL';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/no.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'NO';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/np.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'NP';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/nr.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'NR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/nu.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'NU';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/nz.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'NZ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/om.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'OM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/pa.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PA';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/pe.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/pf.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PF';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/pg.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ph.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PH';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/pk.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PK';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/pl.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PL';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/pm.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/pn.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PN';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/pr.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ps.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PS';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/pt.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PT';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/pw.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PW';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/py.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'PY';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/qa.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'QA';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/re.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'RE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ro.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'RO';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/rs.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'RS';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ru.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'RU';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/rw.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'RW';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sa.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SA';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sb.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SB';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sc.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SC';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sd.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SD';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/se.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sg.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sh.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SH';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/si.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SI';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sj.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SJ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sk.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SK';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sl.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SL';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sm.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sn.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SN';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/so.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SO';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sr.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ss.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SS';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/st.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'ST';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sv.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SV';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sx.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SX';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sy.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SY';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/sz.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'SZ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tc.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TC';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/td.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TD';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tf.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TF';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tg.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/th.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TH';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tj.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TJ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tk.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TK';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tl.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TL';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tm.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tn.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TN';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/to.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TO';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tr.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TR';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tt.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TT';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tv.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TV';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tw.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TW';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/tz.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'TZ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ua.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'UA';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ug.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'UG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/um.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'UM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/us.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'US';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/uy.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'UY';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/uz.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'UZ';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/va.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'VA';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/vc.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'VC';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ve.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'VE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/vg.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'VG';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/vi.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'VI';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/vn.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'VN';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/vu.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'VU';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/wf.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'WF';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ws.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'WS';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/xk.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'XK';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/ye.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'YE';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/yt.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'YT';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/za.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'ZA';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/zm.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'ZM';
|
||||
UPDATE region SET flag_url = 'https://oss.lionquexpress.com/bms/prod/region/flag/zw.png', update_time = sysdate() WHERE del_flag = '0' AND country_code_2 = 'ZW';
|
||||
@@ -0,0 +1,68 @@
|
||||
-- 数据管理 - 渠道管理
|
||||
-- 权限:data:serviceChannel:list|query|add|edit|remove|export
|
||||
-- 启用/停用走修改权限 data:serviceChannel:edit
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @parentId := (
|
||||
SELECT menu_id FROM sys_menu
|
||||
WHERE path = 'data' AND menu_type = 'M' AND parent_id = 0
|
||||
ORDER BY menu_id ASC LIMIT 1
|
||||
);
|
||||
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
|
||||
SELECT 2502, N'渠道管理', @parentId, 3, 'serviceChannel', 'data/serviceChannel/index', '', 'ServiceChannel', 1, 0, 'C', '0', '0', 'data:serviceChannel:list', 'guide', 'admin', sysdate(), N'服务渠道维护,数据表 srv_service_channel'
|
||||
FROM DUAL WHERE @parentId IS NOT NULL AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2502 OR perms = 'data:serviceChannel:list');
|
||||
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
|
||||
SELECT 2503, N'查询', 2502, 1, '', NULL, 1, 0, 'F', '0', '0', 'data:serviceChannel:query', '#', 'admin', sysdate()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2503 OR perms = 'data:serviceChannel:query');
|
||||
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
|
||||
SELECT 2504, N'新增', 2502, 2, '', NULL, 1, 0, 'F', '0', '0', 'data:serviceChannel:add', '#', 'admin', sysdate()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2504 OR perms = 'data:serviceChannel:add');
|
||||
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
|
||||
SELECT 2505, N'修改', 2502, 3, '', NULL, 1, 0, 'F', '0', '0', 'data:serviceChannel:edit', '#', 'admin', sysdate()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2505 OR perms = 'data:serviceChannel:edit');
|
||||
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
|
||||
SELECT 2506, N'删除', 2502, 4, '', NULL, 1, 0, 'F', '0', '0', 'data:serviceChannel:remove', '#', 'admin', sysdate()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2506 OR perms = 'data:serviceChannel:remove');
|
||||
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
|
||||
SELECT 2507, N'导出', 2502, 5, '', NULL, 1, 0, 'F', '0', '0', 'data:serviceChannel:export', '#', 'admin', sysdate()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2507 OR perms = 'data:serviceChannel:export');
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 1, m.menu_id FROM (
|
||||
SELECT 2502 AS menu_id UNION SELECT 2503 UNION SELECT 2504 UNION SELECT 2505 UNION SELECT 2506 UNION SELECT 2507
|
||||
) m
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id);
|
||||
|
||||
-- 已有「数据管理-区域管理」权限的角色,同步授予对应的渠道管理权限
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT DISTINCT rm.role_id, dst.menu_id
|
||||
FROM sys_role_menu rm
|
||||
JOIN sys_menu src ON src.menu_id = rm.menu_id AND src.perms = 'data:region:list'
|
||||
JOIN sys_menu dst ON dst.perms IN ('data:serviceChannel:list', 'data:serviceChannel:query', 'data:serviceChannel:export')
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = dst.menu_id
|
||||
);
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT DISTINCT rm.role_id, dst.menu_id
|
||||
FROM sys_role_menu rm
|
||||
JOIN sys_menu src ON src.menu_id = rm.menu_id AND src.perms = 'data:region:edit'
|
||||
JOIN sys_menu dst ON dst.perms IN ('data:serviceChannel:add', 'data:serviceChannel:edit')
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = dst.menu_id
|
||||
);
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT DISTINCT rm.role_id, dst.menu_id
|
||||
FROM sys_role_menu rm
|
||||
JOIN sys_menu src ON src.menu_id = rm.menu_id AND src.perms = 'data:region:remove'
|
||||
JOIN sys_menu dst ON dst.perms = 'data:serviceChannel:remove'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = dst.menu_id
|
||||
);
|
||||
@@ -0,0 +1,15 @@
|
||||
-- Catalog (PRODUCT / REDLINE) price_build_mode rename:
|
||||
-- COPY → IMPORT (按现有生效版本建价)
|
||||
-- IMPORT → IMPORT_FRESH (重新建产品价)
|
||||
-- Order matters: migrate old IMPORT first, then COPY.
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
UPDATE prc_price_set_version
|
||||
SET price_build_mode = 'IMPORT_FRESH'
|
||||
WHERE price_type IN ('PRODUCT', 'REDLINE')
|
||||
AND UPPER(IFNULL(price_build_mode, '')) = 'IMPORT';
|
||||
|
||||
UPDATE prc_price_set_version
|
||||
SET price_build_mode = 'IMPORT'
|
||||
WHERE price_type IN ('PRODUCT', 'REDLINE')
|
||||
AND UPPER(IFNULL(price_build_mode, '')) = 'COPY';
|
||||
@@ -1,3 +1,3 @@
|
||||
-- Add create-price mode on package version: PULL / IMPORT
|
||||
ALTER TABLE prc_price_set_version
|
||||
ADD COLUMN price_build_mode varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建价格方式 PULL=拉取产品库 IMPORT=Excel继承上一版 IMPORT_FRESH=Excel重新建价' AFTER status;
|
||||
ADD COLUMN price_build_mode varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建价格方式 SALES:PULL/IMPORT/IMPORT_FRESH; PRODUCT/REDLINE:IMPORT=沿用生效版 IMPORT_FRESH=重新建价' AFTER status;
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function listArBillCustomerOptions(query) {
|
||||
return request({ url: '/ar/bill/customer-options', method: 'get', params: query })
|
||||
}
|
||||
|
||||
export function listArBillGenerateCustomerOptions(query) {
|
||||
return request({ url: '/ar/bill/generate/customer-options', method: 'get', params: query })
|
||||
}
|
||||
|
||||
// 应收账单接口统一放在这里;生成和导出均为异步任务,提交接口不等待后台处理完成。
|
||||
export function listArBill(query) {
|
||||
return request({ url: '/ar/bill/list', method: 'get', params: query })
|
||||
|
||||
@@ -9,6 +9,25 @@ export function listCustomer(query) {
|
||||
})
|
||||
}
|
||||
|
||||
// 分页查询未关联业务员的客户
|
||||
export function listUnlinkedSalesUserCustomers(query) {
|
||||
return request({
|
||||
url: '/customersupplier/customerinfo/unlinked-sales-user/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 客户下拉选项:只返回客户ID、客户编码、客户名称
|
||||
// keywords 按客户名称、客户编码、对外代码模糊查询
|
||||
export function listCustomerOption(query) {
|
||||
return request({
|
||||
url: '/customersupplier/customerinfo/optionselect',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询客户主详细
|
||||
export function getCustomer(customerId) {
|
||||
return request({
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/** 分页查询渠道列表 */
|
||||
export function listServiceChannel(query) {
|
||||
return request({
|
||||
url: '/masterdata/servicechannel/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** 服务渠道下拉选项(非分页,来源 srv_service_channel 老系统镜像) */
|
||||
export function listChannelOptions(query) {
|
||||
return request({
|
||||
@@ -8,3 +17,46 @@ export function listChannelOptions(query) {
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询渠道详情 */
|
||||
export function getServiceChannel(id) {
|
||||
return request({
|
||||
url: '/masterdata/servicechannel/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增渠道 */
|
||||
export function addServiceChannel(data) {
|
||||
return request({
|
||||
url: '/masterdata/servicechannel',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 修改渠道 */
|
||||
export function updateServiceChannel(data) {
|
||||
return request({
|
||||
url: '/masterdata/servicechannel',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 启用或停用渠道 */
|
||||
export function changeServiceChannelStatus(id, blOpen) {
|
||||
return request({
|
||||
url: '/masterdata/servicechannel/changeStatus',
|
||||
method: 'put',
|
||||
data: { id, blOpen }
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除渠道 */
|
||||
export function delServiceChannel(ids) {
|
||||
return request({
|
||||
url: '/masterdata/servicechannel/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -198,6 +198,15 @@ export function listSetDetailByProduct(psvId, productCode) {
|
||||
})
|
||||
}
|
||||
|
||||
/** All detail rows for a version (no pagination); one call instead of N byProduct. */
|
||||
export function listSetDetailByVersion(psvId) {
|
||||
return request({
|
||||
url: '/price/set/detail/byVersion',
|
||||
method: 'get',
|
||||
params: { psvId }
|
||||
})
|
||||
}
|
||||
|
||||
/** 版本明细搜索下拉选项:按版本去重后的 产品/国家/分区 */
|
||||
export function listSetDetailOptions(psvId) {
|
||||
return request({
|
||||
|
||||
@@ -32,10 +32,15 @@
|
||||
</template>
|
||||
|
||||
<script setup name="CustomerSelect">
|
||||
import { listCustomer } from '@/api/customersuppliermanagement/customerinfo/customer.js'
|
||||
import { listCustomer, listCustomerOption } from '@/api/customersuppliermanagement/customerinfo/customer.js'
|
||||
import { listSupplier } from '@/api/customersuppliermanagement/supplierinfo/supplier.js'
|
||||
|
||||
const props = defineProps({
|
||||
// 业务页面可传入专用选项接口,默认行为保持不变。
|
||||
fetchOptions: {
|
||||
type: Function,
|
||||
default: undefined
|
||||
},
|
||||
// 单选时为 String/Number;multiple 时为数组
|
||||
modelValue: {
|
||||
type: [String, Number, Array],
|
||||
@@ -95,6 +100,11 @@ const props = defineProps({
|
||||
remoteSearch: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 为 true 时客户下拉走专用选项接口,默认仍走客户列表接口
|
||||
optionSelect: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -138,7 +148,9 @@ const displayOptions = computed(() => {
|
||||
function fetchList(keyword = '') {
|
||||
if (!props.remoteSearch && loaded.value) return Promise.resolve(options.value)
|
||||
loading.value = true
|
||||
const api = isCustomer.value ? listCustomer : listSupplier
|
||||
const api = props.fetchOptions || (!isCustomer.value
|
||||
? listSupplier
|
||||
: (props.optionSelect ? listCustomerOption : listCustomer))
|
||||
const currentRequest = ++requestSerial
|
||||
const query = String(keyword || '').trim()
|
||||
return api({
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<el-dialog v-model="dialogOpen" class="generate-dialog" title="生成应收账单" width="1200px" append-to-body destroy-on-close>
|
||||
<el-form ref="generateFormRef" :model="generateForm" :rules="generateRules" label-width="110px" class="generate-form">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8"><el-form-item label="客户" prop="customerId"><customer-select v-model="generateForm.customerId" placeholder="请输入客户名称或代码搜索" :remote-search="true" :page-size="20" @change="handleCustomerChange" /></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="客户" prop="customerId"><customer-select :fetch-options="listArBillGenerateCustomerOptions" v-model="generateForm.customerId" placeholder="请输入客户名称或代码搜索" :remote-search="true" :page-size="20" @change="handleCustomerChange" /></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="币种" prop="currency"><el-select v-model="generateForm.currency" placeholder="请选择币种" style="width: 100%;" @change="handleCurrencyChange"><el-option label="人民币 CNY" value="CNY" /><el-option label="美元 USD" value="USD" /></el-select></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="账单截止日" prop="cutoffDate"><el-date-picker v-model="generateForm.cutoffDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择账单截止日" style="width: 100%;" @change="resetPreview" /></el-form-item></el-col>
|
||||
<el-col :span="8">
|
||||
@@ -146,7 +146,7 @@
|
||||
</template>
|
||||
|
||||
<script setup name="GenerateBillDialog">
|
||||
import { getDefaultArBankAccount, previewArBill, previewExcludedArBill, generateArBill } from '@/api/ar/bill'
|
||||
import { listArBillGenerateCustomerOptions, getDefaultArBankAccount, previewArBill, previewExcludedArBill, generateArBill } from '@/api/ar/bill'
|
||||
import { Refresh, Search, Select } from '@element-plus/icons-vue'
|
||||
import { AR_BILL_SELECTION_MODE } from '@/constants/ar/enums'
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<el-button type="warning" plain icon="Download" v-hasPermi="['ar:bill:export']" @click="handleExport">导出</el-button>
|
||||
</template>
|
||||
<template #search-customerId="{ model }">
|
||||
<customer-select v-model="model.customerId" placeholder="请输入客户名称或代码搜索" :remote-search="true" :page-size="20" />
|
||||
<customer-select :fetch-options="listArBillCustomerOptions" v-model="model.customerId" placeholder="请输入客户名称或代码搜索" :remote-search="true" :page-size="20" />
|
||||
</template>
|
||||
<template #col-status="{ row }"><el-tag :type="statusType(row.status)">{{ statusText(row.status) }}</el-tag></template>
|
||||
<template #col-totalAmount="{ row }"><span class="amount">{{ money(row.totalAmount) }}</span></template>
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
<script setup name="ArBill">
|
||||
import { saveAs } from 'file-saver'
|
||||
import { listArBill, getArBill, confirmArBill, voidArBill, exportArBill, getArBillExport, downloadArBillExport, getArBillGenerateTask, listActiveArBillGenerateTasks, cancelArBillGenerateTask, retryArBillGenerateTask } from '@/api/ar/bill'
|
||||
import { listArBillCustomerOptions, listArBill, getArBill, confirmArBill, voidArBill, exportArBill, getArBillExport, downloadArBillExport, getArBillGenerateTask, listActiveArBillGenerateTasks, cancelArBillGenerateTask, retryArBillGenerateTask } from '@/api/ar/bill'
|
||||
import BillDetail from './components/BillDetail.vue'
|
||||
import GenerateBillDialog from './components/GenerateBillDialog.vue'
|
||||
import { AR_BILL_STATUS, AR_BILL_STATUS_OPTIONS, AR_BILL_STATUS_TYPES } from '@/constants/ar/bill'
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="modelValue"
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
title="未关联业务员客户"
|
||||
width="min(1100px, 96vw)"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
@open="handleOpen"
|
||||
>
|
||||
<el-table v-loading="loading" :data="customerList" border row-key="customerId" empty-text="暂无未关联业务员的客户">
|
||||
<el-table-column label="客户编码" prop="customerCode" min-width="120" align="center" />
|
||||
<el-table-column label="客户名称" prop="customerName" min-width="240" show-overflow-tooltip />
|
||||
<el-table-column label="对外代码" prop="customerCodeDw" min-width="140" align="center" />
|
||||
<el-table-column label="源业务员工号" prop="sourceSalesCode" min-width="150" align="center" />
|
||||
<el-table-column label="业务类型" prop="customerType" width="110" align="center">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="customer_type" :value="scope.row.customerType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="启用状态" prop="isEnable" width="110" align="center">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.isEnable" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="业务员" width="120" align="center">
|
||||
<template #default>
|
||||
<span class="unlinked-label">未关联</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="pageNum"
|
||||
v-model:limit="pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="$emit('update:modelValue', false)">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { listUnlinkedSalesUserCustomers } from '@/api/customersuppliermanagement/customerinfo/customer'
|
||||
|
||||
defineProps({
|
||||
modelValue: { type: Boolean, default: false }
|
||||
})
|
||||
defineEmits(['update:modelValue'])
|
||||
|
||||
const { sys_normal_disable, customer_type } = useDict('sys_normal_disable', 'customer_type')
|
||||
const loading = ref(false)
|
||||
const customerList = ref([])
|
||||
const total = ref(0)
|
||||
const pageNum = ref(1)
|
||||
const pageSize = ref(10)
|
||||
|
||||
function handleOpen() {
|
||||
pageNum.value = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
function getList() {
|
||||
loading.value = true
|
||||
listUnlinkedSalesUserCustomers({
|
||||
pageNum: pageNum.value,
|
||||
pageSize: pageSize.value
|
||||
}).then(response => {
|
||||
customerList.value = response.rows || []
|
||||
total.value = response.total || 0
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.unlinked-label {
|
||||
color: var(--el-color-warning);
|
||||
}
|
||||
</style>
|
||||
@@ -661,7 +661,6 @@
|
||||
filterable
|
||||
placeholder="请选择生效中的套餐"
|
||||
style="width: 100%;"
|
||||
@change="onPriceSetChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in priceSetOptions"
|
||||
@@ -685,30 +684,6 @@
|
||||
<el-input-number v-model="priceSetForm.bindPriority" :min="1" controls-position="right" style="width: 100%;" />
|
||||
<div class="pane-tip">数字越小优先级越高,1 为最高</div>
|
||||
</el-form-item>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="生效日期" prop="prBegindate">
|
||||
<el-date-picker
|
||||
v-model="priceSetForm.prBegindate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="失效日期" prop="prEnddate">
|
||||
<el-date-picker
|
||||
v-model="priceSetForm.prEnddate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="长期有效"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="备注" prop="priceSetNote">
|
||||
<el-input v-model="priceSetForm.priceSetNote" type="textarea" :rows="2" maxlength="100" show-word-limit placeholder="绑定备注(选填)" />
|
||||
</el-form-item>
|
||||
@@ -835,8 +810,7 @@ const priceSetForm = ref({})
|
||||
|
||||
const priceSetRules = {
|
||||
priceSetId: [{ required: true, message: '请选择价格套餐', trigger: 'change' }],
|
||||
bindPriority: [{ required: true, message: '优先级不能为空', trigger: 'blur' }],
|
||||
prBegindate: [{ required: true, message: '绑定生效日期不能为空', trigger: 'change' }]
|
||||
bindPriority: [{ required: true, message: '优先级不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const selectedPriceSet = computed(() =>
|
||||
@@ -871,26 +845,18 @@ function openPriceSetDialog(row) {
|
||||
ensurePriceSetOptions()
|
||||
priceSetForm.value = row
|
||||
? { ...row }
|
||||
: { priceSetId: null, bindPriority: 1, prBegindate: null, prEnddate: null, priceSetNote: null }
|
||||
: { priceSetId: null, bindPriority: 1, priceSetNote: null }
|
||||
priceSetDialogTitle.value = row || boundPriceSets.value.length ? '更换套餐' : '绑定套餐'
|
||||
priceSetDialogVisible.value = true
|
||||
nextTick(() => proxy.$refs['priceSetRef'] && proxy.$refs['priceSetRef'].clearValidate())
|
||||
}
|
||||
|
||||
/** 选择套餐后,生效日期未填时默认今天 */
|
||||
function onPriceSetChange(priceSetId) {
|
||||
if (!priceSetId || priceSetForm.value.prBegindate) return
|
||||
priceSetForm.value.prBegindate = proxy.parseTime(new Date(), '{y}-{m}-{d}')
|
||||
}
|
||||
|
||||
function submitPriceSetForm() {
|
||||
proxy.$refs['priceSetRef'].validate(valid => {
|
||||
if (!valid) return
|
||||
if (priceSetForm.value.prEnddate && priceSetForm.value.prBegindate > priceSetForm.value.prEnddate) {
|
||||
proxy.$modal.msgWarning('失效日期不能早于生效日期')
|
||||
return
|
||||
}
|
||||
const payload = { ...priceSetForm.value, customerId: form.value.customerId }
|
||||
delete payload.prBegindate
|
||||
delete payload.prEnddate
|
||||
priceSetSaving.value = true
|
||||
saveCustomerPriceSet(payload).then(() => {
|
||||
proxy.$modal.msgSuccess('保存成功')
|
||||
|
||||
@@ -114,6 +114,15 @@
|
||||
v-hasPermi="['customersupplier:customerinfo:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="info"
|
||||
plain
|
||||
icon="User"
|
||||
@click="unlinkedSalesDialogVisible = true"
|
||||
v-hasPermi="['customersupplier:customerinfo:list']"
|
||||
>未关联业务员</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
@@ -252,20 +261,6 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="生效日期" prop="prBegindate">
|
||||
<el-date-picker v-model="batchForm.prBegindate" type="date" value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期" style="width: 100%;" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="失效日期" prop="prEnddate">
|
||||
<el-date-picker v-model="batchForm.prEnddate" type="date" value-format="YYYY-MM-DD"
|
||||
placeholder="长期有效" style="width: 100%;" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="优先级">
|
||||
<el-input-number v-model="batchForm.bindPriority" :min="1" controls-position="right" style="width: 100%;" />
|
||||
<div class="batch-tip">数字越小优先级越高,1 为最高</div>
|
||||
@@ -302,6 +297,8 @@
|
||||
:customer-name="billCustomer.customerName"
|
||||
/>
|
||||
|
||||
<customer-unlinked-sales-dialog v-model="unlinkedSalesDialogVisible" />
|
||||
|
||||
<!-- 右键复制列值 -->
|
||||
<table-copy-menu
|
||||
ref="copyMenuRef"
|
||||
@@ -319,6 +316,7 @@ import { useProducts, parseProductCodes } from '@/components/ProductSelect/usePr
|
||||
import { useUsers } from '@/components/UserSelect/useUsers'
|
||||
import CustomerLogDialog from './components/CustomerLogDialog.vue'
|
||||
import CustomerBillListDialog from './customermoney/CustomerBillListDialog.vue'
|
||||
import CustomerUnlinkedSalesDialog from './components/CustomerUnlinkedSalesDialog.vue'
|
||||
import { useColumnSettings } from '@/utils/columnSettings'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
@@ -531,6 +529,7 @@ function handleCustomerMoney(row) {
|
||||
/** 在客户信息列表内打开只读账单弹窗 */
|
||||
const billDialogVisible = ref(false)
|
||||
const billCustomer = ref({})
|
||||
const unlinkedSalesDialogVisible = ref(false)
|
||||
function handleViewBills(row) {
|
||||
if (!row || !row.customerId) {
|
||||
proxy.$modal.msgWarning('客户信息无效,无法查看账单')
|
||||
@@ -550,8 +549,7 @@ const batchFormRef = ref(null)
|
||||
const priceSetOptions = ref([])
|
||||
const batchForm = ref({})
|
||||
const batchRules = {
|
||||
priceSetId: [{ required: true, message: '请选择价格套餐', trigger: 'change' }],
|
||||
prBegindate: [{ required: true, message: '绑定生效日期不能为空', trigger: 'change' }]
|
||||
priceSetId: [{ required: true, message: '请选择价格套餐', trigger: 'change' }]
|
||||
}
|
||||
|
||||
/** 勾选客户中已绑定套餐的数量 */
|
||||
@@ -566,8 +564,6 @@ function handleBatchBindPriceSet() {
|
||||
batchForm.value = {
|
||||
priceSetId: null,
|
||||
bindPriority: 1,
|
||||
prBegindate: proxy.parseTime(new Date(), '{y}-{m}-{d}'),
|
||||
prEnddate: null,
|
||||
priceSetNote: null,
|
||||
overwriteExisting: false
|
||||
}
|
||||
@@ -583,10 +579,6 @@ function handleBatchBindPriceSet() {
|
||||
function submitBatchBind() {
|
||||
batchFormRef.value.validate(valid => {
|
||||
if (!valid) return
|
||||
if (batchForm.value.prEnddate && batchForm.value.prBegindate > batchForm.value.prEnddate) {
|
||||
proxy.$modal.msgWarning('失效日期不能早于生效日期')
|
||||
return
|
||||
}
|
||||
const total = selectedRows.value.length
|
||||
const bound = boundCount.value
|
||||
const setName = (priceSetOptions.value.find(o => o.priceSetId === batchForm.value.priceSetId) || {}).priceSetName || ''
|
||||
@@ -602,8 +594,6 @@ function submitBatchBind() {
|
||||
customerIds: selectedRows.value.map(r => r.customerId),
|
||||
priceSetId: batchForm.value.priceSetId,
|
||||
bindPriority: batchForm.value.bindPriority,
|
||||
prBegindate: batchForm.value.prBegindate,
|
||||
prEnddate: batchForm.value.prEnddate,
|
||||
priceSetNote: batchForm.value.priceSetNote,
|
||||
overwriteExisting: batchForm.value.overwriteExisting
|
||||
})
|
||||
|
||||
@@ -70,7 +70,8 @@
|
||||
</template>
|
||||
|
||||
<template #col-flagEmoji="{ row }">
|
||||
<span style="font-size: 18px">{{ row.flagEmoji }}</span>
|
||||
<img v-if="row.flagUrl" :src="row.flagUrl" alt="" class="region-flag" />
|
||||
<span v-else class="region-flag-emoji">{{ row.flagEmoji }}</span>
|
||||
</template>
|
||||
|
||||
<template #col-action="{ row }">
|
||||
@@ -267,7 +268,7 @@ const searchColumns = [
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{ label: "区域ID", prop: "regionId", align: "center", width: 80 },
|
||||
{ label: "国旗", prop: "flagEmoji", align: "center", width: 60 },
|
||||
{ label: "国旗", prop: "flagEmoji", align: "center", width: 70 },
|
||||
{ label: "中文名", prop: "countryNameCn", align: "center" },
|
||||
{ label: "英文名", prop: "countryNameEn", align: "center" },
|
||||
{ label: "国家代码", prop: "countryCode2", align: "center", width: 80 },
|
||||
@@ -475,3 +476,17 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.region-flag {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.region-flag-emoji {
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<basic-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
row-key="id"
|
||||
selection
|
||||
>
|
||||
<template #toolbar="{ selection, ids }">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['data:serviceChannel:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="selection.length !== 1" @click="handleUpdate(selection[0])" v-hasPermi="['data:serviceChannel:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="!ids.length" @click="handleDelete(ids)" v-hasPermi="['data:serviceChannel:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['data:serviceChannel:export']">导出</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<template #col-blOpen="{ row }">
|
||||
<el-switch
|
||||
v-model="row.blOpen"
|
||||
active-value="1"
|
||||
inactive-value="0"
|
||||
@change="handleStatusChange(row)"
|
||||
v-hasPermi="['data:serviceChannel:edit']"
|
||||
/>
|
||||
</template>
|
||||
<template #col-blMultiple="{ row }">
|
||||
<span>{{ row.blMultiple === '1' ? '是' : row.blMultiple === '0' ? '否' : '-' }}</span>
|
||||
</template>
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(row)" v-hasPermi="['data:serviceChannel:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete([row.id])" v-hasPermi="['data:serviceChannel:remove']">删除</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
<el-dialog :title="title" v-model="open" width="760px" append-to-body>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="渠道编码" prop="channelCode">
|
||||
<el-input v-model="form.channelCode" placeholder="请输入渠道编码" maxlength="64" :disabled="!!form.id" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="渠道名称" prop="channelName">
|
||||
<el-input v-model="form.channelName" placeholder="请输入渠道名称" maxlength="200" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="服务商编码" prop="serviceCode">
|
||||
<el-input v-model="form.serviceCode" placeholder="所属服务商编码" maxlength="32" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="服务商名称" prop="serviceName">
|
||||
<el-input v-model="form.serviceName" placeholder="所属服务商名称" maxlength="200" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="启用状态" prop="blOpen">
|
||||
<el-radio-group v-model="form.blOpen">
|
||||
<el-radio value="1">启用</el-radio>
|
||||
<el-radio value="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否多票" prop="blMultiple">
|
||||
<el-radio-group v-model="form.blMultiple">
|
||||
<el-radio value="1">是</el-radio>
|
||||
<el-radio value="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="目的港/目的地" prop="desPort">
|
||||
<el-input v-model="form.desPort" placeholder="目的港或目的地区域" maxlength="500" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发货人名称" prop="shipperName">
|
||||
<el-input v-model="form.shipperName" placeholder="请输入发货人名称" maxlength="255" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="货物类型" prop="cargoType">
|
||||
<el-input v-model="form.cargoType" maxlength="255" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="付款方式" prop="payType">
|
||||
<el-input v-model="form.payType" maxlength="255" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="末端服务商" prop="finalServiceName">
|
||||
<el-input v-model="form.finalServiceName" maxlength="255" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="轨迹查询类型" prop="queryType">
|
||||
<el-input v-model="form.queryType" maxlength="20" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="轨迹编码" prop="trajectoryCode">
|
||||
<el-input v-model="form.trajectoryCode" maxlength="60" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="重量类型" prop="weightType">
|
||||
<el-input v-model="form.weightType" maxlength="1" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="抛货系数" prop="dimDivisor">
|
||||
<el-input-number v-model="form.dimDivisor" :min="0" :precision="0" controls-position="right" style="width: 100%;" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发货类型" prop="shipmentType">
|
||||
<el-input v-model="form.shipmentType" maxlength="32" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="计重规则" prop="weightRule">
|
||||
<el-input v-model="form.weightRule" type="textarea" :rows="2" maxlength="500" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remarks">
|
||||
<el-input v-model="form.remarks" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="open = false">取 消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ServiceChannel">
|
||||
import { nextTick } from 'vue'
|
||||
import {
|
||||
listServiceChannel, getServiceChannel, addServiceChannel,
|
||||
updateServiceChannel, delServiceChannel, changeServiceChannelStatus
|
||||
} from '@/api/masterdata/serviceChannel'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const tableRef = ref()
|
||||
const formRef = ref()
|
||||
const open = ref(false)
|
||||
const title = ref('')
|
||||
|
||||
const openOptions = [
|
||||
{ label: '启用', value: '1' },
|
||||
{ label: '停用', value: '0' }
|
||||
]
|
||||
|
||||
const searchColumns = [
|
||||
{ label: '渠道编码', prop: 'channelCode', component: 'input', placeholder: '渠道编码', width: '160px' },
|
||||
{ label: '渠道名称', prop: 'channelName', component: 'input', placeholder: '模糊查询', width: '180px' },
|
||||
{ label: '服务商编码', prop: 'serviceCode', component: 'input', placeholder: '服务商编码', width: '160px' },
|
||||
{ label: '服务商名称', prop: 'serviceName', component: 'input', placeholder: '模糊查询', width: '180px' },
|
||||
{ label: '目的港/目的地', prop: 'desPort', component: 'input', placeholder: '模糊查询', width: '180px' },
|
||||
{ label: '发货人名称', prop: 'shipperName', component: 'input', placeholder: '模糊查询', width: '180px' },
|
||||
{ label: '状态', prop: 'blOpen', component: 'select', options: openOptions, placeholder: '启用状态', width: '140px', props: { clearable: true } }
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{ label: '渠道编码', prop: 'channelCode', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '渠道名称', prop: 'channelName', minWidth: 180, showOverflowTooltip: true },
|
||||
{ label: '服务商编码', prop: 'serviceCode', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '服务商名称', prop: 'serviceName', minWidth: 160, showOverflowTooltip: true },
|
||||
{ label: '目的港/目的地', prop: 'desPort', minWidth: 160, showOverflowTooltip: true },
|
||||
{ label: '发货人', prop: 'shipperName', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '启用', prop: 'blOpen', width: 80, slot: 'blOpen' },
|
||||
{ label: '多票', prop: 'blMultiple', width: 70, slot: 'blMultiple' },
|
||||
{ label: '货物类型', prop: 'cargoType', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '末端服务商', prop: 'finalServiceName', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '抛货系数', prop: 'dimDivisor', width: 90 },
|
||||
{ label: '同步时间', prop: 'syncTime', width: 170, parseTime: true },
|
||||
{ label: '操作', prop: 'action', width: 150, fixed: 'right', slot: 'action', showInToolbar: false }
|
||||
]
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
rules: {
|
||||
channelCode: [{ required: true, message: '渠道编码不能为空', trigger: 'blur' }],
|
||||
channelName: [{ required: true, message: '渠道名称不能为空', trigger: 'blur' }],
|
||||
blOpen: [{ required: true, message: '请选择启用状态', trigger: 'change' }]
|
||||
}
|
||||
})
|
||||
const { form, rules } = toRefs(data)
|
||||
|
||||
function queryList(params) {
|
||||
return listServiceChannel(params)
|
||||
}
|
||||
|
||||
function emptyForm() {
|
||||
return {
|
||||
id: undefined,
|
||||
channelCode: '',
|
||||
channelName: '',
|
||||
serviceCode: '',
|
||||
serviceName: '',
|
||||
blOpen: '1',
|
||||
blMultiple: '0',
|
||||
desPort: '',
|
||||
shipperName: '',
|
||||
shipperId: '',
|
||||
cargoType: '',
|
||||
payType: '',
|
||||
finalServiceName: '',
|
||||
queryType: '',
|
||||
trajectoryCode: '',
|
||||
weightType: '',
|
||||
dimDivisor: undefined,
|
||||
shipmentType: '',
|
||||
weightRule: '',
|
||||
remarks: ''
|
||||
}
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
form.value = emptyForm()
|
||||
title.value = '新增渠道'
|
||||
open.value = true
|
||||
nextTick(() => formRef.value && formRef.value.clearValidate())
|
||||
}
|
||||
|
||||
function handleUpdate(row) {
|
||||
getServiceChannel(row.id).then(res => {
|
||||
form.value = Object.assign(emptyForm(), res.data || {})
|
||||
title.value = '修改渠道'
|
||||
open.value = true
|
||||
nextTick(() => formRef.value && formRef.value.clearValidate())
|
||||
})
|
||||
}
|
||||
|
||||
function submitForm() {
|
||||
formRef.value.validate(valid => {
|
||||
if (!valid) return
|
||||
const req = form.value.id ? updateServiceChannel(form.value) : addServiceChannel(form.value)
|
||||
req.then(() => {
|
||||
proxy.$modal.msgSuccess('保存成功')
|
||||
open.value = false
|
||||
tableRef.value?.refresh()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function handleDelete(ids) {
|
||||
if (!ids || !ids.length) return
|
||||
proxy.$modal.confirm('确认删除选中的 ' + ids.length + ' 条渠道?已被产品或供应商引用的渠道不能删除。').then(() => {
|
||||
return delServiceChannel(ids.join(','))
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
tableRef.value?.refresh()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function handleStatusChange(row) {
|
||||
if (!row || !row.id || !row.channelCode) {
|
||||
nextTick(() => {
|
||||
row.blOpen = row.blOpen === '1' ? '0' : '1'
|
||||
})
|
||||
return
|
||||
}
|
||||
if (row.__originalOpen !== undefined && row.__originalOpen === row.blOpen) {
|
||||
return
|
||||
}
|
||||
row.__originalOpen = row.blOpen === '1' ? '0' : '1'
|
||||
const text = row.blOpen === '1' ? '启用' : '停用'
|
||||
const name = row.channelName || row.channelCode
|
||||
proxy.$modal.confirm('确认要' + text + '渠道「' + name + '」吗?').then(() => {
|
||||
return changeServiceChannelStatus(row.id, row.blOpen)
|
||||
}).then(() => {
|
||||
row.__originalOpen = row.blOpen
|
||||
proxy.$modal.msgSuccess(text + '成功')
|
||||
}).catch(() => {
|
||||
const restored = row.blOpen === '1' ? '0' : '1'
|
||||
row.__originalOpen = restored
|
||||
nextTick(() => { row.blOpen = restored })
|
||||
})
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
proxy.download('masterdata/servicechannel/export', {
|
||||
...tableRef.value.getQueryParams()
|
||||
}, `service_channel_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
</script>
|
||||
@@ -1,10 +1,7 @@
|
||||
<!-- start catalog version: pick effective hour -->
|
||||
<!-- start catalog version: pick effective hour;IMPORT_FRESH 对齐销售价规则 -->
|
||||
<template>
|
||||
<el-dialog v-model="visible" :title="dialogTitle" width="460px" append-to-body @closed="onClosed">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item :label="labNo">
|
||||
<span>{{ form.versionNo || '-' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="labEff" prop="effectiveTime">
|
||||
<el-date-picker
|
||||
v-model="form.effectiveTime"
|
||||
@@ -17,6 +14,7 @@
|
||||
:disabled-hours="disabledHours"
|
||||
style="width: 100%"
|
||||
@change="onTimeChange"
|
||||
@visible-change="onPickerVisible"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div class="activate-hint">{{ hint }}</div>
|
||||
@@ -28,6 +26,8 @@
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import { isImportFreshBuildMode } from '@/views/price/set/utils/priceSet'
|
||||
|
||||
const props = defineProps({
|
||||
loadVersions: { type: Function, required: true },
|
||||
activateFn: { type: Function, required: true }
|
||||
@@ -39,31 +39,43 @@ const submitting = ref(false)
|
||||
const formRef = ref()
|
||||
const versions = ref([])
|
||||
const prevEff = ref()
|
||||
const form = reactive({ psvId: undefined, versionNo: '', versionSeq: 0, effectiveTime: undefined })
|
||||
const isImportFresh = ref(false)
|
||||
const boundaryTimes = ref([])
|
||||
const form = reactive({ psvId: undefined, versionNo: '', versionSeq: 0, effectiveTime: undefined, priceBuildMode: undefined })
|
||||
const dialogTitle = '启动版本'
|
||||
const labNo = '版本号'
|
||||
const labEff = '生效时间'
|
||||
const ph = '请选择生效时间'
|
||||
const btnCancel = '取消'
|
||||
const btnOk = '确认启动'
|
||||
const beforePrev = '生效时间不能早于上一版本的生效时间'
|
||||
const hitBoundary = '生效时间不能与已有版本的生效时间或失效时间相同'
|
||||
const hint = computed(() => {
|
||||
if (isImportFresh.value) {
|
||||
return '重新建价:生效时间可落在历史版本区间内;落在版本 B 区间时,B 失效时间改为本次启动时间,本版本失效时间取 B 原失效时间,并同步产品/明细。不可与本类型已有版本的生效时间或失效时间相同。'
|
||||
}
|
||||
const base = '若生效时间晚于当前,版本将进入待生效;到时自动生效并使上一版本失效。'
|
||||
return prevEff.value ? (base + '不得早于上一版:' + prevEff.value + '。') : base
|
||||
})
|
||||
const rules = {
|
||||
const rules = computed(() => ({
|
||||
effectiveTime: [
|
||||
{ required: true, message: ph, trigger: 'change' },
|
||||
{
|
||||
validator: (_r, v, cb) => {
|
||||
if (!v || !prevEff.value) return cb()
|
||||
if (toMs(v) < toMs(prevEff.value)) return cb(new Error(beforePrev))
|
||||
if (!v) return cb()
|
||||
if (isImportFresh.value) {
|
||||
const ms = toMs(v)
|
||||
if (boundaryTimes.value.some(b => toMs(b) === ms)) {
|
||||
return cb(new Error(hitBoundary))
|
||||
}
|
||||
return cb()
|
||||
}
|
||||
if (prevEff.value && toMs(v) < toMs(prevEff.value)) return cb(new Error(beforePrev))
|
||||
cb()
|
||||
},
|
||||
trigger: 'change'
|
||||
}
|
||||
]
|
||||
}
|
||||
}))
|
||||
function pad2(n) { return String(n).padStart(2, '0') }
|
||||
function formatHour(d) {
|
||||
if (!d || Number.isNaN(d.getTime())) return undefined
|
||||
@@ -93,14 +105,29 @@ function findPrev(row) {
|
||||
}
|
||||
return prev ? normalizeToHour(prev.effectiveTime) : undefined
|
||||
}
|
||||
function pickDefault(prev) {
|
||||
function collectBoundaryTimes(excludePsvId) {
|
||||
const out = []
|
||||
const seen = new Set()
|
||||
for (const v of versions.value || []) {
|
||||
if (!v || v.psvId == null || String(v.psvId) === String(excludePsvId)) continue
|
||||
;[v.effectiveTime, v.expireTime].forEach(t => {
|
||||
const n = t ? normalizeToHour(t) : ''
|
||||
if (!n || seen.has(n)) return
|
||||
seen.add(n)
|
||||
out.push(n)
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
function pickDefault(prev, importFresh) {
|
||||
const d = new Date()
|
||||
d.setHours(9, 0, 0, 0)
|
||||
const now = formatHour(d)
|
||||
if (!prev) return now
|
||||
if (importFresh || !prev) return now
|
||||
return toMs(now) >= toMs(prev) ? now : prev
|
||||
}
|
||||
function disabledDate(date) {
|
||||
if (isImportFresh.value) return false
|
||||
if (!prevEff.value || !date) return false
|
||||
const prevDay = new Date(String(prevEff.value).replace(/-/g, '/'))
|
||||
prevDay.setHours(0, 0, 0, 0)
|
||||
@@ -109,11 +136,25 @@ function disabledDate(date) {
|
||||
return d.getTime() < prevDay.getTime()
|
||||
}
|
||||
function disabledHours() {
|
||||
if (!prevEff.value) return []
|
||||
const prevD = new Date(String(prevEff.value).replace(/-/g, '/'))
|
||||
const curD = form.effectiveTime
|
||||
? new Date(String(normalizeToHour(form.effectiveTime)).replace(/-/g, '/'))
|
||||
: new Date()
|
||||
if (isImportFresh.value) {
|
||||
const hours = new Set()
|
||||
for (const b of boundaryTimes.value) {
|
||||
const bd = new Date(String(b).replace(/-/g, '/'))
|
||||
if (
|
||||
bd.getFullYear() === curD.getFullYear() &&
|
||||
bd.getMonth() === curD.getMonth() &&
|
||||
bd.getDate() === curD.getDate()
|
||||
) {
|
||||
hours.add(bd.getHours())
|
||||
}
|
||||
}
|
||||
return [...hours]
|
||||
}
|
||||
if (!prevEff.value) return []
|
||||
const prevD = new Date(String(prevEff.value).replace(/-/g, '/'))
|
||||
if (prevD.getFullYear() !== curD.getFullYear() || prevD.getMonth() !== curD.getMonth() || prevD.getDate() !== curD.getDate()) return []
|
||||
const hours = []
|
||||
for (let h = 0; h < prevD.getHours(); h++) hours.push(h)
|
||||
@@ -123,6 +164,9 @@ function onTimeChange(val) {
|
||||
form.effectiveTime = normalizeToHour(val)
|
||||
formRef.value && formRef.value.validateField('effectiveTime')
|
||||
}
|
||||
function onPickerVisible(visible) {
|
||||
if (visible && formRef.value) formRef.value.validateField('effectiveTime')
|
||||
}
|
||||
async function open(row) {
|
||||
if (!row || row.psvId == null) return
|
||||
const list = await props.loadVersions()
|
||||
@@ -131,15 +175,21 @@ async function open(row) {
|
||||
form.psvId = found.psvId
|
||||
form.versionNo = found.versionNo || ''
|
||||
form.versionSeq = found.versionSeq
|
||||
form.priceBuildMode = found.priceBuildMode
|
||||
isImportFresh.value = isImportFreshBuildMode(found.priceBuildMode)
|
||||
prevEff.value = findPrev(found)
|
||||
form.effectiveTime = pickDefault(prevEff.value)
|
||||
boundaryTimes.value = isImportFresh.value ? collectBoundaryTimes(found.psvId) : []
|
||||
form.effectiveTime = pickDefault(prevEff.value, isImportFresh.value)
|
||||
visible.value = true
|
||||
}
|
||||
function onClosed() {
|
||||
form.psvId = undefined
|
||||
form.versionNo = ''
|
||||
form.effectiveTime = undefined
|
||||
form.priceBuildMode = undefined
|
||||
prevEff.value = undefined
|
||||
isImportFresh.value = false
|
||||
boundaryTimes.value = []
|
||||
formRef.value && formRef.value.clearValidate()
|
||||
}
|
||||
function submit() {
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
<div class="build-choice__cards">
|
||||
<button
|
||||
type="button"
|
||||
class="build-card build-card--pull"
|
||||
:class="{ selected: buildMode === 'COPY' }"
|
||||
class="build-card build-card--import"
|
||||
:class="{ selected: buildMode === 'IMPORT' }"
|
||||
:disabled="!!psvId || choosing || finishing"
|
||||
@click="chooseMode(true)"
|
||||
>
|
||||
@@ -39,8 +39,8 @@
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="build-card build-card--import"
|
||||
:class="{ selected: buildMode === 'IMPORT' }"
|
||||
class="build-card build-card--import-fresh"
|
||||
:class="{ selected: buildMode === 'IMPORT_FRESH' }"
|
||||
:disabled="!!psvId || choosing || finishing"
|
||||
@click="chooseMode(false)"
|
||||
>
|
||||
@@ -183,7 +183,7 @@ const route = useRoute()
|
||||
const labTip = '创建价格方式'
|
||||
const labModeCopy = '按现有生效版本建价'
|
||||
const labModeCopyDesc = '先本地预览已生效版本产品,点击「完成」时再复制到待生效草稿'
|
||||
const labModeFresh = '重新建产品价'
|
||||
const labModeFresh = '新建产品价'
|
||||
const labModeFreshDesc = "不沿用已生效版本;导入解析后先展示,点「完成」时再写入草稿"
|
||||
const labImport = '导入 Excel 建价'
|
||||
const labImportAll = '全部导入(多国家价表)'
|
||||
@@ -300,9 +300,10 @@ function applyVersion(ver) {
|
||||
}
|
||||
psvId.value = ver.psvId
|
||||
versionNo.value = ver.versionNo || ''
|
||||
// Catalog: IMPORT = 沿用生效版; IMPORT_FRESH = 重新建价. Legacy COPY → IMPORT.
|
||||
const mode = String(ver.priceBuildMode || '').toUpperCase()
|
||||
if (mode === 'COPY') buildMode.value = 'COPY'
|
||||
else if (mode === 'IMPORT' || mode === 'PULL') buildMode.value = 'IMPORT'
|
||||
if (mode === 'IMPORT' || mode === 'COPY') buildMode.value = 'IMPORT'
|
||||
else if (mode === 'IMPORT_FRESH' || mode === 'PULL') buildMode.value = 'IMPORT_FRESH'
|
||||
}
|
||||
|
||||
async function loadProducts() {
|
||||
@@ -329,7 +330,7 @@ async function bootstrap() {
|
||||
psvId.value = Number(qPsv)
|
||||
versionNo.value = (route.query && route.query.versionNo) ? String(route.query.versionNo) : ''
|
||||
editingExisting.value = true
|
||||
if (!buildMode.value) buildMode.value = 'IMPORT'
|
||||
if (!buildMode.value) buildMode.value = 'IMPORT_FRESH'
|
||||
await loadProducts()
|
||||
}
|
||||
} finally {
|
||||
@@ -337,10 +338,10 @@ async function bootstrap() {
|
||||
}
|
||||
}
|
||||
|
||||
/** Mode select: local only, no DB write. */
|
||||
/** Mode select: local only, no DB write. inherit=IMPORT, fresh=IMPORT_FRESH. */
|
||||
async function chooseMode(copyFromLatest) {
|
||||
if (psvId.value || choosing.value || finishing.value) return
|
||||
const next = copyFromLatest ? 'COPY' : 'IMPORT'
|
||||
const next = copyFromLatest ? 'IMPORT' : 'IMPORT_FRESH'
|
||||
if (buildMode.value === next && (copyFromLatest ? rows.value.length : true)) {
|
||||
if (copyFromLatest && rows.value.length) return
|
||||
if (!copyFromLatest && !rows.value.length && !pendingImports.value.length) return
|
||||
@@ -426,7 +427,7 @@ async function persistDraft() {
|
||||
proxy.$modal.msgWarning('请先选择建价方式')
|
||||
return false
|
||||
}
|
||||
const res = await props.prepareFn(buildMode.value === 'COPY')
|
||||
const res = await props.prepareFn(buildMode.value === 'IMPORT')
|
||||
applyVersion(res.data)
|
||||
return !!psvId.value
|
||||
}
|
||||
@@ -682,8 +683,10 @@ onMounted(() => { bootstrap() })
|
||||
.build-card:hover:not(:disabled) { box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06); }
|
||||
.build-card:disabled { opacity: 0.55; cursor: not-allowed; }
|
||||
.build-card__title { font-size: 16px; font-weight: 600; color: #303133; margin-bottom: 10px; }
|
||||
.build-card--pull .build-card__title { color: #67c23a; }
|
||||
.build-card--import .build-card__title { color: #409eff; }
|
||||
.build-card--import-fresh .build-card__title { color: #e6a23c; }
|
||||
.build-card--import:hover:not(:disabled) { border-color: #409eff; }
|
||||
.build-card--import-fresh:hover:not(:disabled) { border-color: #e6a23c; }
|
||||
.build-card__desc { font-size: 13px; line-height: 1.6; color: #909399; }
|
||||
.filters { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
|
||||
.filters__left { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
placeholder="客户名称/客户代码/对外代码"
|
||||
multiple
|
||||
remote-search
|
||||
option-select
|
||||
/>
|
||||
</template>
|
||||
<template #search-productCodes="{ model }">
|
||||
@@ -124,7 +125,7 @@
|
||||
@click="openBatchFee"
|
||||
>批量增加费用</el-button>
|
||||
</el-col>
|
||||
<!-- <el-col :span="1.5">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@@ -132,7 +133,7 @@
|
||||
v-hasPermi="['price:billing:chargeWeightEdit']"
|
||||
@click="openChargeWeightImport"
|
||||
>批量修改计费重</el-button>
|
||||
</el-col> -->
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="info"
|
||||
@@ -163,14 +164,14 @@
|
||||
</template>
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" icon="View" @click="handleView(row)">查看</el-button>
|
||||
<!-- <el-button
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
:disabled="row.refundStatus === 'REFUNDED'"
|
||||
v-hasPermi="['price:billing:chargeWeightEdit']"
|
||||
@click="openEditChargeWeight(row)"
|
||||
>改计费重</el-button> -->
|
||||
>改计费重</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@@ -460,7 +461,7 @@ const columns = [
|
||||
{ label: '销售员', prop: 'belongSaler', minWidth: 100, showOverflowTooltip: true },
|
||||
{ label: '结算员', prop: 'settlementMan', minWidth: 100, showOverflowTooltip: true },
|
||||
{ label: '客服员', prop: 'serviceMan', minWidth: 100, showOverflowTooltip: true },
|
||||
{ label: '操作', prop: 'action', align: 'center', width: 150, fixed: 'right', slot: 'action', showInToolbar: false, showOverflowTooltip: false }
|
||||
{ label: '操作', prop: 'action', align: 'center', width: 240, fixed: 'right', slot: 'action', showInToolbar: false, showOverflowTooltip: false }
|
||||
]
|
||||
|
||||
const arrearsStatusOptions = [
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
placeholder="客户名称/客户代码/对外代码"
|
||||
multiple
|
||||
remote-search
|
||||
option-select
|
||||
/>
|
||||
</template>
|
||||
<template #search-orderStatuses="{ model }">
|
||||
@@ -244,7 +245,7 @@ const columns = [
|
||||
{ label: '订单状态', prop: 'orderStatus', align: 'center', width: 110, slot: 'orderStatus' },
|
||||
{ label: '计费状态', prop: 'billingFailStatus', align: 'center', width: 140, slot: 'billingFailStatus' },
|
||||
{ label: '计费时间', prop: 'bizDate', align: 'center', width: 170, parseTime: true },
|
||||
{ label: '下单时间', prop: 'orderTime', align: 'center', width: 170, parseTime: true },
|
||||
// { label: '下单时间', prop: 'orderTime', align: 'center', width: 170, parseTime: true },
|
||||
{ label: '提货时间', prop: 'pickupDate', align: 'center', width: 170, parseTime: true },
|
||||
{ label: '销售员', prop: 'belongSaler', minWidth: 100, showOverflowTooltip: true },
|
||||
{ label: '结算员', prop: 'settlementMan', minWidth: 100, showOverflowTooltip: true },
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
:disabled="!canChooseBuildMode || !isFixedType(setType)"
|
||||
@click="chooseBuildMode('IMPORT_FRESH')"
|
||||
>
|
||||
<div class="build-card__title">Excel 重新建价</div>
|
||||
<div class="build-card__title">Excel 新建价</div>
|
||||
<div class="build-card__desc">不延用以前的产品价格,不带出上一版,上传 Excel 直接生成套餐销售价</div>
|
||||
</button>
|
||||
</div>
|
||||
@@ -227,7 +227,7 @@ import {
|
||||
surchargeAppliesToRow,
|
||||
toDetailPayload
|
||||
} from '../../utils/priceSet'
|
||||
import { getPriceSet, listSetDetailByProduct, importSetDetailPricesAsync, pullSetDetailsFromLibraryAsync, previewPullFromLibrary, previewImportDetailPrices, updateSetDetail, updateSetDetailSurchargeAsync, delSetDetail } from '@/api/price/set'
|
||||
import { getPriceSet, listSetDetailByProduct, listSetDetailByVersion, importSetDetailPricesAsync, pullSetDetailsFromLibraryAsync, previewPullFromLibrary, previewImportDetailPrices, updateSetDetail, updateSetDetailSurchargeAsync, delSetDetail } from '@/api/price/set'
|
||||
import { listRegionOptions } from '@/api/data/region'
|
||||
|
||||
const props = defineProps({
|
||||
@@ -338,7 +338,7 @@ const importDesc = computed(() => {
|
||||
return [
|
||||
'适用威狮B2C小包价表(含「产品目录」)。',
|
||||
'不延用以前的产品价格;导入按「产品+国家」写入套餐销售价,直接生成产品明细。',
|
||||
'同一产品下重复国家+重量段将整单失败。仅固定价套餐「Excel 重新建价」可用。'
|
||||
'同一产品下重复国家+重量段将整单失败。仅固定价套餐「Excel 新建价」可用。'
|
||||
]
|
||||
}
|
||||
return [
|
||||
@@ -351,7 +351,7 @@ const importDesc = computed(() => {
|
||||
const typeHint = computed(() => {
|
||||
if (isPullBuildMode(props.priceBuildMode)) return '· 按产品价建价'
|
||||
if (isImportBuildMode(props.priceBuildMode)) return '· 按上一版 Excel 建价'
|
||||
if (isImportFreshBuildMode(props.priceBuildMode)) return '· Excel 重新建价'
|
||||
if (isImportFreshBuildMode(props.priceBuildMode)) return '· Excel 新建价'
|
||||
if (hasBuildMode.value) return '· 当前:' + setTypeLabel(props.setType)
|
||||
return '· 请选择创建价格方式'
|
||||
})
|
||||
@@ -437,12 +437,8 @@ async function loadLatestPriorVersionDraft() {
|
||||
return row
|
||||
})
|
||||
if (!products.length) return false
|
||||
const chunks = await Promise.all(products.map(p =>
|
||||
listSetDetailByProduct(prior.psvId, p.productCode)
|
||||
.then(r => r.data || [])
|
||||
.catch(() => [])
|
||||
))
|
||||
applyLocalDraft(products, chunks.flat())
|
||||
const detailRes = await listSetDetailByVersion(prior.psvId)
|
||||
applyLocalDraft(products, detailRes.data || [])
|
||||
return true
|
||||
} finally {
|
||||
pulling.value = false
|
||||
|
||||
@@ -123,9 +123,21 @@ function toEffMs(v) {
|
||||
return Number.isNaN(t) ? NaN : t
|
||||
}
|
||||
|
||||
/** Sort by effective time descending; drafts without time last. */
|
||||
function sortByEffectiveTime(list) {
|
||||
/** draft / pending first, then effect, then expired; within group by effective time desc. */
|
||||
function statusRank(v) {
|
||||
const s = String(v && v.status != null ? v.status : '')
|
||||
if (s === '0') return 0
|
||||
if (s === '3') return 1
|
||||
if (s === '1') return 2
|
||||
if (s === '2') return 3
|
||||
return 9
|
||||
}
|
||||
|
||||
function sortVersions(list) {
|
||||
return (list || []).slice().sort((a, b) => {
|
||||
const ra = statusRank(a)
|
||||
const rb = statusRank(b)
|
||||
if (ra !== rb) return ra - rb
|
||||
const ta = toEffMs(a)
|
||||
const tb = toEffMs(b)
|
||||
const aNull = Number.isNaN(ta)
|
||||
@@ -143,7 +155,7 @@ const filteredVersions = computed(() => {
|
||||
const filtered = statusTab.value === 'all'
|
||||
? list
|
||||
: list.filter(v => String(v.status) === statusTab.value)
|
||||
return sortByEffectiveTime(filtered)
|
||||
return sortVersions(filtered)
|
||||
})
|
||||
|
||||
function detailCount(row) {
|
||||
|
||||
@@ -235,8 +235,8 @@ const activateEffHitBoundaryMsg = '生效时间不能与已有版本的生效时
|
||||
const activateDialogHint = computed(() => {
|
||||
if (activateIsImportFresh.value) {
|
||||
const base = activateMode.value === 'reschedule'
|
||||
? 'Excel 重新建价:生效时间可落在历史版本区间内,将自动截断该版本失效时间并同步产品/明细。'
|
||||
: 'Excel 重新建价:生效时间可落在历史版本区间内;落在版本 B 区间时,B 失效时间改为本次启动时间,本版本失效时间取 B 原失效时间,并同步双方产品/明细。'
|
||||
? 'Excel 新建价:生效时间可落在历史版本区间内,将自动截断该版本失效时间并同步产品/明细。'
|
||||
: 'Excel 新建价:生效时间可落在历史版本区间内;落在版本 B 区间时,B 失效时间改为本次启动时间,本版本失效时间取 B 原失效时间,并同步双方产品/明细。'
|
||||
return base + '不可与已有版本的生效时间或失效时间相同。'
|
||||
}
|
||||
const base = activateMode.value === 'reschedule'
|
||||
|
||||
Reference in New Issue
Block a user