修改数字
This commit is contained in:
commit
17a21c4cb4
@ -0,0 +1,5 @@
|
||||
---
|
||||
name: 修复BatchPushProducts并生成测试变量
|
||||
overview: 修复 service/product.go 中 BatchPushProducts 方法的编译错误(strconv.Itoa → strconv.Itoa),并生成测试用的请求变量(UserId=1965254774327533570,包含 shop_ids 和 product_ids)
|
||||
---
|
||||
|
||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
9
.idea/cards.iml
generated
Normal file
9
.idea/cards.iml
generated
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/cards.iml" filepath="$PROJECT_DIR$/.idea/cards.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
610
PSI项目说明文档.md
Normal file
610
PSI项目说明文档.md
Normal file
@ -0,0 +1,610 @@
|
||||
# PSI 进销存系统 - 项目说明文档
|
||||
|
||||
## 一、项目概述
|
||||
|
||||
PSI 是一套基于 Go 语言开发的完整进销存管理系统,涵盖采购、销售、库存、仓库、物流等核心业务环节,支持波次(Wave)管理、OCR 识别、外部系统对接等企业级功能。
|
||||
|
||||
- **项目名称**:PSI 进销存系统
|
||||
- **技术栈**:Go 1.25.7 + Gin + GORM + MySQL
|
||||
- **服务端口**:`9090`
|
||||
- **项目路径**:`D:\www\wwwroot\psi`
|
||||
|
||||
---
|
||||
|
||||
## 二、技术架构
|
||||
|
||||
| 组件 | 技术 |
|
||||
|------|------|
|
||||
| Web 框架 | [Gin v1.11.0](https://github.com/gin-gonic/gin) |
|
||||
| ORM | [GORM v1.31.1](https://gorm.io/) + MySQL Driver |
|
||||
| 认证 | JWT(golang-jwt/jwt/v5) |
|
||||
| 日志 | Logrus + file-rotatelogs(按时间轮转) |
|
||||
| 搜索引擎 | Elasticsearch v8(可选,当前已注释) |
|
||||
| OCR | 本地 OCR 服务(可启动独立进程) |
|
||||
| Excel 导出 | Excelize v2 |
|
||||
| 条形码 | boombuler/barcode(二维码/条形码生成) |
|
||||
| 图像处理 | golang.org/x/image |
|
||||
| 加密 | golang.org/x/crypto |
|
||||
|
||||
---
|
||||
|
||||
## 三、目录结构
|
||||
|
||||
```
|
||||
psi/
|
||||
├── main.go # 程序入口
|
||||
├── config.yaml # 配置文件
|
||||
├── go.mod / go.sum # Go 依赖管理
|
||||
├── config/ # 配置加载模块
|
||||
├── constant/ # 常量定义(日志通道等)
|
||||
├── controllers/ # HTTP 控制器层(处理请求,32个)
|
||||
├── middleware/ # 中间件(JWT 认证、CORS、API 签名)
|
||||
├── models/ # 数据模型层
|
||||
│ ├── request/ # 请求参数结构体
|
||||
│ ├── response/ # 响应结构体
|
||||
│ ├── book_info.go # 图书信息模型
|
||||
│ ├── employee.go # 员工模型
|
||||
│ └── ... # 其他业务模型(共41个表)
|
||||
├── service/ # 业务逻辑层
|
||||
├── database/ # 数据库连接初始化
|
||||
├── routes/ # 路由定义
|
||||
├── utils/ # 工具函数(JWT、加密等)
|
||||
├── es/ # Elasticsearch 初始化(可选)
|
||||
├── ocr/ # OCR 服务
|
||||
├── template/ # 模板文件
|
||||
├── fonts/ # 字体文件
|
||||
├── linux/ # Linux 部署文件
|
||||
├── runtime/ # 运行时文件(日志等)
|
||||
├── .idea/ # IDE 配置
|
||||
└── sql.txt # 数据库补充脚本
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、数据库表关系
|
||||
|
||||
### 4.1 表分类统计
|
||||
|
||||
| 模块 | 表数量 | 说明 |
|
||||
|-----|-------|------|
|
||||
| 基础信息 | 10 | 员工、供应商、仓库、库位、商品、店铺、客户等 |
|
||||
| 库存管理 | 5 | 库存、库存明细、库存流水、盘库等 |
|
||||
| 采购管理 | 4 | 采购订单、采购明细、入库单、入库明细 |
|
||||
| 销售出库 | 7 | 销售订单、销售明细、出库单、出库明细、发货单等 |
|
||||
| 波次管理 | 3 | 波次主表、波次任务、波次任务明细 |
|
||||
| 其他 | 12 | 小车、外部任务、打印、分账、统计等 |
|
||||
| **总计** | **41** | |
|
||||
|
||||
### 4.2 核心表结构
|
||||
|
||||
#### (1)基础信息模块
|
||||
|
||||
| 表名 | 说明 | 关键字段 |
|
||||
|-----|------|---------|
|
||||
| **employees** | 员工表 | id, username, role, status, expire_time |
|
||||
| **supplier** | 供应商表 | id, code, name, status |
|
||||
| **warehouse** | 仓库表 | id, code, name, logistics_id, type, status |
|
||||
| **location** | 库位表 | id, warehouse_id, code, type, capacity, status |
|
||||
| **product** | 商品表 | id, category_id, barcode, name, price, status |
|
||||
| **shop** | 店铺表 | id, mall_id, shop_type, shop_alias_name, status |
|
||||
| **customer** | 客户表 | - |
|
||||
| **logistics** | 物流模板表 | - |
|
||||
|
||||
#### (2)库存模块
|
||||
|
||||
| 表名 | 说明 | 关键字段 |
|
||||
|-----|------|---------|
|
||||
| **inventory** | 库存汇总表 | id, warehouse_id, product_id, batch_no, quantity, locked_quantity |
|
||||
| **inventory_log** | 库存流水表 | id, warehouse_id, product_id, location_id, change_type, change_quantity, related_order_no |
|
||||
|
||||
#### (3)采购模块
|
||||
|
||||
| 表名 | 说明 | 关键字段 |
|
||||
|-----|------|---------|
|
||||
| **purchase_order** | 采购订单主表 | id, po_no, supplier_id, warehouse_id, total_amount, status |
|
||||
| **receiving_order** | 入库单主表 | id, receiving_no, purchase_order_id, wave_task_id, warehouse_id, status |
|
||||
|
||||
#### (4)销售与出库模块
|
||||
|
||||
| 表名 | 说明 | 关键字段 |
|
||||
|-----|------|---------|
|
||||
| **sales_order** | 销售订单主表 | id, so_no, customer_id, warehouse_id, shop_type, total_amount, status, is_distribution |
|
||||
| **outbound_order** | 出库单主表 | id, out_no, wave_task_id, warehouse_id, total_quantity, status |
|
||||
| **shipping_order** | 发货单主表 | id, shipping_no, customer_id, status, shipping_time |
|
||||
|
||||
#### (5)波次管理模块
|
||||
|
||||
| 表名 | 说明 | 关键字段 |
|
||||
|-----|------|---------|
|
||||
| **wave_header** | 波次主表 | id, wave_no, direction, type, warehouse_id, related_order_id, status |
|
||||
| **wave_task** | 波次任务表 | - |
|
||||
| **wave_task_detail** | 波次任务明细表 | - |
|
||||
|
||||
### 4.3 表关系图谱
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ employees │
|
||||
└──────┬──────┘
|
||||
│ 创建/操作
|
||||
↓
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ 核心业务流程 │
|
||||
├───────────────────────────────────────────────────────────────────┤
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ supplier │─────▶│purchase_order│─────▶│ receiving │ │
|
||||
│ │ (供应商) │ │ (采购单) │ │ (入库单) │ │
|
||||
│ └──────────────┘ └──────┬───────┘ └──────┬───────┘ │
|
||||
│ │ │ │
|
||||
│ ┌──────────────┐ │ │ │
|
||||
│ │ product │◀──────────┘ │ │
|
||||
│ │ (商品) │ │ │
|
||||
│ └──────┬───────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────────┐ │ │
|
||||
│ └─────────────▶│ inventory │◀───────────┘ │
|
||||
│ │ (库存) │ │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ ┌──────────────┐ │ ┌──────────────┐ │
|
||||
│ │ customer │ │ │ sales │ │
|
||||
│ │ (客户) │◀───────────┴───────────▶│ (销售单) │ │
|
||||
│ └──────────────┘ └──────┬───────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ │ │
|
||||
│ │ shop │ │ outbound │◀───────┘ │
|
||||
│ │ (店铺) │◀────▶│ (出库单) │ │
|
||||
│ └──────────────┘ └──────┬───────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ shipping │ │
|
||||
│ │ (发货单) │ │
|
||||
│ └──────────────┘ │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
↑
|
||||
│
|
||||
┌──────────────┐
|
||||
│ wave_header │
|
||||
│ (波次) │
|
||||
└──────┬───────┘
|
||||
│
|
||||
┌──────────────┐
|
||||
│ wave_task │
|
||||
│ (波次任务) │
|
||||
└──────────────┘
|
||||
↑
|
||||
│
|
||||
┌──────────────┐
|
||||
│ car │
|
||||
│ (小车) │
|
||||
└──────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 基础信息层 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ warehouse│ │ location │ │logistics │ │
|
||||
│ │ (仓库) │───▶│ (库位) │ │(物流模板)│ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4.4 关键表关系详细说明
|
||||
|
||||
#### 采购入库流程关系
|
||||
|
||||
```
|
||||
供应商(supplier) → 采购订单(purchase_order)
|
||||
↓
|
||||
波次(wave_header)
|
||||
↓
|
||||
波次任务(wave_task)
|
||||
↓
|
||||
入库单(receiving_order)
|
||||
↓
|
||||
库存增加(inventory + inventory_log)
|
||||
```
|
||||
|
||||
- **purchase_order.supplier_id** → supplier.id
|
||||
- **purchase_order.warehouse_id** → warehouse.id
|
||||
- **receiving_order.purchase_order_id** → purchase_order.id
|
||||
- **receiving_order.wave_task_id** → wave_task.id
|
||||
- **inventory_log.related_order_no** → purchase_order.po_no 或 receiving_order.receiving_no
|
||||
|
||||
#### 销售出库流程关系
|
||||
|
||||
```
|
||||
客户(customer) → 销售订单(sales_order)
|
||||
↓
|
||||
波次(wave_header)
|
||||
↓
|
||||
波次任务(wave_task)
|
||||
↓
|
||||
出库单(outbound_order)
|
||||
↓
|
||||
库存减少(inventory + inventory_log)
|
||||
↓
|
||||
发货单(shipping_order)
|
||||
```
|
||||
|
||||
- **sales_order.customer_id** → customer.id
|
||||
- **sales_order.warehouse_id** → warehouse.id
|
||||
- **sales_order.sales_person_id** → shop.id
|
||||
- **outbound_order.wave_task_id** → wave_task.id
|
||||
- **outbound_order.warehouse_id** → warehouse.id
|
||||
- **inventory_log.related_order_no** → sales_order.so_no 或 outbound_order.out_no
|
||||
|
||||
#### 库存管理关系
|
||||
|
||||
```
|
||||
warehouse(仓库) ──┐
|
||||
├─→ inventory(库存) ←─ product(商品)
|
||||
location(库位) ──┘ ↑
|
||||
│
|
||||
└─ inventory_log(库存流水)
|
||||
```
|
||||
|
||||
- **inventory.warehouse_id** → warehouse.id
|
||||
- **inventory.product_id** → product.id
|
||||
- **inventory_log.warehouse_id** → warehouse.id
|
||||
- **inventory_log.location_id** → location.id
|
||||
- **inventory_log.product_id** → product.id
|
||||
- **location.warehouse_id** → warehouse.id
|
||||
|
||||
---
|
||||
|
||||
## 五、核心业务模块
|
||||
|
||||
### 5.1 认证与权限
|
||||
|
||||
| 功能 | 说明 |
|
||||
|------|------|
|
||||
| JWT 认证 | 基于 Bearer Token,有效期 24 小时 |
|
||||
| 角色权限 | 普通员工(role≠255)/ 管理员(role=255) |
|
||||
| API 签名 | MD5 签名验证,防篡改(app_key + app_secret) |
|
||||
| 账号过期 | 支持设置员工账号过期时间 |
|
||||
|
||||
### 5.2 员工管理(管理员接口)
|
||||
|
||||
- 员工列表 / 新增 / 修改密码 / 设置级别
|
||||
- 设置账号过期时间 / 查看过期状态
|
||||
- PDA 配置(用于手持设备)
|
||||
- 员工操作日志
|
||||
|
||||
### 5.3 供应商管理
|
||||
|
||||
- 供应商增删改查
|
||||
- 供应商详情查看
|
||||
|
||||
### 5.4 仓库与库位管理
|
||||
|
||||
- 仓库增删改查
|
||||
- 库位增删改查
|
||||
- **库位批量生成**(按规则批量创建库位编号)
|
||||
- 库位同步外部系统
|
||||
|
||||
### 5.5 商品管理
|
||||
|
||||
- 商品列表(支持分页、筛选)
|
||||
- 商品新增 / 修改(支持图片上传)
|
||||
- **商品售价批量修改**(公开接口,支持外部调用)
|
||||
- 商品导出到 Excel
|
||||
- 商品库存查询(按库位/仓库聚合)
|
||||
- 商品日志(记录价格/信息变更,支持审核流程)
|
||||
- 图书信息获取(对接外部图书 API)
|
||||
- 条形码 / 二维码生成
|
||||
|
||||
### 5.6 采购流程(波次模式)
|
||||
|
||||
```
|
||||
创建采购单 → 生成采购波次 → 释放波次(生成波次任务)
|
||||
↓
|
||||
PDA 扫码收货
|
||||
↓
|
||||
提交入库单 → 库存增加
|
||||
```
|
||||
|
||||
- `POST /api/purchase-order/create-with-wave` — 创建采购单并生成波次
|
||||
- `POST /api/wave/release` — 释放波次,生成波次任务明细
|
||||
- `POST /api/receiving/bind-wave` — 绑定波次,创建入库单
|
||||
- `POST /api/receiving/submit` — 提交入库,更新库存
|
||||
- 支持**小车(Car)**容量管理,按车容量分配波次任务
|
||||
|
||||
### 5.7 销售与出库流程
|
||||
|
||||
```
|
||||
创建销售单 → 生成出库波次 → 释放出库波次
|
||||
↓
|
||||
PDA 拣货/出库
|
||||
↓
|
||||
提交出库单 → 库存减少
|
||||
↓
|
||||
创建发货单 → 填写物流信息
|
||||
```
|
||||
|
||||
- `POST /api/sales-order/create` — 创建销售订单(公开接口)
|
||||
- `POST /api/outbound-order/create` — 创建出库单
|
||||
- `POST /api/wave/outbound/create` — 生成出库波次
|
||||
- `POST /api/wave/outbound/release` — 释放出库波次
|
||||
- `POST /api/outbound/bind-wave` — 绑定出库波次
|
||||
- `POST /api/outbound/submit` — 提交出库
|
||||
- `POST /api/shipping-order/create` — 创建发货单
|
||||
- `POST /api/shipping-order/update` — 更新物流信息
|
||||
- `POST /api/sales-order/cancel` — 取消销售单
|
||||
- `POST /api/wave/outbound/cancel` — 取消出库波次
|
||||
|
||||
### 5.8 库存管理
|
||||
|
||||
- 库存列表(按商品/库位/仓库维度)
|
||||
- **库存按商品聚合列表**(同商品不同库位合并显示)
|
||||
- 库存明细(跟踪每笔出入库记录)
|
||||
- 库存变动日志
|
||||
- **盘库调整**(库存盘点,支持盘盈/盘亏调整)
|
||||
- **盘库退货**(将盘点差异商品退回)
|
||||
- 库存统计
|
||||
|
||||
### 5.9 波次任务管理
|
||||
|
||||
- 波次任务列表(按采购/出库类型)
|
||||
- 波次任务详情
|
||||
- 波次任务进度跟踪
|
||||
|
||||
### 5.10 店铺与外部对接
|
||||
|
||||
- 店铺管理(增删改查)
|
||||
- **外部任务(OutTask)**:与外部系统同步任务状态
|
||||
- 按店铺查询外部任务
|
||||
- 外部任务日志
|
||||
- 同步商品到外部系统(`external_api.sync_product_url`)
|
||||
- 同步任务到外部系统(`external_api.sync_task_url`)
|
||||
|
||||
### 5.11 物流管理
|
||||
|
||||
- 物流模板增删改查
|
||||
- 物流模板详情
|
||||
|
||||
### 5.12 分拣设置
|
||||
|
||||
- 获取分拣配置
|
||||
- 保存分拣配置(影响波次任务分配逻辑)
|
||||
|
||||
### 5.13 仪表盘统计
|
||||
|
||||
- 仪表盘概览统计(`/api/dashboard/statist`)
|
||||
- 各仓库库存统计(`/api/dashboard/warehouse`)
|
||||
- 订单统计(`/api/dashboard/order`)
|
||||
|
||||
### 5.14 OCR 识别
|
||||
|
||||
- `POST /api/ocr` — 图片文字识别(公开接口)
|
||||
- 可启动本地 OCR 服务进程(`OCRService.exe`)
|
||||
|
||||
---
|
||||
|
||||
## 六、API 接口分类
|
||||
|
||||
### 公开接口(无需认证)
|
||||
|
||||
| 接口 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/api/login/:type` | POST | 员工登录 |
|
||||
| `/api/employee/reg` | POST | 员工注册 |
|
||||
| `/api/ocr` | POST | OCR 文字识别 |
|
||||
| `/api/sales-order/create` | POST | 创建销售单 |
|
||||
| `/api/product/updatePrice` | POST | 批量修改商品售价 |
|
||||
| `/api/product/getProductInventory` | GET | 查询商品库存 |
|
||||
| `/api/employee/check_code` | GET | 校验员工码 |
|
||||
| `/api/employee/clear_code` | POST | 清除员工码 |
|
||||
| `/api/out-task-log/update` | POST | 更新外部任务日志 |
|
||||
|
||||
### 签名接口(需要 API 签名,无需 JWT)
|
||||
|
||||
| 接口 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/api/location/sync-locations` | POST | 同步库位到外部系统 |
|
||||
| `/api/goods/sync-batch` | POST | 批量同步商品到外部系统 |
|
||||
|
||||
### 需认证接口(JWT + API 签名)
|
||||
|
||||
大部分业务接口需要 `Authorization: Bearer <token>` 请求头,以及 API 签名验证。
|
||||
|
||||
### 管理员接口(需要 role=255)
|
||||
|
||||
员工管理、用户类型管理的完整 CRUD。
|
||||
|
||||
---
|
||||
|
||||
## 七、配置说明(config.yaml)
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: "9090" # 服务监听端口
|
||||
host: "http://192.168.101.213:9090/" # 服务外部访问地址
|
||||
|
||||
database:
|
||||
host: localhost
|
||||
port: "3306"
|
||||
user: root
|
||||
password: root
|
||||
name: psi # 数据库名
|
||||
encrypt_key: "..." # 敏感字段加密密钥(AES)
|
||||
|
||||
jwt:
|
||||
secret: "..." # JWT 签名密钥
|
||||
expire_hours: 24 # Token 有效期
|
||||
|
||||
api_sign:
|
||||
app_key: "psi"
|
||||
app_secret: "psi_api_sign_secret" # API 签名密钥
|
||||
timestamp_tolerance: 300 # 时间戳容差(秒)
|
||||
|
||||
log:
|
||||
max_age: 600 # 日志最大保留时间(秒)
|
||||
rotate_time: 600 # 日志轮转时间间隔(秒)
|
||||
root_path: "./runtime/logs"
|
||||
channel: # 各日志通道文件路径
|
||||
sql: "/sql/err.log"
|
||||
work: "/work/err.log"
|
||||
request: "/request/err.log"
|
||||
es: "/es/err.log"
|
||||
redis: "/redis/err.log"
|
||||
|
||||
ocr:
|
||||
service_url: "http://127.0.0.1:35569/ocr"
|
||||
exe_url: "./ocr/OCRService.exe" # OCR 可执行文件路径
|
||||
|
||||
external_api:
|
||||
sync_product_url: "http://192.168.101.127:8080/zhishu/filterSet/save"
|
||||
sync_task_url: "http://192.168.101.156:8080/task/create"
|
||||
sync_task_body_url: "http://192.168.101.156:8080/task/setTaskBody"
|
||||
timeout: 30
|
||||
```
|
||||
|
||||
> **注意**:Elasticsearch 配置已注释,ES 同步功能当前未启用。
|
||||
|
||||
---
|
||||
|
||||
## 八、数据库模型一览
|
||||
|
||||
| 模型文件 | 说明 |
|
||||
|----------|------|
|
||||
| `employee.go` | 员工/用户信息 |
|
||||
| `employee_level.go` | 员工级别配置 |
|
||||
| `employee_level_log.go` | 员工级别变更日志 |
|
||||
| `employee_settings.go` | 员工 PDA 配置 |
|
||||
| `supplier.go` | 供应商 |
|
||||
| `warehouse.go` | 仓库 |
|
||||
| `location.go` | 库位 |
|
||||
| `product.go` | 商品 |
|
||||
| `product_category.go` | 商品分类 |
|
||||
| `product_unit_conversion.go` | 商品单位换算 |
|
||||
| `product_serial.go` | 商品序列号 |
|
||||
| `product_log.go` | 商品变更日志(审核流) |
|
||||
| `purchase_order.go` | 采购订单 |
|
||||
| `purchase_order_item.go` | 采购订单明细 |
|
||||
| `receiving_order.go` | 入库单 |
|
||||
| `receiving_order_item.go` | 入库单明细 |
|
||||
| `sales_order.go` | 销售订单 |
|
||||
| `sales_order_item.go` | 销售订单明细 |
|
||||
| `outbound_order.go` | 出库单 |
|
||||
| `outbound_order_item.go` | 出库单明细 |
|
||||
| `shipping_order.go` | 发货单 |
|
||||
| `inventory.go` | 库存 |
|
||||
| `inventory_detail.go` | 库存明细 |
|
||||
| `inventory_log.go` | 库存变动日志 |
|
||||
| `stock_check.go` | 盘库单 |
|
||||
| `stock_check_item.go` | 盘库明细 |
|
||||
| `wave_header.go` | 波次头 |
|
||||
| `wave_task.go` | 波次任务 |
|
||||
| `wave_task_detail.go` | 波次任务明细 |
|
||||
| `car.go` | 小车(AGV/搬运车) |
|
||||
| `car_shop.go` | 小车与店铺关联 |
|
||||
| `shop.go` | 店铺 |
|
||||
| `logistics.go` | 物流模板 |
|
||||
| `out_task.go` | 外部任务 |
|
||||
| `out_task_log.go` | 外部任务日志 |
|
||||
| `print_log.go` | 打印日志 |
|
||||
| `print_task.go` | 打印任务 |
|
||||
| `sorting_settings.go` | 分拣设置 |
|
||||
| `statist.go` | 统计数据 |
|
||||
| `customer.go` | 客户 |
|
||||
| `user_type.go` | 用户类型 |
|
||||
| `book_info.go` | 图书信息(对接外部图书 API) |
|
||||
| `split_account_config.go` | 分账配置 |
|
||||
| `split_account_deduction_log.go` | 分账扣钱日志 |
|
||||
| `outbound_order_location_log.go` | 出库单库位变更记录 |
|
||||
|
||||
---
|
||||
|
||||
## 九、启动与运行
|
||||
|
||||
### 本地开发
|
||||
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd D:\www\wwwroot\psi
|
||||
|
||||
# 安装依赖
|
||||
go mod download
|
||||
|
||||
# 运行项目
|
||||
go run main.go
|
||||
```
|
||||
|
||||
服务启动后监听 `http://localhost:9090`。
|
||||
|
||||
### 配置说明
|
||||
|
||||
1. 确保 MySQL 服务运行,并创建 `psi` 数据库
|
||||
2. 根据实际的 MySQL 账号密码修改 `config.yaml`
|
||||
3. 如需启用 OCR,确保 `ocr/OCRService.exe` 存在
|
||||
4. 如需启用 Elasticsearch,取消 `main.go` 中 ES 初始化代码的注释,并配置 `config.yaml` 中的 `es` 节点
|
||||
5. 执行 `sql.txt` 中的补充 SQL 脚本(分账配置、出库单库位变更记录等)
|
||||
|
||||
---
|
||||
|
||||
## 十、中间件说明
|
||||
|
||||
| 中间件文件 | 功能 |
|
||||
|-----------|------|
|
||||
| `middleware/cors.go` | 跨域资源共享(CORS)处理 |
|
||||
| `middleware/auth.go` | JWT 认证 + 员工状态校验 + 租户数据库连接 |
|
||||
| `middleware/sign.go` | API 签名验证(防篡改、防重放攻击) |
|
||||
|
||||
**请求头要求(需认证接口)**:
|
||||
|
||||
```
|
||||
Authorization: Bearer <jwt_token>
|
||||
X-App-Key: psi
|
||||
X-Timestamp: <unix_timestamp>
|
||||
X-Sign: <md5_sign>
|
||||
```
|
||||
|
||||
签名计算方式:`md5(app_key + app_secret + timestamp)`
|
||||
|
||||
---
|
||||
|
||||
## 十一、外部系统集成
|
||||
|
||||
系统支持与以下外部系统对接:
|
||||
|
||||
| 对接功能 | 配置项 | 说明 |
|
||||
|---------|--------|------|
|
||||
| 商品同步 | `sync_product_url` | 将商品信息推送到外部系统 |
|
||||
| 任务创建 | `sync_task_url` | 在外部系统创建任务 |
|
||||
| 任务内容同步 | `sync_task_body_url` | 同步任务详细信息到外部系统 |
|
||||
| 库位同步 | `/api/location/sync-locations` | 将库位信息推送到外部系统 |
|
||||
| 商品批量同步 | `/api/goods/sync-batch` | 批量推送商品到外部系统 |
|
||||
|
||||
---
|
||||
|
||||
## 十二、技术架构亮点
|
||||
|
||||
| 技术特性 | 说明 |
|
||||
|---------|------|
|
||||
| **GORM ORM** | 强大的数据库操作,自动关联查询 |
|
||||
| **JWT 认证** | 无状态认证,有效期 24 小时 |
|
||||
| **API 签名验证** | MD5 签名防篡改、防重放攻击 |
|
||||
| **Elasticsearch** | 可选的搜索引擎集成(代码已注释) |
|
||||
| **OCR 识别** | 本地 OCR 服务进程,支持图片文字识别 |
|
||||
| **Excel 处理** | Excelize 库,支持数据导入导出 |
|
||||
| **条码生成** | boombuler/barcode,支持二维码/条形码 |
|
||||
| **日志轮转** | file-rotatelogs,按时间轮转日志 |
|
||||
| **软删除** | 所有表都支持 IsDel 逻辑删除标记 |
|
||||
| **时间戳** | 使用 Unix 时间戳(秒)而非 datetime |
|
||||
|
||||
---
|
||||
|
||||
## 十三、待完善功能(根据代码注释)
|
||||
|
||||
- ✅ Elasticsearch 同步功能(代码已注释,可启用)
|
||||
- ✅ OCR 服务(代码已注释,可启用)
|
||||
- 外部系统对接 URL 已在配置中,需确认外部系统接口可用性
|
||||
- `main.go` 中 ES 和 OCR 的初始化代码已注释,按需启用
|
||||
|
||||
---
|
||||
|
||||
*文档生成时间:2026-06-05*
|
||||
*项目路径:D:\www\wwwroot\psi*
|
||||
61
config.yaml
Normal file
61
config.yaml
Normal file
@ -0,0 +1,61 @@
|
||||
server:
|
||||
port: "9090"
|
||||
# host: "https://psi.api.buzhiyushu.cn/"
|
||||
host: "http://192.168.101.213:9090/"
|
||||
|
||||
database:
|
||||
host: 175.27.224.66
|
||||
port: "3306"
|
||||
user: root
|
||||
password: 5e07c0eec1770c94
|
||||
name: psi
|
||||
encrypt_key: "0123456789abcdef0123456789abcdef"
|
||||
|
||||
task_database:
|
||||
host: nj-cynosdbmysql-grp-1v6vxn5f.sql.tencentcdb.com
|
||||
port: "26247"
|
||||
user: root
|
||||
password: Long6166@@
|
||||
name: task
|
||||
|
||||
jwt:
|
||||
secret: "0123456789abcdef0123456789abcdef"
|
||||
expire_hours: 24
|
||||
|
||||
api_sign:
|
||||
app_key: "psi"
|
||||
app_secret: "psi_api_sign_secret"
|
||||
client_id: "psi"
|
||||
sign_method: "md5"
|
||||
timestamp_tolerance: 300
|
||||
|
||||
log:
|
||||
max_age: 600
|
||||
rotate_time: 600
|
||||
root_path: "./runtime/logs"
|
||||
channel:
|
||||
sql: "/sql/err.log"
|
||||
work: "/work/err.log"
|
||||
request: "/request/err.log"
|
||||
es: "/es/err.log"
|
||||
redis: "/redis/err.log"
|
||||
|
||||
es:
|
||||
host: "http://36.212.12.92:9527"
|
||||
index: "books-from-mysql-v2"
|
||||
username: "elastic"
|
||||
password: "+Tz5qR_KushZ-bPgZ_H-"
|
||||
|
||||
ocr:
|
||||
service_url: "http://127.0.0.1:35569/ocr"
|
||||
exe_url: "./ocr/OCRService.exe"
|
||||
|
||||
external_api:
|
||||
# sync_product_url: "http://192.168.101.127:8080/zhishu/filterSet/save"
|
||||
sync_product_url: "https://api.buzhiyushu.cn/zhishu/filterSet/save"
|
||||
es_update_book_url: "https://book.center.yushutx.com/api/es/updateBookFieldsByISBN"
|
||||
# sync_task_url: "http://192.168.101.156:8080/task/create"
|
||||
sync_task_url: "http://36.212.7.246:8283/task/create"
|
||||
# sync_task_body_url: "http://192.168.101.156:8080/task/setTaskBody"
|
||||
sync_task_body_url: "http://36.212.7.246:8283/task/setTaskBody"
|
||||
timeout: 30
|
||||
106
config/config.go
Normal file
106
config/config.go
Normal file
@ -0,0 +1,106 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Server ServerConfig `yaml:"server"`
|
||||
Database DatabaseConfig `yaml:"database"`
|
||||
TaskDatabase DatabaseConfig `yaml:"task_database"`
|
||||
JWT JWTConfig `yaml:"jwt"`
|
||||
APISign APISignConfig `yaml:"api_sign"`
|
||||
Log LogConfig `yaml:"log"`
|
||||
ES ESConfig `yaml:"es"`
|
||||
OCR OCRConfig `yaml:"ocr"`
|
||||
ExternalAPI ExternalAPIConfig `yaml:"external_api"`
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
Port string `yaml:"port"`
|
||||
Host string `yaml:"host"`
|
||||
}
|
||||
|
||||
type DatabaseConfig struct {
|
||||
Host string `yaml:"host"`
|
||||
Port string `yaml:"port"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Name string `yaml:"name"`
|
||||
EncryptKey string `yaml:"encrypt_key"`
|
||||
}
|
||||
|
||||
type JWTConfig struct {
|
||||
Secret string `yaml:"secret"`
|
||||
ExpireHours int `yaml:"expire_hours"`
|
||||
}
|
||||
|
||||
type APISignConfig struct {
|
||||
AppKey string `yaml:"app_key"`
|
||||
AppSecret string `yaml:"app_secret"`
|
||||
SignMethod string `yaml:"sign_method"`
|
||||
TimestampTolerance int `yaml:"timestamp_tolerance"`
|
||||
ClientId string `yaml:"client_id"`
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
MaxAge int `yaml:"max_age"`
|
||||
RotateTime int `yaml:"rotate_time"`
|
||||
RootPath string `yaml:"root_path"`
|
||||
Channel map[string]string `yaml:"channel"`
|
||||
}
|
||||
|
||||
type ESConfig struct {
|
||||
Host string `yaml:"host"`
|
||||
Index string `yaml:"index"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
}
|
||||
|
||||
type OCRConfig struct {
|
||||
ServiceUrl string `yaml:"service_url"`
|
||||
ExeUrl string `yaml:"exe_url"`
|
||||
}
|
||||
|
||||
type ExternalAPIConfig struct {
|
||||
SyncProductURL string `yaml:"sync_product_url"`
|
||||
SyncTaskURL string `yaml:"sync_task_url"`
|
||||
SyncTaskBodyURL string `yaml:"sync_task_body_url"`
|
||||
ESUpdateBookURL string `yaml:"es_update_book_url"`
|
||||
Timeout int `yaml:"timeout"`
|
||||
}
|
||||
|
||||
var AppConfig *Config
|
||||
|
||||
func Init() {
|
||||
configPath := "config.yaml"
|
||||
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("读取配置文件失败: %v", err)
|
||||
}
|
||||
|
||||
AppConfig = &Config{}
|
||||
err = yaml.Unmarshal(data, AppConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("解析配置文件失败: %v", err)
|
||||
}
|
||||
|
||||
log.Println("配置文件加载成功")
|
||||
}
|
||||
|
||||
// GetDSN 返回MySQL连接字符串
|
||||
func (c *Config) GetDSN() string {
|
||||
return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=10s&readTimeout=30s&writeTimeout=30s&interpolateParams=true&multiStatements=true&allowNativePasswords=true&checkConnLiveness=true",
|
||||
c.Database.User, c.Database.Password, c.Database.Host, c.Database.Port, c.Database.Name)
|
||||
}
|
||||
|
||||
// GetTaskDSN 返回任务库MySQL连接字符串(腾讯云MySQL)
|
||||
func (c *Config) GetTaskDSN() string {
|
||||
return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=10s&readTimeout=30s&writeTimeout=30s&allowNativePasswords=true",
|
||||
c.TaskDatabase.User, c.TaskDatabase.Password, c.TaskDatabase.Host, c.TaskDatabase.Port, c.TaskDatabase.Name)
|
||||
}
|
||||
120
constant/constant.go
Normal file
120
constant/constant.go
Normal file
@ -0,0 +1,120 @@
|
||||
package constant
|
||||
|
||||
const MaxTaskQuantity int = 200 // 单次任务最大数量
|
||||
|
||||
// 响应状态码
|
||||
const (
|
||||
OAUTH = 401 // 未授权
|
||||
ERROR = 500 // 服务器错误
|
||||
SUCCESS = 200 // 成功
|
||||
VALIDATE = 204
|
||||
)
|
||||
|
||||
// 波次类型
|
||||
const (
|
||||
WaveNormal = 1 // 普通
|
||||
WaveNUrgent = 2 // 紧急
|
||||
WaveNBatch = 3 // 批量
|
||||
)
|
||||
|
||||
// 波次状态
|
||||
const (
|
||||
WaveStatusCreated = 1 // 已创建
|
||||
WaveStatusReleased = 2 // 已下发
|
||||
WaveStatusPicking = 3 // 拣货中
|
||||
WaveStatusCompleted = 4 // 已完成
|
||||
WaveStatusCancelled = 5 // 已取消
|
||||
)
|
||||
|
||||
// 入出库方向
|
||||
const (
|
||||
DirectionInbound = 1 // 入库
|
||||
DirectionOutbound = 2 // 出库
|
||||
)
|
||||
|
||||
// 任务类型
|
||||
const (
|
||||
TaskTypePicking = 1 // 拣货任务
|
||||
TaskTypePutaway = 4 // 上架任务
|
||||
)
|
||||
|
||||
// 库存变更类型
|
||||
const (
|
||||
InventoryChangeInbound = 1 // 入库
|
||||
InventoryChangeOutbound = 2 // 出库
|
||||
InventoryChangeTransfer = 3 // 移库
|
||||
InventoryChangeAdjustment = 4 // 盘点调整
|
||||
InventoryChangeLock = 5 // 锁定库存
|
||||
InventoryChangeUnlock = 6 // 解锁库存
|
||||
)
|
||||
|
||||
// 订单类型
|
||||
const (
|
||||
OrderTypePurchase = "purchase" // 采购订单
|
||||
OrderTypeReceiving = "receiving" // 入库订单
|
||||
OrderTypeSales = "sales" // 销售订单
|
||||
OrderTypeStockCheck = "stock_check" // 盘库单
|
||||
)
|
||||
|
||||
// 采购订单状态 (PurchaseOrder.Status)
|
||||
const (
|
||||
PurchaseStatusDraft = 1 // 草稿
|
||||
PurchaseStatusSubmitted = 2 // 已提交
|
||||
PurchaseStatusApproved = 3 // 已审核
|
||||
PurchaseStatusPartialReceived = 4 // 部分收货
|
||||
PurchaseStatusReceived = 5 // 已收货
|
||||
PurchaseStatusCancelled = 6 // 已取消
|
||||
)
|
||||
|
||||
// 入库单状态 (ReceivingOrder.Status)
|
||||
const (
|
||||
ReceivingStatusPending = 1 // 待收货
|
||||
ReceivingStatusChecking = 2 // 验收中
|
||||
ReceivingStatusCompleted = 3 // 已完成
|
||||
ReceivingStatusCancelled = 4 // 已取消
|
||||
)
|
||||
|
||||
// 销售订单状态 (SalesOrder.Status)
|
||||
const (
|
||||
SalesStatusDraft = 1 // 草稿
|
||||
SalesStatusConfirmed = 2 // 已确认
|
||||
SalesStatusAllocated = 3 // 已分配库存
|
||||
SalesStatusPicking = 4 // 拣货完成
|
||||
SalesStatusShipped = 5 // 已发货
|
||||
SalesStatusCancelled = 6 // 已取消
|
||||
)
|
||||
|
||||
// 出库单状态 (OutboundOrder.Status)
|
||||
const (
|
||||
OutboundStatusCreated = 1 // 已创建
|
||||
OutboundStatusPicking = 2 // 拣货中
|
||||
OutboundStatusCompleted = 3 // 已完成
|
||||
OutboundStatusCancelled = 4 // 已取消
|
||||
OutboundStatusShipping = 5 // 发货中
|
||||
OutboundStatusShipped = 6 // 已发货
|
||||
)
|
||||
|
||||
// 发货单状态 (ShippingOrder.Status)
|
||||
const (
|
||||
ShippingStatusPending = 1 // 待发货
|
||||
ShippingStatusShipped = 2 // 已发货
|
||||
ShippingStatusSigned = 3 // 已签收
|
||||
ShippingStatusCancelled = 4 // 已取消
|
||||
)
|
||||
|
||||
// 库存盘点状态
|
||||
const (
|
||||
InventoryCheckStatusDraft = 1 // 待盘点
|
||||
InventoryCheckStatusInProgress = 2 // 盘点中
|
||||
InventoryCheckStatusCompleted = 3 // 已完成
|
||||
InventoryCheckStatusCancelled = 4 // 已取消
|
||||
)
|
||||
|
||||
// 日志渠道
|
||||
const (
|
||||
LoggerChannelSql = "sql" // SQL日志
|
||||
LoggerChannelWork = "work" // 业务日志
|
||||
LoggerChannelRequest = "request" // 请求日志
|
||||
LoggerChannelEs = "es" // ES日志
|
||||
LoggerChannelRedis = "redis" // Redis日志
|
||||
)
|
||||
33
controllers/barcode.go
Normal file
33
controllers/barcode.go
Normal file
@ -0,0 +1,33 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
systemReq "psi/models/request"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type BarcodeApi struct{}
|
||||
|
||||
var barcodeService = service.BarcodeService{}
|
||||
|
||||
func (r *BarcodeApi) GenerateBarcode(c *gin.Context) {
|
||||
var req systemReq.BarcodeRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "条形码生成请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := barcodeService.GenerateBarcode(req.Content)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "条形码生成异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
128
controllers/book.go
Normal file
128
controllers/book.go
Normal file
@ -0,0 +1,128 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type BookApi struct{}
|
||||
|
||||
var bookService = service.BookService{}
|
||||
|
||||
// GetBookInfo 获取图书信息
|
||||
func (r *BookApi) GetBookInfo(c *gin.Context) {
|
||||
var req systemReq.BookRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "获取图书信息请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := bookService.GetBookInfo(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取图书信息异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetSuitBook 获取套装书
|
||||
func (r *BookApi) GetSuitBook(c *gin.Context) {
|
||||
var req systemReq.BookRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "获取套装书请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := bookService.GetSuitBook(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取套装书异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetNoIsbnBook 获取无书号书
|
||||
func (r *BookApi) GetNoIsbnBook(c *gin.Context) {
|
||||
var req systemReq.GetNoIsbnBookRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "获取无书号书请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := bookService.GetNoIsbnBook(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取无书号书异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
"msg": "查询成功",
|
||||
})
|
||||
}
|
||||
|
||||
// GetProCode 获取商品编码
|
||||
func (r *BookApi) GetProCode(c *gin.Context) {
|
||||
var req systemReq.GetCodeRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "获取商品编码请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := bookService.GetProCode(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取商品编码异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// SyncBook 同步书籍信息
|
||||
func (r *BookApi) SyncBook(c *gin.Context) {
|
||||
var req systemReq.AddBookRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "同步书籍信息请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.LiveImage) == 0 {
|
||||
image, err := parseImageFromForm(c)
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.LiveImage = image
|
||||
}
|
||||
|
||||
bookID, result, err := bookService.SyncBook(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "同步书籍信息异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": gin.H{
|
||||
"id": bookID,
|
||||
"isbn": result,
|
||||
},
|
||||
})
|
||||
}
|
||||
38
controllers/cancel_logistics.go
Normal file
38
controllers/cancel_logistics.go
Normal file
@ -0,0 +1,38 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"psi/models/request"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type CancelLogisticsApi struct{}
|
||||
|
||||
var cancelLogisticsService = service.CancelLogisticsService{}
|
||||
|
||||
// CancelLogistics 取消物流单号
|
||||
func (r *CancelLogisticsApi) CancelLogistics(c *gin.Context) {
|
||||
var req request.CancelLogisticsRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": "500",
|
||||
"msg": "参数错误: " + err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := cancelLogisticsService.CancelLogistics(req.UserID, req.LogisticsNo)
|
||||
if err != nil {
|
||||
utils.ErrorLog("cancel_logistics", nil) // simple log
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": "500",
|
||||
"msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
161
controllers/car.go
Normal file
161
controllers/car.go
Normal file
@ -0,0 +1,161 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type CarApi struct{}
|
||||
|
||||
var carService = service.CarService{}
|
||||
|
||||
func (r *CarApi) GetCarList(c *gin.Context) {
|
||||
var req systemReq.QueryCarRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "查询小车列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
list, total, err := carService.GetCarList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询小车列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"data": gin.H{
|
||||
"list": list,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"page_size": req.PageSize,
|
||||
},
|
||||
"msg": "查询成功",
|
||||
})
|
||||
}
|
||||
|
||||
func (r *CarApi) GetCarDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
|
||||
if idStr == "" {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取小车详情请求参数异常",
|
||||
"err_msg": "ID参数不能为空",
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id <= 0 {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取小车详情请求参数异常",
|
||||
"err_msg": "ID格式错误: " + idStr,
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c)
|
||||
return
|
||||
}
|
||||
|
||||
car, err := carService.GetCarByID(id, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询小车详情异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(car, "查询成功", c)
|
||||
}
|
||||
|
||||
func (r *CarApi) CreateCar(c *gin.Context) {
|
||||
var req systemReq.CreateCarRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建小车请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.ShopInfo) == 0 {
|
||||
info, err := parseInfo(c)
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.ShopInfo = info
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
id, err := carService.CreateCar(req, userInfo.ID, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建小车异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(gin.H{"id": id}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (r *CarApi) UpdateCar(c *gin.Context) {
|
||||
var req systemReq.UpdateCarRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "更新小车请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.ShopInfo) == 0 {
|
||||
info, err := parseInfo(c)
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.ShopInfo = info
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
if err := carService.UpdateCar(req, userInfo.ID, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "更新小车异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
func (r *CarApi) DeleteCar(c *gin.Context) {
|
||||
var req systemReq.DeleteCarRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "删除小车请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := carService.DeleteCar(req.ID, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "删除小车异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
func parseInfo(c *gin.Context) ([]map[string]interface{}, error) {
|
||||
var info []map[string]interface{}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
infoStr := c.PostForm(fmt.Sprintf("shop_info[%d]", i))
|
||||
if infoStr == "" {
|
||||
break
|
||||
}
|
||||
|
||||
var parsedData map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(infoStr), &parsedData); err != nil {
|
||||
return nil, fmt.Errorf("第%d个shop_info参数JSON解析失败: %v", i, err)
|
||||
}
|
||||
|
||||
info = append(info, parsedData)
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
120
controllers/config.go
Normal file
120
controllers/config.go
Normal file
@ -0,0 +1,120 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type ConfigApi struct{}
|
||||
|
||||
var configService = service.ConfigService{}
|
||||
|
||||
// GetConfigList 获取配置列表
|
||||
func (r *ConfigApi) GetConfigList(c *gin.Context) {
|
||||
var req systemReq.GetConfigListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "配置列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := configService.GetConfigList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "配置列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetConfigDetail 获取配置详情
|
||||
func (r *ConfigApi) GetConfigDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
|
||||
if idStr == "" {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取配置详情请求参数异常",
|
||||
"err_msg": "ID参数不能为空",
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id <= 0 {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取配置详情请求参数异常",
|
||||
"err_msg": "ID格式错误: " + idStr,
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := configService.GetConfigDetail(id, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "配置详情异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// CreateConfig 创建配置
|
||||
func (r *ConfigApi) CreateConfig(c *gin.Context) {
|
||||
var req systemReq.AddConfigRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建配置请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := configService.CreateConfig(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建配置异常", err, c, req)
|
||||
return
|
||||
}
|
||||
systemRes.OkWithDetailed(gin.H{"id": id}, "创建成功", c)
|
||||
}
|
||||
|
||||
// UpdateConfig 更新配置
|
||||
func (r *ConfigApi) UpdateConfig(c *gin.Context) {
|
||||
var req systemReq.UpdateConfigRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "更新配置请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := configService.UpdateConfig(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "更新配置异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// DeleteConfig 删除配置
|
||||
func (r *ConfigApi) DeleteConfig(c *gin.Context) {
|
||||
var req systemReq.DeleteConfigRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "删除配置请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := configService.DeleteConfig(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "删除配置异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("删除成功", c)
|
||||
}
|
||||
308
controllers/employee.go
Normal file
308
controllers/employee.go
Normal file
@ -0,0 +1,308 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/models"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type EmployeeApi struct{}
|
||||
|
||||
var employeeService = service.EmployeeService{}
|
||||
|
||||
// Login 登录
|
||||
// @param type: 255-admin, 128-employee
|
||||
func (r *EmployeeApi) Login(c *gin.Context) {
|
||||
userType := c.Param("type")
|
||||
|
||||
var req systemReq.LoginRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "登录请求异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
response, err := employeeService.Login(req, userType, c)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "登录异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(response, "登录成功", c)
|
||||
}
|
||||
|
||||
// GetCurrentUser 获取当前用户信息
|
||||
func (r *EmployeeApi) GetCurrentUser(c *gin.Context) {
|
||||
employee, exists := c.Get("employee")
|
||||
if !exists {
|
||||
systemRes.NoAuth("未登录", c)
|
||||
return
|
||||
}
|
||||
|
||||
currentEmployee := employee.(models.Employee)
|
||||
|
||||
employeeInfo, err := employeeService.GetCurrentUser(currentEmployee.ID)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "获取当前用户信息异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(gin.H{
|
||||
"type": "admin",
|
||||
"id": employeeInfo.ID,
|
||||
"employee_id_str": employeeInfo.EmployeeIDStr,
|
||||
"username": employeeInfo.Username,
|
||||
"name": employeeInfo.Name,
|
||||
"role": employeeInfo.Role,
|
||||
"points": employeeInfo.Score,
|
||||
"status": employeeInfo.Status,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// Logout 退出登录
|
||||
func (r *EmployeeApi) Logout(c *gin.Context) {
|
||||
systemRes.OkWithMessage("退出成功", c)
|
||||
}
|
||||
|
||||
// GetEmployeeList 员工列表
|
||||
func (r *EmployeeApi) GetEmployeeList(c *gin.Context) {
|
||||
var req systemReq.GetEmployeeListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "员工列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := employeeService.GetEmployeeList(req, userInfo.ID, userInfo.AboutID)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "员工列表异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// AddEmployee 添加员工/租户注册
|
||||
func (r *EmployeeApi) AddEmployee(c *gin.Context) {
|
||||
var req systemReq.AddEmployeeRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "添加员工请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := employeeService.AddEmployee(req)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "添加员工异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"message": "添加成功",
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdatePasswordEmployee 修改员工密码
|
||||
func (r *EmployeeApi) UpdatePasswordEmployee(c *gin.Context) {
|
||||
var req systemReq.UpdatePasswordEmployeeRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "修改员工密码请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := employeeService.UpdatePasswordEmployee(req)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "修改员工密码异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("修改成功", c)
|
||||
}
|
||||
|
||||
// UpdateExpireTimeEmployee 修改员工过期时间
|
||||
func (r *EmployeeApi) UpdateExpireTimeEmployee(c *gin.Context) {
|
||||
var req systemReq.UpdateExpireTimeEmployeeRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "修改员工过期时间请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := employeeService.UpdateExpireTimeEmployee(req)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "修改员工过期时间异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("修改成功", c)
|
||||
}
|
||||
|
||||
// CheckExpireTimeEmployee 校验员工过期时间
|
||||
func (r *EmployeeApi) CheckExpireTimeEmployee(c *gin.Context) {
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
err := employeeService.CheckExpireTimeEmployee(userInfo.ID)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "校验员工过期时间异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("操作成功", c)
|
||||
}
|
||||
|
||||
// CheckCodeEmployee 校验员工机械码
|
||||
func (r *EmployeeApi) CheckCodeEmployee(c *gin.Context) {
|
||||
var req systemReq.CheckCodeEmployeeRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "校验员工机械码请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
code, err := employeeService.CheckCodeEmployee(req)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "校验员工机械码异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(map[string]interface{}{
|
||||
"code": code,
|
||||
}, "查询成功", c)
|
||||
}
|
||||
|
||||
// ClearCodeEmployee 清除员工机械码
|
||||
func (r *EmployeeApi) ClearCodeEmployee(c *gin.Context) {
|
||||
var req systemReq.ClearCodeEmployeeRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "清除员工机械码请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := employeeService.ClearCodeEmployee(req)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "清除员工机械码异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("修改成功", c)
|
||||
}
|
||||
|
||||
// GetLevelConfigList 获取员工等级配置列表
|
||||
func (r *EmployeeApi) GetLevelConfigList(c *gin.Context) {
|
||||
levelStr := c.Query("level")
|
||||
|
||||
var levelInt int8
|
||||
if levelStr != "" {
|
||||
fmt.Sscanf(levelStr, "%d", &levelInt)
|
||||
}
|
||||
|
||||
result := service.GetLevelConfig(levelInt)
|
||||
if result == nil {
|
||||
systemRes.FailWithMessage("无效的等级配置", c)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// SetEmployeeLevel 设置员工等级
|
||||
func (r *EmployeeApi) SetEmployeeLevel(c *gin.Context) {
|
||||
var req systemReq.SetEmployeeLevelRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "设置员工等级请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
err := employeeService.SetEmployeeLevel(req, userInfo.ID, userInfo.Username)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "设置员工等级异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("操作成功", c)
|
||||
}
|
||||
|
||||
// GetEmployeeSettings 获取员工设置
|
||||
func (r *EmployeeApi) GetEmployeeSettings(c *gin.Context) {
|
||||
var req systemReq.GetEmployeeSettingsRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "获取员工设置请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
result := employeeService.GetEmployeeSettings(req)
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// SaveEmployeeSettings 保存员工设置
|
||||
func (r *EmployeeApi) SaveEmployeeSettings(c *gin.Context) {
|
||||
var req systemReq.SaveEmployeeSettingsRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "保存员工设置请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
err := employeeService.SaveEmployeeSettings(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "保存员工设置异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("保存成功", c)
|
||||
}
|
||||
99
controllers/goods_import.go
Normal file
99
controllers/goods_import.go
Normal file
@ -0,0 +1,99 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"psi/constant"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
var goodsImportService = &service.GoodsImportService{}
|
||||
|
||||
// GoodsImportApi 商品导入API(独立模块)
|
||||
type GoodsImportApi struct{}
|
||||
|
||||
// ImportFromExcel 从Excel文件导入商品
|
||||
// POST /api/goods/import-from-excel
|
||||
// multipart/form-data: file (Excel), user_id, warehouse_id
|
||||
func (r *GoodsImportApi) ImportFromExcel(c *gin.Context) {
|
||||
// 获取 user_id
|
||||
userIDStr := c.PostForm("user_id")
|
||||
if userIDStr == "" {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelRequest, "商品导入-缺少user_id", fmt.Errorf("user_id不能为空"), c, nil)
|
||||
return
|
||||
}
|
||||
userID, err := strconv.ParseInt(userIDStr, 10, 64)
|
||||
if err != nil || userID <= 0 {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelRequest, "商品导入-user_id格式错误", err, c, userIDStr)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取 warehouse_id
|
||||
warehouseIDStr := c.PostForm("warehouse_id")
|
||||
if warehouseIDStr == "" {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelRequest, "商品导入-缺少warehouse_id", fmt.Errorf("warehouse_id不能为空"), c, nil)
|
||||
return
|
||||
}
|
||||
warehouseID, err := strconv.ParseInt(warehouseIDStr, 10, 64)
|
||||
if err != nil || warehouseID <= 0 {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelRequest, "商品导入-warehouse_id格式错误", err, c, warehouseIDStr)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取上传的 Excel 文件
|
||||
file, _, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelRequest, "商品导入-未上传文件", fmt.Errorf("未上传Excel文件: %v", err), c, nil)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
fileBytes, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelRequest, "商品导入-读取文件失败", fmt.Errorf("读取文件内容失败: %v", err), c, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 解析Excel
|
||||
rows, err := goodsImportService.ParseGoodsImportExcel(fileBytes)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "商品导入-解析Excel失败", err, c, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if len(rows) == 0 {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"msg": "没有有效数据",
|
||||
"data": gin.H{
|
||||
"success_count": 0,
|
||||
"fail_count": 0,
|
||||
"message": "Excel中没有有效数据行",
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 导入商品
|
||||
result, err := goodsImportService.ImportGoodsFromExcel(userID, warehouseID, rows)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "商品导入-导入失败", err, c, nil)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"msg": "导入完成",
|
||||
"data": gin.H{
|
||||
"success_count": result.SuccessCount,
|
||||
"fail_count": result.FailCount,
|
||||
"message": result.Message,
|
||||
},
|
||||
})
|
||||
}
|
||||
168
controllers/inventory.go
Normal file
168
controllers/inventory.go
Normal file
@ -0,0 +1,168 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type InventoryApi struct{}
|
||||
|
||||
var inventoryService = service.InventoryService{}
|
||||
|
||||
// GetInventoryList 获取库存汇总列表
|
||||
func (r *InventoryApi) GetInventoryList(c *gin.Context) {
|
||||
var req systemReq.GetInventoryListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "库存汇总列表请求异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := inventoryService.GetInventoryList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "库存汇总列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetInventoryGroupedList 获取按仓库库位分组的库存列表
|
||||
func (i *InventoryApi) GetInventoryGroupedList(c *gin.Context) {
|
||||
var req systemReq.GetInventoryGroupedListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "库存分组列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := inventoryService.GetInventoryGroupedList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "库存分组列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetInventorySummary 获取库存统计信息
|
||||
func (r *InventoryApi) GetInventorySummary(c *gin.Context) {
|
||||
result, err := inventoryService.GetInventorySummary(database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "库存统计信息异常", err, c, nil)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetInventoryDetail 获取库存明细
|
||||
func (r *InventoryApi) GetInventoryDetail(c *gin.Context) {
|
||||
var req systemReq.GetInventoryDetailRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "库存明细请求异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := inventoryService.GetInventoryDetail(req.ProductID, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "库存明细异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetInventoryLogList 获取库存流水列表
|
||||
func (r *InventoryApi) GetInventoryLogList(c *gin.Context) {
|
||||
var req systemReq.GetInventoryLogListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "库存流水列表请求异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := inventoryService.GetInventoryLogList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "库存流水列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// InventoryStatist 获取ISBN/品相的库存总数
|
||||
func (r *InventoryApi) InventoryStatist(c *gin.Context) {
|
||||
var req systemReq.InventoryStatistRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "库存统计请求异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := inventoryService.InventoryStatist(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "库存统计异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetStockCheckList 获取盘库列表
|
||||
func (r *InventoryApi) GetStockCheckList(c *gin.Context) {
|
||||
var req systemReq.GetStockCheckListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "盘库列表请求异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := inventoryService.GetStockCheckList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "盘库列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetStockCheckDetail 获取盘库明细列表
|
||||
func (r *InventoryApi) GetStockCheckDetail(c *gin.Context) {
|
||||
var req systemReq.GetStockCheckDetailRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "盘库明细列表请求异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := inventoryService.GetStockCheckDetail(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "盘库明细列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
333
controllers/location.go
Normal file
333
controllers/location.go
Normal file
@ -0,0 +1,333 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type LocationApi struct{}
|
||||
|
||||
var locationService = service.LocationService{}
|
||||
|
||||
func (r *LocationApi) GetLocationList(c *gin.Context) {
|
||||
var req systemReq.QueryLocationRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "查询库位列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
list, total, err := locationService.GetLocationList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询库位列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"data": gin.H{
|
||||
"list": list,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"page_size": req.PageSize,
|
||||
},
|
||||
"msg": "查询成功",
|
||||
})
|
||||
}
|
||||
|
||||
// GetAllLocationList 查询所有库位列表(仓库ID可选)
|
||||
func (r *LocationApi) GetAllLocationList(c *gin.Context) {
|
||||
var req systemReq.QueryAllLocationRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "查询所有库位列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
list, total, err := locationService.GetAllLocationList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询所有库位列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"data": gin.H{
|
||||
"list": list,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"page_size": req.PageSize,
|
||||
},
|
||||
"msg": "查询成功",
|
||||
})
|
||||
}
|
||||
|
||||
func (r *LocationApi) GetLocationDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
|
||||
if idStr == "" {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取库位详情请求参数异常",
|
||||
"err_msg": "ID参数不能为空",
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id <= 0 {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取库位详情请求参数异常",
|
||||
"err_msg": "ID格式错误: " + idStr,
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c)
|
||||
return
|
||||
}
|
||||
|
||||
location, err := locationService.GetLocationDetail(id, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询库位详情异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(location, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetLocationInfo 获取库位ID
|
||||
func (r *LocationApi) GetLocationInfo(c *gin.Context) {
|
||||
var req systemReq.GetLocationIdRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "获取库位ID请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
location, err := locationService.GetLocationInfo(req.Code, req.WarehouseCode, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取库位ID异常", err, c, gin.H{"code": req.Code})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(location, "查询成功", c)
|
||||
}
|
||||
|
||||
func (r *LocationApi) CreateLocation(c *gin.Context) {
|
||||
var req systemReq.CreateLocationRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建库位请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := locationService.CreateLocation(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建库位异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(gin.H{"id": id}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (r *LocationApi) BatchGenerateLocations(c *gin.Context) {
|
||||
var req systemReq.BatchGenerateLocationRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "批量生成库位请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
if len(req.Groups) == 0 {
|
||||
groups, err := parseGroupsFromForm(c)
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.Groups = groups
|
||||
}
|
||||
result, err := locationService.BatchGenerateLocations(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "批量生成库位异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, result.Message, c)
|
||||
}
|
||||
|
||||
func (r *LocationApi) UpdateLocation(c *gin.Context) {
|
||||
var req systemReq.UpdateLocationRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "更新库位请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.IDs) == 0 {
|
||||
ids, err := parseIdsFromForm(c)
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.IDs = ids
|
||||
}
|
||||
if err := locationService.UpdateLocation(req, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "更新库位异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
func (r *LocationApi) DeleteLocation(c *gin.Context) {
|
||||
var req systemReq.DeleteLocationRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "删除库位请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.IDs) == 0 {
|
||||
ids, err := parseIdsFromForm(c)
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.IDs = ids
|
||||
}
|
||||
if err := locationService.DeleteLocation(req.IDs, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "删除库位异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
func (r *LocationApi) SyncLocations(c *gin.Context) {
|
||||
var req systemReq.SyncLocationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "同步库位数据请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := locationService.SyncLocations(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "同步库位数据异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
"msg": "同步完成",
|
||||
})
|
||||
}
|
||||
|
||||
// SyncGoods 同步商品数据
|
||||
func (r *LocationApi) SyncGoods(c *gin.Context) {
|
||||
var req systemReq.SyncGoodsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "同步商品数据请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := locationService.SyncGoods(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "同步商品数据异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"msg": "同步完成",
|
||||
"data": gin.H{
|
||||
"success_count": result.SuccessCount,
|
||||
"fail_count": result.FailCount,
|
||||
"message": result.Message,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func parseIdsFromForm(c *gin.Context) ([]int64, error) {
|
||||
var ids []int64
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
idStr := c.PostForm(fmt.Sprintf("ids[%d]", i))
|
||||
if idStr == "" {
|
||||
break
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil {
|
||||
return nil, fmt.Errorf("第%d个ID格式错误", i)
|
||||
}
|
||||
if id <= 0 {
|
||||
return nil, fmt.Errorf("第%d个ID必须大于0", i)
|
||||
}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
|
||||
if len(ids) == 0 {
|
||||
return nil, fmt.Errorf("至少需要一个ID")
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func parseGroupsFromForm(c *gin.Context) ([]systemReq.GroupConfig, error) {
|
||||
var groups []systemReq.GroupConfig
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
formatTypeStr := c.PostForm(fmt.Sprintf("groups[%d][format_type]", i))
|
||||
if formatTypeStr == "" {
|
||||
break
|
||||
}
|
||||
|
||||
startValue := c.PostForm(fmt.Sprintf("groups[%d][start_value]", i))
|
||||
endValue := c.PostForm(fmt.Sprintf("groups[%d][end_value]", i))
|
||||
separator := c.PostForm(fmt.Sprintf("groups[%d][separator]", i))
|
||||
paddingLenStr := c.PostForm(fmt.Sprintf("groups[%d][padding_len]", i))
|
||||
|
||||
if startValue == "" || endValue == "" {
|
||||
return nil, fmt.Errorf("第%d组配置缺少必填字段", i)
|
||||
}
|
||||
|
||||
var formatType int
|
||||
if _, err := fmt.Sscanf(formatTypeStr, "%d", &formatType); err != nil {
|
||||
return nil, fmt.Errorf("第%d组format_type格式错误", i)
|
||||
}
|
||||
|
||||
if formatType < 1 || formatType > 4 {
|
||||
return nil, fmt.Errorf("第%d组format_type必须在1-4之间", i)
|
||||
}
|
||||
|
||||
paddingLen := 0
|
||||
if paddingLenStr != "" {
|
||||
if _, err := fmt.Sscanf(paddingLenStr, "%d", &paddingLen); err != nil {
|
||||
return nil, fmt.Errorf("第%d组padding_len格式错误", i)
|
||||
}
|
||||
}
|
||||
|
||||
if separator == "" {
|
||||
separator = ""
|
||||
}
|
||||
|
||||
group := systemReq.GroupConfig{
|
||||
FormatType: formatType,
|
||||
StartValue: startValue,
|
||||
EndValue: endValue,
|
||||
PaddingLen: paddingLen,
|
||||
Separator: separator,
|
||||
}
|
||||
|
||||
groups = append(groups, group)
|
||||
}
|
||||
|
||||
if len(groups) == 0 {
|
||||
return nil, fmt.Errorf("至少需要一个分组配置")
|
||||
}
|
||||
|
||||
if len(groups) > 5 {
|
||||
return nil, fmt.Errorf("最多支持5组")
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
99
controllers/location_import.go
Normal file
99
controllers/location_import.go
Normal file
@ -0,0 +1,99 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"psi/constant"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
var locationImportService = &service.LocationImportService{}
|
||||
|
||||
// LocationImportApi 库位导入API
|
||||
type LocationImportApi struct{}
|
||||
|
||||
// ImportFromExcel 从Excel文件批量创建库位
|
||||
// POST /api/location/import-from-excel (sign鉴权)
|
||||
// multipart/form-data: file (Excel), user_id, warehouse_id
|
||||
func (r *LocationImportApi) ImportFromExcel(c *gin.Context) {
|
||||
// 获取 user_id
|
||||
userIDStr := c.PostForm("user_id")
|
||||
if userIDStr == "" {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelRequest, "库位导入-缺少user_id", fmt.Errorf("user_id不能为空"), c, nil)
|
||||
return
|
||||
}
|
||||
userID, err := strconv.ParseInt(userIDStr, 10, 64)
|
||||
if err != nil || userID <= 0 {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelRequest, "库位导入-user_id格式错误", err, c, userIDStr)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取 warehouse_id
|
||||
warehouseIDStr := c.PostForm("warehouse_id")
|
||||
if warehouseIDStr == "" {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelRequest, "库位导入-缺少warehouse_id", fmt.Errorf("warehouse_id不能为空"), c, nil)
|
||||
return
|
||||
}
|
||||
warehouseID, err := strconv.ParseInt(warehouseIDStr, 10, 64)
|
||||
if err != nil || warehouseID <= 0 {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelRequest, "库位导入-warehouse_id格式错误", err, c, warehouseIDStr)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取上传的 Excel 文件
|
||||
file, _, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelRequest, "库位导入-未上传文件", fmt.Errorf("未上传Excel文件: %v", err), c, nil)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
fileBytes, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelRequest, "库位导入-读取文件失败", fmt.Errorf("读取文件内容失败: %v", err), c, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 解析Excel
|
||||
rows, err := locationImportService.ParseLocationImportExcel(fileBytes)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "库位导入-解析Excel失败", err, c, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if len(rows) == 0 {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"msg": "没有有效数据",
|
||||
"data": gin.H{
|
||||
"success_count": 0,
|
||||
"fail_count": 0,
|
||||
"message": "Excel中没有有效的库位编码",
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 导入库位
|
||||
result, err := locationImportService.ImportLocationsFromExcel(userID, warehouseID, rows)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "库位导入-导入失败", err, c, nil)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"msg": "导入完成",
|
||||
"data": gin.H{
|
||||
"success_count": result.SuccessCount,
|
||||
"fail_count": result.FailCount,
|
||||
"message": result.Message,
|
||||
},
|
||||
})
|
||||
}
|
||||
111
controllers/logistics.go
Normal file
111
controllers/logistics.go
Normal file
@ -0,0 +1,111 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
"psi/models/request"
|
||||
"psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type LogisticsApi struct{}
|
||||
|
||||
var logisticsService = service.LogisticsService{}
|
||||
|
||||
func (r *LogisticsApi) GetLogisticsList(c *gin.Context) {
|
||||
var req request.QueryLogisticsRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "查询物流模板列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
list, total, err := logisticsService.GetLogisticsList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询物流模板列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"data": gin.H{
|
||||
"list": list,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"page_size": req.PageSize,
|
||||
},
|
||||
"msg": "查询成功",
|
||||
})
|
||||
}
|
||||
|
||||
func (r *LogisticsApi) GetLogisticsDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
|
||||
if idStr == "" {
|
||||
response.FailWithValidateMessage("参数错误: ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
var id uint64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id == 0 {
|
||||
response.FailWithValidateMessage("参数错误: ID格式不正确", c)
|
||||
return
|
||||
}
|
||||
|
||||
logistics, err := logisticsService.GetLogisticsByID(id, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询物流模板详情异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(logistics, "查询成功", c)
|
||||
}
|
||||
|
||||
func (r *LogisticsApi) CreateLogistics(c *gin.Context) {
|
||||
var req request.CreateLogisticsRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建物流模板请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := logisticsService.CreateLogistics(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建物流模板异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(gin.H{"id": id}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (r *LogisticsApi) UpdateLogistics(c *gin.Context) {
|
||||
var req request.UpdateLogisticsRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "更新物流模板请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := logisticsService.UpdateLogistics(req, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "更新物流模板异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
func (r *LogisticsApi) DeleteLogistics(c *gin.Context) {
|
||||
var req request.DeleteLogisticsRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "删除物流模板请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := logisticsService.DeleteLogistics(req.Id, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "删除物流模板异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
64
controllers/ocr.go
Normal file
64
controllers/ocr.go
Normal file
@ -0,0 +1,64 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type OcrApi struct{}
|
||||
|
||||
var ocrService = service.OcrService{}
|
||||
|
||||
func (r *OcrApi) RecognizeText(c *gin.Context) {
|
||||
file, err := c.FormFile("image")
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "OCR识别请求异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithValidateMessage("请上传图片文件", c)
|
||||
return
|
||||
}
|
||||
|
||||
imageFile, err := file.Open()
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "打开图片文件异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithValidateMessage("读取图片文件失败", c)
|
||||
return
|
||||
}
|
||||
defer imageFile.Close()
|
||||
|
||||
imageData, err := io.ReadAll(imageFile)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "读取图片数据异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithValidateMessage("读取图片数据失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := ocrService.RecognizeText(imageData)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "OCR识别异常",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
systemRes.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
100
controllers/out_task.go
Normal file
100
controllers/out_task.go
Normal file
@ -0,0 +1,100 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type OutTaskApi struct{}
|
||||
|
||||
var outTaskService = service.OutTaskService{}
|
||||
|
||||
// GetOutTaskList 获取外部任务列表
|
||||
func (r *OutTaskApi) GetOutTaskList(c *gin.Context) {
|
||||
var req systemReq.GetOutTaskListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "外部任务列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := outTaskService.GetOutTaskList(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "外部任务列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetOutTaskLogList 获取外部任务日志列表
|
||||
func (r *OutTaskApi) GetOutTaskLogList(c *gin.Context) {
|
||||
var req systemReq.GetOutTaskLogListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "外部任务日志列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := outTaskService.GetOutTaskLogList(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "外部任务日志列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetOutTaskByShop 按店铺分组获取外部任务列表
|
||||
func (r *OutTaskApi) GetOutTaskByShop(c *gin.Context) {
|
||||
var req systemReq.GetOutTaskByShopRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "按店铺获取外部任务列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := outTaskService.GetOutTaskByShop(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "按店铺获取外部任务列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateOutTaskLog 修改外部任务日志
|
||||
func (r *OutTaskApi) UpdateOutTaskLog(c *gin.Context) {
|
||||
var req systemReq.UpdateOutTaskLogRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "修改外部任务日志请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := outTaskService.UpdateOutTaskLog(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "修改外部任务日志异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("操作成功", c)
|
||||
}
|
||||
57
controllers/outbound.go
Normal file
57
controllers/outbound.go
Normal file
@ -0,0 +1,57 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type OutboundApi struct{}
|
||||
|
||||
var outboundService = service.OutboundService{}
|
||||
|
||||
// GetOutboundOrderList 获取出库单列表
|
||||
func (r *OutboundApi) GetOutboundOrderList(c *gin.Context) {
|
||||
var req systemReq.GetOutboundOrderListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "出库单列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := outboundService.GetOutboundOrderList(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "出库单列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetOutboundOrderDetail 获取出库单详情
|
||||
func (r *OutboundApi) GetOutboundOrderDetail(c *gin.Context) {
|
||||
var req systemReq.GetOutboundOrderDetailRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "出库单详情请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := outboundService.GetOutboundOrderDetail(req.ID, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "出库单详情异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
854
controllers/process.go
Normal file
854
controllers/process.go
Normal file
@ -0,0 +1,854 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
"psi/models"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ProcessApi struct{}
|
||||
|
||||
var processService = &service.ProcessService{}
|
||||
|
||||
// CreatePurchaseOrderWithWave 创建采购单并生成入库波次
|
||||
func (r *ProcessApi) CreatePurchaseOrderWithWave(c *gin.Context) {
|
||||
var req systemReq.PurchaseOrderCreateRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建采购单请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
carService := &service.CarService{}
|
||||
capacity, err := carService.GetCarCapacityByID(req.CarID, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询小车容量异常", err, c, gin.H{"car_id": req.CarID})
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Items) == 0 {
|
||||
items, err := bindPurchaseOrderItems(c, int(capacity))
|
||||
if err != nil {
|
||||
logAndFail(constant.LoggerChannelRequest, "绑定采购订单项异常", "参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.Items = items
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
purchaseOrderID, waveID, err := processService.CreatePurchaseOrderWithWave(req, userInfo.Username, userInfo.ID, int(capacity), database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建采购单及波次异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(gin.H{"order_id": purchaseOrderID, "wave_id": waveID}, "采购单创建成功,波次已生成", c)
|
||||
}
|
||||
|
||||
// ReleaseWave 提交波次,生成采购订单明细和波次任务明细
|
||||
func (r *ProcessApi) ReleaseWave(c *gin.Context) {
|
||||
var req systemReq.WaveRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "提交波次请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
carService := &service.CarService{}
|
||||
capacity, err := carService.GetCarCapacityByID(req.CarID, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询小车容量异常", err, c, gin.H{"car_id": req.CarID})
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Items) == 0 {
|
||||
items, err := bindWaveItems(c, int(capacity))
|
||||
if err != nil {
|
||||
logAndFail(constant.LoggerChannelRequest, "绑定波次项异常", "参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.Items = items
|
||||
}
|
||||
|
||||
waveID, waveNo, err := processService.ReleaseWave(req, capacity, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "提交波次异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(systemRes.WaveReleaseResponse{
|
||||
WaveID: waveID,
|
||||
WaveNo: waveNo,
|
||||
}, "波次提交成功,任务和明细已生成", c)
|
||||
}
|
||||
|
||||
// BindWave 绑定波次,创建入库单
|
||||
func (r *ProcessApi) BindWave(c *gin.Context) {
|
||||
var req systemReq.BindWaveRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "绑定波次请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
receivingOrderID, waveTaskID, waveTaskBatchNo, err := processService.BindWave(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "绑定波次异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(map[string]interface{}{
|
||||
"receiving_order_id": receivingOrderID,
|
||||
"wave_task_id": waveTaskID,
|
||||
"wave_task_batch_no": waveTaskBatchNo,
|
||||
}, "绑定波次成功,入库单已创建", c)
|
||||
}
|
||||
|
||||
// GetWaveTaskInfo 获取波次任务信息
|
||||
func (r *ProcessApi) GetWaveTaskInfo(c *gin.Context) {
|
||||
id, err := parsePathID(c, "id", "获取波次任务信息")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
info, err := processService.GetWaveTaskInfo(id, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取波次任务信息异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(info, "获取成功", c)
|
||||
}
|
||||
|
||||
// SubmitReceiving 提交入库
|
||||
func (r *ProcessApi) SubmitReceiving(c *gin.Context) {
|
||||
var req systemReq.ReceivingSubmitRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "提交入库请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Items) == 0 {
|
||||
databaseConn := database.GetDB(c)
|
||||
var waveTask models.WaveTask
|
||||
if err := databaseConn.Where("id = ? AND is_del = 0", req.WaveTaskID).First(&waveTask).Error; err != nil {
|
||||
logAndFail(constant.LoggerChannelWork, "查询波次任务失败", "错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
carCapacity := int(waveTask.CarCapacity)
|
||||
if carCapacity <= 0 {
|
||||
logAndFail(constant.LoggerChannelWork, "波次任务未设置小车容量", fmt.Sprintf("wave_task_id: %d", req.WaveTaskID), c)
|
||||
return
|
||||
}
|
||||
items, err := bindReceivingItems(c, carCapacity)
|
||||
if err != nil {
|
||||
logAndFail(constant.LoggerChannelRequest, "绑定入库项异常", "参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.Items = items
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
err := processService.SubmitReceiving(req, userInfo.Username, userInfo.ID, userInfo.AboutID, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "提交入库异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
go func(receivingOrderID, aboutID, waveTaskID int64, itemProductIDs []int64) {
|
||||
databaseConn := database.DB
|
||||
|
||||
var receivingOrder models.ReceivingOrder
|
||||
if err := databaseConn.Where("id = ? AND is_del = 0", receivingOrderID).First(&receivingOrder).Error; err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "查询入库单失败",
|
||||
"receiving_order_id": receivingOrderID,
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var warehouse models.Warehouse
|
||||
if err := databaseConn.Where("id = ? AND is_del = 0", receivingOrder.WarehouseID).First(&warehouse).Error; err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "查询仓库失败",
|
||||
"warehouse_id": receivingOrder.WarehouseID,
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
warehouseId := warehouse.ID
|
||||
|
||||
productIds := make([]string, len(itemProductIDs))
|
||||
for i, pid := range itemProductIDs {
|
||||
productIds[i] = fmt.Sprintf("%d", pid)
|
||||
}
|
||||
productIdsStr := strings.Join(productIds, ",")
|
||||
|
||||
url := "https://erp.buzhiyushu.cn/zhishu/product/releaseGoodsAuto"
|
||||
method := "POST"
|
||||
|
||||
payload := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(payload)
|
||||
_ = writer.WriteField("userId", fmt.Sprintf("%d", aboutID))
|
||||
_ = writer.WriteField("warehouseId", fmt.Sprintf("%d", warehouseId))
|
||||
_ = writer.WriteField("productId", productIdsStr)
|
||||
err := writer.Close()
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "关闭multipart writer失败",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest(method, url, payload)
|
||||
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "创建HTTP请求失败",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
req.Header.Add("Authorization", "Basic ZWxhc3RpYzo1bVJESVVnNTJWQzBmcDE0bnctRg==")
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "调用外部API失败",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "调用外部API失败-Body关闭失败",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
}
|
||||
}(res.Body)
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "读取响应失败",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
utils.InfoLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "外部API调用完成",
|
||||
"user_id": aboutID,
|
||||
"warehouse_id": warehouseId,
|
||||
"product_ids": productIdsStr,
|
||||
"status_code": res.StatusCode,
|
||||
"response": string(body),
|
||||
})
|
||||
}(req.ReceivingOrderID, userInfo.AboutID, req.WaveTaskID, func() []int64 {
|
||||
productIDs := make([]int64, len(req.Items))
|
||||
for i, item := range req.Items {
|
||||
productIDs[i] = item.ProductID
|
||||
}
|
||||
return productIDs
|
||||
}())
|
||||
|
||||
systemRes.OkWithMessage("入库提交成功", c)
|
||||
}
|
||||
|
||||
// GetReceivingDetail 获取入库单详情
|
||||
func (r *ProcessApi) GetReceivingDetail(c *gin.Context) {
|
||||
id, err := parsePathID(c, "id", "获取入库单详情")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
detail, err := processService.GetReceivingDetail(id, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取入库单详情异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(detail, "获取成功", c)
|
||||
}
|
||||
|
||||
// CreateSalesOrderWithDetail 创建销售订单和明细
|
||||
func (r *ProcessApi) CreateSalesOrderWithDetail(c *gin.Context) {
|
||||
var req systemReq.SalesOrderCreateRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建销售订单请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Items) == 0 {
|
||||
items, err := bindSalesOrderItems(c)
|
||||
if err != nil {
|
||||
logAndFail(constant.LoggerChannelRequest, "绑定销售订单项异常", "参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.Items = items
|
||||
}
|
||||
|
||||
salesOrderID, err := processService.CreateSalesOrderWithDetail(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建销售订单及波次异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(gin.H{"order_id": salesOrderID}, "销售订单创建成功", c)
|
||||
}
|
||||
|
||||
// CreateOutboundOrder 基于多个销售订单创建出库单
|
||||
func (r *ProcessApi) CreateOutboundOrder(c *gin.Context) {
|
||||
var req systemReq.CreateOutboundOrderRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建出库单请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.SalesOrderIDs) == 0 {
|
||||
ids, err := parseIdsFrom(c, req.Total)
|
||||
if err != nil {
|
||||
systemRes.FailWithMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.SalesOrderIDs = ids
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
outboundOrderID, outNo, err := processService.CreateOutboundOrder(req, userInfo.Username, userInfo.ID, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建出库单异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(gin.H{
|
||||
"outbound_order_id": outboundOrderID,
|
||||
"out_no": outNo,
|
||||
}, "出库单创建成功", c)
|
||||
}
|
||||
|
||||
// CreateOutboundWave 基于出库单创建出库波次
|
||||
func (r *ProcessApi) CreateOutboundWave(c *gin.Context) {
|
||||
var req systemReq.CreateOutboundWaveRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建出库波次请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if req.OutboundOrderID == 0 {
|
||||
systemRes.FailWithMessage("参数错误: 出库单ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
waveID, err := processService.CreateOutboundWave(req, userInfo.Username, userInfo.ID, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建出库波次异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(systemRes.WaveReleaseResponse{
|
||||
WaveID: waveID,
|
||||
}, "出库波次创建成功", c)
|
||||
}
|
||||
|
||||
// ReleaseOutboundWave 提交出库波次,生成波次任务明细
|
||||
func (r *ProcessApi) ReleaseOutboundWave(c *gin.Context) {
|
||||
var req systemReq.WaveRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "提交出库波次请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
waveID, waveNo, err := processService.ReleaseOutboundWave(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "提交出库波次异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(systemRes.WaveReleaseResponse{
|
||||
WaveID: waveID,
|
||||
WaveNo: waveNo,
|
||||
}, "出库波次提交成功,任务和明细已生成", c)
|
||||
}
|
||||
|
||||
// BindOutboundWave 绑定出库波次
|
||||
func (r *ProcessApi) BindOutboundWave(c *gin.Context) {
|
||||
var req systemReq.BindWaveRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "绑定出库波次请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
outboundOrderID, waveTaskID, waveTaskBatchNo, err := processService.BindOutboundWave(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "绑定出库波次异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(map[string]interface{}{
|
||||
"out_order_id": outboundOrderID,
|
||||
"wave_task_id": waveTaskID,
|
||||
"wave_task_batch_no": waveTaskBatchNo,
|
||||
}, "绑定出库波次成功", c)
|
||||
}
|
||||
|
||||
// SubmitOutbound 提交出库
|
||||
func (r *ProcessApi) SubmitOutbound(c *gin.Context) {
|
||||
var req systemReq.OutboundSubmitRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "提交出库请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Items) == 0 {
|
||||
databaseConn := database.GetDB(c)
|
||||
var waveTask models.WaveTask
|
||||
if err := databaseConn.Where("id = ? AND is_del = 0", req.WaveTaskID).First(&waveTask).Error; err != nil {
|
||||
logAndFail(constant.LoggerChannelWork, "查询波次任务失败", "错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := databaseConn.Model(&models.WaveTaskDetail{}).Where("wave_task_id = ? AND status = 1 AND is_del = 0", waveTask.ID).Count(&count).Error; err != nil {
|
||||
logAndFail(constant.LoggerChannelWork, "查询波次任务明细数量失败", "错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
items, err := bindOutboundItems(c, int(count))
|
||||
if err != nil {
|
||||
logAndFail(constant.LoggerChannelRequest, "绑定出库项异常", "参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.Items = items
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
err := processService.SubmitOutbound(req, userInfo.Username, userInfo.ID, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "提交出库异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("出库提交成功", c)
|
||||
}
|
||||
|
||||
// GetOutboundDetail 获取发货单详情
|
||||
func (r *ProcessApi) GetOutboundDetail(c *gin.Context) {
|
||||
id, err := parsePathID(c, "id", "获取发货单详情")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
detail, err := processService.GetOutboundDetail(id, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取发货单详情异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(detail, "获取成功", c)
|
||||
}
|
||||
|
||||
// CreateShippingOrder 基于多个出库单创建发货单
|
||||
func (r *ProcessApi) CreateShippingOrder(c *gin.Context) {
|
||||
var req systemReq.CreateShippingOrderRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建发货单请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.OutboundOrderIDs) == 0 {
|
||||
ids, err := parseIdsFrom(c, req.Total)
|
||||
if err != nil {
|
||||
systemRes.FailWithMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.OutboundOrderIDs = ids
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
shippingOrderID, shippingNo, err := processService.CreateShippingOrder(req, userInfo.Username, userInfo.ID, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建发货单异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(gin.H{
|
||||
"shipping_order_id": shippingOrderID,
|
||||
"shipping_no": shippingNo,
|
||||
}, "发货单创建成功", c)
|
||||
}
|
||||
|
||||
// UpdateShippingLogistics 更新发货单物流信息并回填销售订单明细
|
||||
func (r *ProcessApi) UpdateShippingLogistics(c *gin.Context) {
|
||||
var req systemReq.UpdateShippingLogisticsRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "更新发货单物流信息请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
err := processService.UpdateShippingLogistics(req, userInfo.ID, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "更新发货单物流信息异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// CancelSalesOrder 取消销售订单
|
||||
func (r *ProcessApi) CancelSalesOrder(c *gin.Context) {
|
||||
var req systemReq.CancelSalesOrderRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "取消销售订单请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
err := processService.CancelSalesOrder(req.OrderID, userInfo.Username, userInfo.ID, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "取消销售订单异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("销售订单取消成功,库存已释放", c)
|
||||
}
|
||||
|
||||
// CancelOutboundWave 取消出库波次
|
||||
func (r *ProcessApi) CancelOutboundWave(c *gin.Context) {
|
||||
var req systemReq.CancelOutboundWaveRequest
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "取消出库波次请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
err := processService.CancelOutboundWave(req.WaveID, userInfo.Username, userInfo.ID, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "取消出库波次异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("出库波次取消成功,库存已释放", c)
|
||||
}
|
||||
|
||||
// AdjustInventory 盘库调整(加库存/减库存)
|
||||
func (r *ProcessApi) AdjustInventory(c *gin.Context) {
|
||||
var req systemReq.StockCheckAdjustRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "盘库调整请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
if err := processService.AdjustInventory(req, userInfo.Username, userInfo.ID, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "盘库调整异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("盘库调整成功", c)
|
||||
}
|
||||
|
||||
// ReturnInventory 盘库退货
|
||||
func (r *ProcessApi) ReturnInventory(c *gin.Context) {
|
||||
var req systemReq.StockCheckReturnRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "盘库退货请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
if err := processService.ReturnInventory(req, userInfo.Username, userInfo.ID, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "盘库退货异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("盘库退货成功", c)
|
||||
}
|
||||
|
||||
// ChangeLocation 出库单切换库位
|
||||
func (r *ProcessApi) ChangeLocation(c *gin.Context) {
|
||||
var req systemReq.ChangeLocationRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "出库单切换库位请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
if err := processService.ChangeLocation(req, userInfo.Username, userInfo.ID, database.GetDB(c)); err != nil {
|
||||
if errors.Is(err, utils.ErrNoAvailableLocation) {
|
||||
c.Status(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "出库单切换库位异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("库位切换成功", c)
|
||||
}
|
||||
|
||||
// parsePathID 从URL路径参数中获取并验证ID
|
||||
func parsePathID(c *gin.Context, paramName, source string) (int64, error) {
|
||||
idStr := c.Param(paramName)
|
||||
|
||||
if idStr == "" {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": source + "请求参数异常",
|
||||
"err_msg": "ID参数不能为空",
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID不能为空", c)
|
||||
return 0, fmt.Errorf("ID参数不能为空")
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id <= 0 {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": source + "请求参数异常",
|
||||
"err_msg": "ID格式错误: " + idStr,
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c)
|
||||
return 0, fmt.Errorf("ID格式错误")
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// parseOptionalDate 解析可选日期字段
|
||||
func parseOptionalDate(c *gin.Context, key string) int64 {
|
||||
if str := c.PostForm(key); str != "" {
|
||||
if val, err := strconv.ParseInt(str, 10, 64); err == nil {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// bindSimpleItem 绑定简单项(ProductID + Quantity + UnitPrice)
|
||||
func bindSimpleItem[T any](c *gin.Context, i int, creator func(int64, int64, int64) T) (T, bool, error) {
|
||||
var zero T
|
||||
|
||||
productID, hasProduct, err := getPostFormInt64(c, fmt.Sprintf("items[%d][product_id]", i))
|
||||
if err != nil || !hasProduct {
|
||||
return zero, false, err
|
||||
}
|
||||
|
||||
quantity, hasQuantity, err := getPostFormInt64(c, fmt.Sprintf("items[%d][quantity]", i))
|
||||
if err != nil || !hasQuantity {
|
||||
return zero, false, err
|
||||
}
|
||||
|
||||
unitPrice, hasPrice, err := getPostFormInt64(c, fmt.Sprintf("items[%d][unit_price]", i))
|
||||
if err != nil || !hasPrice {
|
||||
return zero, false, err
|
||||
}
|
||||
|
||||
return creator(productID, quantity, unitPrice), true, nil
|
||||
}
|
||||
|
||||
// bindLocationItem 绑定带位置信息的项(ProductID + LocationID + Quantity + 批次信息)
|
||||
func bindLocationItem[T any](c *gin.Context, i int, creator func(int64, int64, string, int64, int64, int64) T) (T, bool, error) {
|
||||
var zero T
|
||||
|
||||
productID, hasProduct, err := getPostFormInt64(c, fmt.Sprintf("items[%d][product_id]", i))
|
||||
if err != nil || !hasProduct {
|
||||
return zero, false, err
|
||||
}
|
||||
|
||||
locationID, hasLocation, err := getPostFormInt64(c, fmt.Sprintf("items[%d][location_id]", i))
|
||||
if err != nil || !hasLocation {
|
||||
return zero, false, err
|
||||
}
|
||||
|
||||
quantity, hasQuantity, err := getPostFormInt64(c, fmt.Sprintf("items[%d][quantity]", i))
|
||||
if err != nil || !hasQuantity {
|
||||
return zero, false, err
|
||||
}
|
||||
|
||||
batchNo := c.PostForm(fmt.Sprintf("items[%d][batch_no]", i))
|
||||
productionDate := parseOptionalDate(c, fmt.Sprintf("items[%d][production_date]", i))
|
||||
expiryDate := parseOptionalDate(c, fmt.Sprintf("items[%d][expiry_date]", i))
|
||||
|
||||
return creator(productID, locationID, batchNo, productionDate, expiryDate, quantity), true, nil
|
||||
}
|
||||
|
||||
// bindPurchaseOrderItems 绑定采购订单项
|
||||
func bindPurchaseOrderItems(c *gin.Context, capacity int) ([]systemReq.PurchaseOrderItemRequest, error) {
|
||||
return bindItemsWithForm(c, func(i int) (systemReq.PurchaseOrderItemRequest, bool, error) {
|
||||
return bindSimpleItem(c, i, func(productID, quantity, unitPrice int64) systemReq.PurchaseOrderItemRequest {
|
||||
return systemReq.PurchaseOrderItemRequest{
|
||||
ProductID: productID,
|
||||
Quantity: quantity,
|
||||
UnitPrice: unitPrice,
|
||||
}
|
||||
})
|
||||
}, capacity)
|
||||
}
|
||||
|
||||
// bindWaveItems 绑定波次项
|
||||
func bindWaveItems(c *gin.Context, capacity int) ([]systemReq.WaveItemRequest, error) {
|
||||
return bindItemsWithForm(c, func(i int) (systemReq.WaveItemRequest, bool, error) {
|
||||
return bindSimpleItem(c, i, func(productID, quantity, unitPrice int64) systemReq.WaveItemRequest {
|
||||
return systemReq.WaveItemRequest{
|
||||
ProductID: productID,
|
||||
Quantity: quantity,
|
||||
UnitPrice: unitPrice,
|
||||
}
|
||||
})
|
||||
}, capacity)
|
||||
}
|
||||
|
||||
// bindReceivingItems 绑定入库项
|
||||
func bindReceivingItems(c *gin.Context, maxItems int) ([]systemReq.ReceivingItemRequest, error) {
|
||||
return bindItemsWithForm(c, func(i int) (systemReq.ReceivingItemRequest, bool, error) {
|
||||
return bindLocationItem(c, i, func(productID, locationID int64, batchNo string, productionDate, expiryDate, quantity int64) systemReq.ReceivingItemRequest {
|
||||
return systemReq.ReceivingItemRequest{
|
||||
ProductID: productID,
|
||||
LocationID: locationID,
|
||||
BatchNo: batchNo,
|
||||
ProductionDate: productionDate,
|
||||
ExpiryDate: expiryDate,
|
||||
Quantity: quantity,
|
||||
}
|
||||
})
|
||||
}, maxItems)
|
||||
}
|
||||
|
||||
// bindSalesOrderItems 绑定销售订单项
|
||||
func bindSalesOrderItems(c *gin.Context) ([]systemReq.SalesOrderItemRequest, error) {
|
||||
return bindItemsWithForm(c, func(i int) (systemReq.SalesOrderItemRequest, bool, error) {
|
||||
return bindSimpleItem(c, i, func(productID, quantity, unitPrice int64) systemReq.SalesOrderItemRequest {
|
||||
return systemReq.SalesOrderItemRequest{
|
||||
ProductID: productID,
|
||||
Quantity: quantity,
|
||||
UnitPrice: unitPrice,
|
||||
}
|
||||
})
|
||||
}, 200)
|
||||
}
|
||||
|
||||
// bindOutboundItems 绑定出库项
|
||||
func bindOutboundItems(c *gin.Context, maxItems int) ([]systemReq.OutboundItemRequest, error) {
|
||||
return bindItemsWithForm(c, func(i int) (systemReq.OutboundItemRequest, bool, error) {
|
||||
return bindLocationItem(c, i, func(productID, locationID int64, batchNo string, productionDate, expiryDate, quantity int64) systemReq.OutboundItemRequest {
|
||||
return systemReq.OutboundItemRequest{
|
||||
ProductID: productID,
|
||||
LocationID: locationID,
|
||||
BatchNo: batchNo,
|
||||
ProductionDate: productionDate,
|
||||
ExpiryDate: expiryDate,
|
||||
Quantity: quantity,
|
||||
}
|
||||
})
|
||||
}, maxItems)
|
||||
}
|
||||
|
||||
// bindItemsWithForm 通用表单项绑定函数
|
||||
func bindItemsWithForm[T any](c *gin.Context, builder func(int) (T, bool, error), maxItems int) ([]T, error) {
|
||||
items := make([]T, 0)
|
||||
for i := 0; i < maxItems; i++ {
|
||||
item, hasData, err := builder(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !hasData {
|
||||
continue
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func parseIdsFrom(c *gin.Context, total int) ([]int64, error) {
|
||||
var ids []int64
|
||||
|
||||
for i := 0; i < total; i++ {
|
||||
idStr := c.PostForm(fmt.Sprintf("order_ids[%d]", i))
|
||||
if idStr == "" {
|
||||
break
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil {
|
||||
return nil, fmt.Errorf("第%d个ID格式错误", i)
|
||||
}
|
||||
if id <= 0 {
|
||||
return nil, fmt.Errorf("第%d个ID必须大于0", i)
|
||||
}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
|
||||
if len(ids) == 0 {
|
||||
return nil, fmt.Errorf("至少需要一个ID")
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// getPostFormInt64 获取表单中的int64值
|
||||
func getPostFormInt64(c *gin.Context, key string) (int64, bool, error) {
|
||||
str := c.PostForm(key)
|
||||
if str == "" {
|
||||
return 0, false, nil
|
||||
}
|
||||
val, err := strconv.ParseInt(str, 10, 64)
|
||||
if err != nil {
|
||||
return 0, true, err
|
||||
}
|
||||
return val, true, nil
|
||||
}
|
||||
|
||||
// logAndFail 业务统一的错误日志和响应函数
|
||||
func logAndFail(channel string, source, errMsg string, c *gin.Context) {
|
||||
utils.ErrorLog(channel, logrus.Fields{
|
||||
"source": source,
|
||||
"err_msg": errMsg,
|
||||
})
|
||||
systemRes.FailWithMessage(errMsg, c)
|
||||
}
|
||||
|
||||
// ValidAndFail 参数统一的错误日志和响应函数
|
||||
func ValidAndFail(channel string, source, errMsg string, c *gin.Context, err error) {
|
||||
utils.ErrorLog(channel, logrus.Fields{
|
||||
"source": source,
|
||||
"err_msg": errMsg,
|
||||
})
|
||||
utils.HandleValidationError(c, err)
|
||||
}
|
||||
464
controllers/product.go
Normal file
464
controllers/product.go
Normal file
@ -0,0 +1,464 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ProductApi struct{}
|
||||
|
||||
var productService = service.ProductService{}
|
||||
|
||||
func (r *ProductApi) GetProductList(c *gin.Context) {
|
||||
var req systemReq.GetProductListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "商品列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.IDs) == 0 {
|
||||
ids, err := parseIds(c)
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.IDs = ids
|
||||
}
|
||||
|
||||
result, err := productService.GetProductList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "商品列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *ProductApi) GetDistributionProductList(c *gin.Context) {
|
||||
var req systemReq.GetDistributionProductListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "分销商品列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Name支持双重编码:如果绑定后仍含%编码字符,再解码一次
|
||||
if req.Name != "" && strings.Contains(req.Name, "%") {
|
||||
if decoded, err := url.QueryUnescape(req.Name); err == nil {
|
||||
req.Name = decoded
|
||||
}
|
||||
}
|
||||
// StockOperator支持双重编码
|
||||
if req.StockOperator != "" && strings.Contains(req.StockOperator, "%") {
|
||||
if decoded, err := url.QueryUnescape(req.StockOperator); err == nil {
|
||||
req.StockOperator = decoded
|
||||
}
|
||||
}
|
||||
// SalePriceOperator支持双重编码
|
||||
if req.SalePriceOperator != "" && strings.Contains(req.SalePriceOperator, "%") {
|
||||
if decoded, err := url.QueryUnescape(req.SalePriceOperator); err == nil {
|
||||
req.SalePriceOperator = decoded
|
||||
}
|
||||
}
|
||||
|
||||
result, err := productService.GetDistributionProductList(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "分销商品列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetProductDetail 获取商品详情
|
||||
func (r *ProductApi) GetProductDetail(c *gin.Context) {
|
||||
var req systemReq.GetProductDetailRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "商品详情请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := productService.GetProductDetail(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "商品详情异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// SaveProduct 保存商品(添加或修改)
|
||||
func (r *ProductApi) SaveProduct(c *gin.Context) {
|
||||
var req systemReq.ProductRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "保存商品请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
if len(req.LiveImage) == 0 {
|
||||
image, err := parseImageFromForm(c)
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.LiveImage = image
|
||||
}
|
||||
id, err := productService.SaveProduct(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "保存商品异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(gin.H{"id": id}, "操作成功", c)
|
||||
}
|
||||
|
||||
// UpdatePrice 修改商品售价
|
||||
func (r *ProductApi) UpdatePrice(c *gin.Context) {
|
||||
var req systemReq.UpdatePriceRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "修改售价请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := productService.UpdatePrice(req); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "修改售价异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("售价修改成功", c)
|
||||
}
|
||||
|
||||
// DeleteProduct 删除商品
|
||||
func (r *ProductApi) DeleteProduct(c *gin.Context) {
|
||||
var req systemReq.DeleteProductRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "删除商品请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := productService.DeleteProduct(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "删除商品异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// GetProductInventory 获取商品库存数量
|
||||
func (r *ProductApi) GetProductInventory(c *gin.Context) {
|
||||
var req systemReq.GetProductInventoryRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "获取商品库存请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := productService.GetProductInventory(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取商品库存异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// RetryOutTask 重试失败的外部任务
|
||||
func (r *ProductApi) RetryOutTask(c *gin.Context) {
|
||||
var req systemReq.RetryOutTaskRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "重试任务请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := productService.RetryOutTask(req, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "重试任务异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("重试任务已提交", c)
|
||||
}
|
||||
|
||||
// ExportProducts 导出商品到Excel
|
||||
func (r *ProductApi) ExportProducts(c *gin.Context) {
|
||||
var req systemReq.ExportProductRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "导出商品请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := productService.ExportProducts(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "导出商品异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// PushProductToShop 上架到商铺:创建商品、库存记录,并推送到商铺
|
||||
func (r *ProductApi) PushProductToShop(c *gin.Context) {
|
||||
var req systemReq.PushProductToShopRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "上架到商铺请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 处理photos参数: 如果表单未绑定,尝试从原始表单解析
|
||||
if len(req.Photos) == 0 {
|
||||
var photos []string
|
||||
for i := 0; i < 20; i++ {
|
||||
photoStr := c.PostForm(fmt.Sprintf("photos[%d]", i))
|
||||
if photoStr == "" {
|
||||
break
|
||||
}
|
||||
photos = append(photos, photoStr)
|
||||
}
|
||||
req.Photos = photos
|
||||
}
|
||||
|
||||
result, err := productService.PushProductToShop(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "上架到商铺异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// BatchPushProducts 批量推送商品到多个店铺
|
||||
func (r *ProductApi) BatchPushProducts(c *gin.Context) {
|
||||
var req systemReq.BatchPushProductRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "批量推送请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := productService.BatchPushProducts(req); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "批量推送异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("批量推送完成", c)
|
||||
}
|
||||
|
||||
func (r *ProductApi) GetProductLogList(c *gin.Context) {
|
||||
var req systemReq.GetProductLogListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "商品日志列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := productService.GetProductLogList(req, userInfo.AboutID)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "商品日志列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// SaveProductLog 保存商品日志(添加或修改)
|
||||
func (r *ProductApi) SaveProductLog(c *gin.Context) {
|
||||
var req systemReq.ProductLogRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "保存商品日志请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.OldLiveImage) == 0 {
|
||||
image, err := parseOldImageFromForm(c)
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.OldLiveImage = image
|
||||
}
|
||||
|
||||
if len(req.NewLiveImage) == 0 {
|
||||
image, err := parseNewImageFromForm(c)
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.NewLiveImage = image
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
id, err := productService.SaveProductLog(req, userInfo.ID)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "保存商品日志异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(gin.H{"id": id}, "操作成功", c)
|
||||
}
|
||||
|
||||
func (r *ProductApi) AuditProductLog(c *gin.Context) {
|
||||
var req systemReq.AuditProductLogRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "审核商品日志请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.NewLiveImage) == 0 {
|
||||
image, err := parseNewImageFromForm(c)
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.NewLiveImage = image
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
if err := productService.AuditProductLog(req, userInfo.ID); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "审核商品日志异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("审核成功", c)
|
||||
}
|
||||
|
||||
func (r *ProductApi) DeleteProductLog(c *gin.Context) {
|
||||
var req systemReq.DeleteProductLogRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "删除商品日志请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := productService.DeleteProductLog(req); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "删除商品日志异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// GetShopProductDetail 获取店铺商品详情(包含商品数据和发送记录)
|
||||
func (r *ProductApi) GetShopProductDetail(c *gin.Context) {
|
||||
var req systemReq.GetShopProductDetailRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "获取店铺商品详情请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := productService.GetShopProductDetail(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取店铺商品详情异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
func parseIds(c *gin.Context) ([]int64, error) {
|
||||
var ids []int64
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
idStr := c.Query(fmt.Sprintf("ids[%d]", i))
|
||||
if idStr == "" {
|
||||
break
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil {
|
||||
return nil, fmt.Errorf("第%d个ID格式错误", i)
|
||||
}
|
||||
if id <= 0 {
|
||||
return nil, fmt.Errorf("第%d个ID必须大于0", i)
|
||||
}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func parseImageFromForm(c *gin.Context) ([]string, error) {
|
||||
var images []string
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
imageStr := c.PostForm(fmt.Sprintf("live_image[%d]", i))
|
||||
if imageStr == "" {
|
||||
break
|
||||
}
|
||||
|
||||
images = append(images, imageStr)
|
||||
}
|
||||
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func parseOldImageFromForm(c *gin.Context) ([]string, error) {
|
||||
var images []string
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
imageStr := c.PostForm(fmt.Sprintf("old_live_image[%d]", i))
|
||||
if imageStr == "" {
|
||||
break
|
||||
}
|
||||
|
||||
images = append(images, imageStr)
|
||||
}
|
||||
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func parseNewImageFromForm(c *gin.Context) ([]string, error) {
|
||||
var images []string
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
imageStr := c.PostForm(fmt.Sprintf("new_live_image[%d]", i))
|
||||
if imageStr == "" {
|
||||
break
|
||||
}
|
||||
|
||||
images = append(images, imageStr)
|
||||
}
|
||||
|
||||
return images, nil
|
||||
}
|
||||
57
controllers/purchase.go
Normal file
57
controllers/purchase.go
Normal file
@ -0,0 +1,57 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type PurchaseApi struct{}
|
||||
|
||||
var purchaseService = service.PurchaseService{}
|
||||
|
||||
// GetPurchaseOrderList 获取采购订单列表
|
||||
func (r *PurchaseApi) GetPurchaseOrderList(c *gin.Context) {
|
||||
var req systemReq.GetPurchaseOrderListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "采购订单列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := purchaseService.GetPurchaseOrderList(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "采购订单列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetPurchaseOrderDetail 获取采购订单详情
|
||||
func (r *PurchaseApi) GetPurchaseOrderDetail(c *gin.Context) {
|
||||
var req systemReq.GetPurchaseOrderDetailRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "采购订单详情请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := purchaseService.GetPurchaseOrderDetail(req.ID, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "采购订单详情异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
57
controllers/receiving.go
Normal file
57
controllers/receiving.go
Normal file
@ -0,0 +1,57 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type ReceivingApi struct{}
|
||||
|
||||
var receivingService = service.ReceivingService{}
|
||||
|
||||
// GetReceivingOrderList 获取入库单列表
|
||||
func (r *ReceivingApi) GetReceivingOrderList(c *gin.Context) {
|
||||
var req systemReq.GetReceivingOrderListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "入库单列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := receivingService.GetReceivingOrderList(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "入库单列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetReceivingOrderDetail 获取入库单详情
|
||||
func (r *ReceivingApi) GetReceivingOrderDetail(c *gin.Context) {
|
||||
var req systemReq.GetReceivingOrderDetailRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "入库单详情请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := receivingService.GetReceivingOrderDetail(req.ID, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "入库单详情异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
79
controllers/sales.go
Normal file
79
controllers/sales.go
Normal file
@ -0,0 +1,79 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type SalesApi struct{}
|
||||
|
||||
var salesService = service.SalesService{}
|
||||
|
||||
// GetSalesOrderList 获取销售订单列表
|
||||
func (r *SalesApi) GetSalesOrderList(c *gin.Context) {
|
||||
var req systemReq.GetSalesOrderListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "销售订单列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := salesService.GetSalesOrderList(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "销售订单列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetSalesOrderDetail 获取销售订单详情
|
||||
func (r *SalesApi) GetSalesOrderDetail(c *gin.Context) {
|
||||
var req systemReq.GetSalesOrderDetailRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "销售订单详情请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := salesService.GetSalesOrderDetail(req.ID, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "销售订单详情异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetSalesOrderDetailList 获取分页的销售订单详情列表
|
||||
func (r *SalesApi) GetSalesOrderDetailList(c *gin.Context) {
|
||||
var req systemReq.GetSalesOrderDetailListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "销售订单详情列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := salesService.GetSalesOrderDetailList(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "销售订单详情列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
79
controllers/shipping.go
Normal file
79
controllers/shipping.go
Normal file
@ -0,0 +1,79 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type ShippingApi struct{}
|
||||
|
||||
var shippingService = service.ShippingService{}
|
||||
|
||||
// GetShippingOrderList 获取发货单列表
|
||||
func (r *ShippingApi) GetShippingOrderList(c *gin.Context) {
|
||||
var req systemReq.GetShippingOrderListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "发货单列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := shippingService.GetShippingOrderList(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "发货单列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetShippingOrderDetail 获取发货单详情
|
||||
func (r *ShippingApi) GetShippingOrderDetail(c *gin.Context) {
|
||||
var req systemReq.GetShippingOrderDetailRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "发货单详情请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := shippingService.GetShippingOrderDetail(req.ID, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "发货单详情异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetShippingOrderDetailList 获取发货单详情列表(按状态筛选,返回list+detail所有字段)
|
||||
func (r *ShippingApi) GetShippingOrderDetailList(c *gin.Context) {
|
||||
var req systemReq.GetShippingOrderDetailListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "发货单详情列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := shippingService.GetShippingOrderDetailList(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "发货单详情列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
57
controllers/shipping.go.bak
Normal file
57
controllers/shipping.go.bak
Normal file
@ -0,0 +1,57 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type ShippingApi struct{}
|
||||
|
||||
var shippingService = service.ShippingService{}
|
||||
|
||||
// GetShippingOrderList 获取发货单列表
|
||||
func (r *ShippingApi) GetShippingOrderList(c *gin.Context) {
|
||||
var req systemReq.GetShippingOrderListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "发货单列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := shippingService.GetShippingOrderList(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "发货单列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetShippingOrderDetail 获取发货单详情
|
||||
func (r *ShippingApi) GetShippingOrderDetail(c *gin.Context) {
|
||||
var req systemReq.GetShippingOrderDetailRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "发货单详情请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := shippingService.GetShippingOrderDetail(req.ID, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "发货单详情异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
120
controllers/shop.go
Normal file
120
controllers/shop.go
Normal file
@ -0,0 +1,120 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type ShopApi struct{}
|
||||
|
||||
var shopService = service.ShopService{}
|
||||
|
||||
func (r *ShopApi) GetShopList(c *gin.Context) {
|
||||
var req systemReq.QueryShopRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "查询店铺列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
list, total, err := shopService.GetShopList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询店铺列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"data": gin.H{
|
||||
"list": list,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"page_size": req.PageSize,
|
||||
},
|
||||
"msg": "查询成功",
|
||||
})
|
||||
}
|
||||
|
||||
func (r *ShopApi) GetShopDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
|
||||
if idStr == "" {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取店铺详情请求参数异常",
|
||||
"err_msg": "ID参数不能为空",
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id <= 0 {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取店铺详情请求参数异常",
|
||||
"err_msg": "ID格式错误: " + idStr,
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c)
|
||||
return
|
||||
}
|
||||
|
||||
shop, err := shopService.GetShopByID(id, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询店铺详情异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(shop, "查询成功", c)
|
||||
}
|
||||
|
||||
func (r *ShopApi) CreateShop(c *gin.Context) {
|
||||
var req systemReq.CreateShopRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建店铺请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := shopService.CreateShop(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建店铺异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(gin.H{"id": id}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (r *ShopApi) UpdateShop(c *gin.Context) {
|
||||
var req systemReq.UpdateShopRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "更新店铺请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := shopService.UpdateShop(req, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "更新店铺异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
func (r *ShopApi) DeleteShop(c *gin.Context) {
|
||||
var req systemReq.DeleteShopRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "删除店铺请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := shopService.DeleteShop(req.ID, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "删除店铺异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("删除成功", c)
|
||||
}
|
||||
39
controllers/sorting_settings.go
Normal file
39
controllers/sorting_settings.go
Normal file
@ -0,0 +1,39 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type SortingSettingsApi struct{}
|
||||
|
||||
var sortingSettingsService = service.SortingSettingsService{}
|
||||
|
||||
// GetSortingSettings 获取分拣设置
|
||||
func (r *SortingSettingsApi) GetSortingSettings(c *gin.Context) {
|
||||
result := sortingSettingsService.GetSortingSettings(database.GetDB(c))
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// SaveSortingSettings 保存分拣设置
|
||||
func (r *SortingSettingsApi) SaveSortingSettings(c *gin.Context) {
|
||||
var req systemReq.SaveSortingSettingsRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "保存分拣设置请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := sortingSettingsService.SaveSortingSettings(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "保存分拣设置异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("保存成功", c)
|
||||
}
|
||||
127
controllers/split_account_config.go
Normal file
127
controllers/split_account_config.go
Normal file
@ -0,0 +1,127 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type SplitAccountConfigApi struct{}
|
||||
|
||||
var splitAccountConfigService = service.SplitAccountConfigService{}
|
||||
|
||||
// GetSplitAccountConfigList 获取分账配置列表
|
||||
func (r *SplitAccountConfigApi) GetSplitAccountConfigList(c *gin.Context) {
|
||||
var req systemReq.GetSplitAccountConfigListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "分账配置列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := splitAccountConfigService.GetSplitAccountConfigList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "分账配置列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetSplitAccountConfigDetail 获取分账配置详情
|
||||
func (r *SplitAccountConfigApi) GetSplitAccountConfigDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
|
||||
if idStr == "" {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取分账配置详情请求参数异常",
|
||||
"err_msg": "ID参数不能为空",
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id <= 0 {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取分账配置详情请求参数异常",
|
||||
"err_msg": "ID格式错误: " + idStr,
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := splitAccountConfigService.GetSplitAccountConfigDetail(id, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "分账配置详情异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// CreateSplitAccountConfig 创建分账配置
|
||||
func (r *SplitAccountConfigApi) CreateSplitAccountConfig(c *gin.Context) {
|
||||
var req systemReq.AddSplitAccountConfigRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建分账配置请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
id, err := splitAccountConfigService.CreateSplitAccountConfig(req, userInfo.Username, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建分账配置异常", err, c, req)
|
||||
return
|
||||
}
|
||||
systemRes.OkWithDetailed(gin.H{"id": id}, "创建成功", c)
|
||||
}
|
||||
|
||||
// UpdateSplitAccountConfig 更新分账配置
|
||||
func (r *SplitAccountConfigApi) UpdateSplitAccountConfig(c *gin.Context) {
|
||||
var req systemReq.UpdateSplitAccountConfigRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "更新分账配置请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
err := splitAccountConfigService.UpdateSplitAccountConfig(req, userInfo.Username, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "更新分账配置异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// DeleteSplitAccountConfig 删除分账配置
|
||||
func (r *SplitAccountConfigApi) DeleteSplitAccountConfig(c *gin.Context) {
|
||||
var req systemReq.DeleteSplitAccountConfigRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "删除分账配置请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
err := splitAccountConfigService.DeleteSplitAccountConfig(req, userInfo.Username, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "删除分账配置异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("删除成功", c)
|
||||
}
|
||||
127
controllers/split_account_deduction_log.go
Normal file
127
controllers/split_account_deduction_log.go
Normal file
@ -0,0 +1,127 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type SplitAccountDeductionLogApi struct{}
|
||||
|
||||
var splitAccountDeductionLogService = service.SplitAccountDeductionLogService{}
|
||||
|
||||
// GetSplitAccountDeductionLogList 获取分账扣钱日志列表
|
||||
func (r *SplitAccountDeductionLogApi) GetSplitAccountDeductionLogList(c *gin.Context) {
|
||||
var req systemReq.GetSplitAccountDeductionLogListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "分账扣钱日志列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := splitAccountDeductionLogService.GetSplitAccountDeductionLogList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "分账扣钱日志列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetSplitAccountDeductionLogDetail 获取分账扣钱日志详情
|
||||
func (r *SplitAccountDeductionLogApi) GetSplitAccountDeductionLogDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
|
||||
if idStr == "" {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取分账扣钱日志详情请求参数异常",
|
||||
"err_msg": "ID参数不能为空",
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id <= 0 {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取分账扣钱日志详情请求参数异常",
|
||||
"err_msg": "ID格式错误: " + idStr,
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := splitAccountDeductionLogService.GetSplitAccountDeductionLogDetail(id, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "分账扣钱日志详情异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// CreateSplitAccountDeductionLog 创建分账扣钱日志
|
||||
func (r *SplitAccountDeductionLogApi) CreateSplitAccountDeductionLog(c *gin.Context) {
|
||||
var req systemReq.AddSplitAccountDeductionLogRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建分账扣钱日志请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
id, err := splitAccountDeductionLogService.CreateSplitAccountDeductionLog(req, userInfo.Username, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建分账扣钱日志异常", err, c, req)
|
||||
return
|
||||
}
|
||||
systemRes.OkWithDetailed(gin.H{"id": id}, "创建成功", c)
|
||||
}
|
||||
|
||||
// UpdateSplitAccountDeductionLog 更新分账扣钱日志
|
||||
func (r *SplitAccountDeductionLogApi) UpdateSplitAccountDeductionLog(c *gin.Context) {
|
||||
var req systemReq.UpdateSplitAccountDeductionLogRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "更新分账扣钱日志请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
err := splitAccountDeductionLogService.UpdateSplitAccountDeductionLog(req, userInfo.Username, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "更新分账扣钱日志异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// DeleteSplitAccountDeductionLog 删除分账扣钱日志
|
||||
func (r *SplitAccountDeductionLogApi) DeleteSplitAccountDeductionLog(c *gin.Context) {
|
||||
var req systemReq.DeleteSplitAccountDeductionLogRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "删除分账扣钱日志请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
err := splitAccountDeductionLogService.DeleteSplitAccountDeductionLog(req, userInfo.Username, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "删除分账扣钱日志异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("删除成功", c)
|
||||
}
|
||||
69
controllers/statist.go
Normal file
69
controllers/statist.go
Normal file
@ -0,0 +1,69 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type StatistApi struct{}
|
||||
|
||||
var statistService = service.StatistService{}
|
||||
|
||||
// DashboardStatist 仪表盘统计
|
||||
func (i *StatistApi) DashboardStatist(c *gin.Context) {
|
||||
var req systemReq.DashboardStatistRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "仪表盘统计请求异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := statistService.DashboardStatist(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "仪表盘统计异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetWarehouseStatist 仓库统计接口
|
||||
func (i *StatistApi) GetWarehouseStatist(c *gin.Context) {
|
||||
var req systemReq.WarehouseStatistRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "仓库统计请求异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := statistService.GetWarehouseStatist(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "仓库统计异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetOrderStatist 订单统计接口
|
||||
func (i *StatistApi) GetOrderStatist(c *gin.Context) {
|
||||
var req systemReq.OrderStatistRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "订单统计请求异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := statistService.GetOrderStatist(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "订单统计异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
124
controllers/supplier.go
Normal file
124
controllers/supplier.go
Normal file
@ -0,0 +1,124 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type SupplierApi struct{}
|
||||
|
||||
var supplierService = service.SupplierService{}
|
||||
|
||||
// GetSupplierList 获取供应商列表
|
||||
func (r *SupplierApi) GetSupplierList(c *gin.Context) {
|
||||
var req systemReq.GetSupplierListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "供应商列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := supplierService.GetSupplierList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "供应商列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetSupplierDetail 获取供应商详情
|
||||
func (r *SupplierApi) GetSupplierDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
|
||||
if idStr == "" {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取供应商详情请求参数异常",
|
||||
"err_msg": "ID参数不能为空",
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id <= 0 {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取供应商详情请求参数异常",
|
||||
"err_msg": "ID格式错误: " + idStr,
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := supplierService.GetSupplierDetail(id, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "供应商详情异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// CreateSupplier 创建供应商
|
||||
func (r *SupplierApi) CreateSupplier(c *gin.Context) {
|
||||
var req systemReq.AddSupplierRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建供应商请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := supplierService.CreateSupplier(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建供应商异常", err, c, req)
|
||||
return
|
||||
}
|
||||
systemRes.OkWithDetailed(gin.H{"id": id}, "创建成功", c)
|
||||
}
|
||||
|
||||
// UpdateSupplier 更新供应商
|
||||
func (r *SupplierApi) UpdateSupplier(c *gin.Context) {
|
||||
var req systemReq.UpdateSupplierRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "更新供应商请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := supplierService.UpdateSupplier(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "更新供应商异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// DeleteSupplier 删除供应商
|
||||
func (r *SupplierApi) DeleteSupplier(c *gin.Context) {
|
||||
var req systemReq.DeleteSupplierRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "删除供应商请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := supplierService.DeleteSupplier(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "删除供应商异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("删除成功", c)
|
||||
}
|
||||
123
controllers/user_type.go
Normal file
123
controllers/user_type.go
Normal file
@ -0,0 +1,123 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
type UserTypeApi struct{}
|
||||
|
||||
var userTypeService = service.UserTypeService{}
|
||||
|
||||
// GetUserTypeList 获取用户类型列表
|
||||
func (r *UserTypeApi) GetUserTypeList(c *gin.Context) {
|
||||
var req systemReq.GetUserTypeListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "用户类型列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := userTypeService.GetUserTypeList(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "用户类型列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetUserTypeDetail 获取用户类型详情
|
||||
func (r *UserTypeApi) GetUserTypeDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
|
||||
if idStr == "" {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取用户类型详情请求参数异常",
|
||||
"err_msg": "ID参数不能为空",
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id <= 0 {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取用户类型详情请求参数异常",
|
||||
"err_msg": "ID格式错误: " + idStr,
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := userTypeService.GetUserTypeDetail(id)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "用户类型详情异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
// CreateUserType 创建用户类型
|
||||
func (r *UserTypeApi) CreateUserType(c *gin.Context) {
|
||||
var req systemReq.AddUserTypeRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建用户类型请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := userTypeService.CreateUserType(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建用户类型异常", err, c, req)
|
||||
return
|
||||
}
|
||||
systemRes.OkWithDetailed(gin.H{"id": id}, "创建成功", c)
|
||||
}
|
||||
|
||||
// UpdateUserType 更新用户类型
|
||||
func (r *UserTypeApi) UpdateUserType(c *gin.Context) {
|
||||
var req systemReq.UpdateUserTypeRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "更新用户类型请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := userTypeService.UpdateUserType(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "更新用户类型异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// DeleteUserType 删除用户类型
|
||||
func (r *UserTypeApi) DeleteUserType(c *gin.Context) {
|
||||
var req systemReq.DeleteUserTypeRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "删除用户类型请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := userTypeService.DeleteUserType(req)
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "删除用户类型异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("删除成功", c)
|
||||
}
|
||||
207
controllers/warehouse.go
Normal file
207
controllers/warehouse.go
Normal file
@ -0,0 +1,207 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"os"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WarehouseApi struct{}
|
||||
|
||||
var warehouseService = service.WarehouseService{}
|
||||
|
||||
func (r *WarehouseApi) GetWarehouseList(c *gin.Context) {
|
||||
var req systemReq.QueryWarehouseRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "查询仓库列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.IDs) == 0 {
|
||||
ids, err := parseIds(c)
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
req.IDs = ids
|
||||
}
|
||||
|
||||
list, total, err := warehouseService.GetWarehouseList(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询仓库列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"data": gin.H{
|
||||
"list": list,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"page_size": req.PageSize,
|
||||
},
|
||||
"msg": "查询成功",
|
||||
})
|
||||
}
|
||||
|
||||
func (r *WarehouseApi) GetWarehouseDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
|
||||
if idStr == "" {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取仓库详情请求参数异常",
|
||||
"err_msg": "ID参数不能为空",
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
var id int64
|
||||
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id <= 0 {
|
||||
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
|
||||
"source": "获取仓库详情请求参数异常",
|
||||
"err_msg": "ID格式错误: " + idStr,
|
||||
})
|
||||
systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c)
|
||||
return
|
||||
}
|
||||
|
||||
warehouse, err := warehouseService.GetWarehouseByID(id, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询仓库详情异常", err, c, gin.H{"id": id})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(warehouse, "查询成功", c)
|
||||
}
|
||||
|
||||
func (r *WarehouseApi) CreateWarehouse(c *gin.Context) {
|
||||
var req systemReq.CreateWarehouseRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "创建仓库请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
id, err := warehouseService.CreateWarehouse(req, userInfo.AboutID, userInfo.Username, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建仓库异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(gin.H{"id": id}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (r *WarehouseApi) UpdateWarehouse(c *gin.Context) {
|
||||
var req systemReq.UpdateWarehouseRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "更新仓库请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
if err := warehouseService.UpdateWarehouse(req, userInfo.AboutID, userInfo.Username, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "更新仓库异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
func (r *WarehouseApi) DeleteWarehouse(c *gin.Context) {
|
||||
var req systemReq.DeleteWarehouseRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "删除仓库请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
if err := warehouseService.DeleteWarehouse(req.ID, userInfo.AboutID, userInfo.Username, database.GetDB(c)); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "删除仓库异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
func (r *LocationApi) LocationToCsv(c *gin.Context) {
|
||||
var req systemReq.ExportLocationRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "导出库位请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := locationService.ExportLocations(req, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "导出库位异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "导出成功", c)
|
||||
}
|
||||
|
||||
func (r *LocationApi) CsvToLocation(c *gin.Context) {
|
||||
var req systemReq.ImportLocationRequest
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "导入库位请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
systemRes.FailWithValidateMessage("请上传文件", c)
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(strings.ToLower(file.Filename), ".xlsx") && !strings.HasSuffix(strings.ToLower(file.Filename), ".xls") {
|
||||
systemRes.FailWithValidateMessage("只支持Excel文件格式(.xlsx, .xls)", c)
|
||||
return
|
||||
}
|
||||
|
||||
filePath := fmt.Sprintf("excel/import_%s_%d.xlsx", time.Now().Format("20060102150405"), time.Now().UnixNano())
|
||||
if err := c.SaveUploadedFile(file, filePath); err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "保存上传文件异常", err, c, gin.H{"filename": file.Filename})
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
|
||||
"source": "删除临时文件失败",
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
result, err := locationService.ImportLocationsFromCSV(req, filePath, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "导入库位异常", err, c, gin.H{"filename": file.Filename})
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, result.Message, c)
|
||||
}
|
||||
|
||||
func (r *WarehouseApi) GetUserWarehouseMappings(c *gin.Context) {
|
||||
data, err := warehouseService.GetUserWarehouseMappings()
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取用户的仓库映射列表异常", err, c, nil)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"data": data,
|
||||
"msg": "查询成功",
|
||||
})
|
||||
}
|
||||
83
controllers/wave.go
Normal file
83
controllers/wave.go
Normal file
@ -0,0 +1,83 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/constant"
|
||||
"psi/database"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/service"
|
||||
"psi/utils"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type WaveApi struct{}
|
||||
|
||||
var waveService = service.WaveService{}
|
||||
|
||||
// GetWaveTaskList 获取波次任务列表
|
||||
func (r *WaveApi) GetWaveTaskList(c *gin.Context) {
|
||||
var req systemReq.GetWaveTaskListRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "波次任务列表请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := waveService.GetWaveTaskList(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "波次任务列表异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GetWaveTaskDetail 获取波次任务详情
|
||||
func (r *WaveApi) GetWaveTaskDetail(c *gin.Context) {
|
||||
var req systemReq.GetWaveTaskDetailRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
ValidAndFail(constant.LoggerChannelRequest, "波次任务详情请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := utils.GetUserInfo(c)
|
||||
result, err := waveService.GetWaveTaskDetail(req.ID, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||
if err != nil {
|
||||
utils.FailWithRequestLog(constant.LoggerChannelWork, "波次任务详情异常", err, c, req)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||
}
|
||||
|
||||
func (r *WaveApi) GetWaveStatusById(c *gin.Context) {
|
||||
waveIDStr := c.Query("id")
|
||||
if waveIDStr == "" {
|
||||
systemRes.FailWithValidateMessage("参数错误: id不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
waveID, err := strconv.ParseInt(waveIDStr, 10, 64)
|
||||
if err != nil || waveID <= 0 {
|
||||
systemRes.FailWithValidateMessage("参数错误: id格式不正确", c)
|
||||
return
|
||||
}
|
||||
|
||||
waveTask, err := waveService.GetWaveStatusById(waveID, database.GetDB(c))
|
||||
if err != nil {
|
||||
systemRes.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
systemRes.OkWithDetailed(gin.H{
|
||||
"id": waveTask.ID,
|
||||
"status": waveTask.Status,
|
||||
}, "查询成功", c)
|
||||
}
|
||||
45
database/db_helper.go
Normal file
45
database/db_helper.go
Normal file
@ -0,0 +1,45 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// GetTenantDBFromContext 从 gin.Context 获取租户 DB
|
||||
// 已弃用,请使用 GetDB
|
||||
func GetTenantDBFromContext(aboutID int64) (*gorm.DB, error) {
|
||||
return GetTenantDB(aboutID)
|
||||
}
|
||||
|
||||
// GetDB 从 gin.Context 获取当前应使用的数据库连接
|
||||
// 如果上下文中有租户DB则返回租户DB,否则返回主库
|
||||
// 使用方式: db := database.GetDB(c)
|
||||
func GetDB(c *gin.Context) *gorm.DB {
|
||||
if c == nil {
|
||||
return DB
|
||||
}
|
||||
if tenantDB, exists := c.Get("tenant_db"); exists {
|
||||
if db, ok := tenantDB.(*gorm.DB); ok {
|
||||
return db
|
||||
}
|
||||
}
|
||||
return DB
|
||||
}
|
||||
|
||||
// GetDBWithFallback 获取数据库连接,支持可选的 DB 参数覆盖
|
||||
// 使用方式: db := database.GetDBWithFallback(c, dbOverride)
|
||||
func GetDBWithFallback(c *gin.Context, dbOverride *gorm.DB) *gorm.DB {
|
||||
if dbOverride != nil {
|
||||
return dbOverride
|
||||
}
|
||||
return GetDB(c)
|
||||
}
|
||||
|
||||
// OptionalDB 可选 DB 参数处理
|
||||
// 使用方式: db := database.OptionalDB(db...)
|
||||
func OptionalDB(db ...*gorm.DB) *gorm.DB {
|
||||
if len(db) > 0 && db[0] != nil {
|
||||
return db[0]
|
||||
}
|
||||
return DB
|
||||
}
|
||||
301
database/mysql.go
Normal file
301
database/mysql.go
Normal file
@ -0,0 +1,301 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"log"
|
||||
"os"
|
||||
"psi/config"
|
||||
"psi/models"
|
||||
"psi/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
func Init() {
|
||||
cfg := config.AppConfig
|
||||
|
||||
var err error
|
||||
// 使用MySQL驱动连接数据库,开启慢查询日志(>=200ms)
|
||||
slowLogger := logger.New(
|
||||
log.New(os.Stdout, "\r\n", log.LstdFlags),
|
||||
logger.Config{
|
||||
SlowThreshold: 200 * time.Millisecond, // 慢SQL阈值
|
||||
LogLevel: logger.Warn, // 只输出警告和错误
|
||||
IgnoreRecordNotFoundError: true, // 忽略RecordNotFound
|
||||
Colorful: false,
|
||||
},
|
||||
)
|
||||
DB, err = gorm.Open(mysql.Open(cfg.GetDSN()), &gorm.Config{
|
||||
Logger: slowLogger,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("数据库连接失败:", err)
|
||||
}
|
||||
// 获取底层的 sql.DB 对象以配置连接池
|
||||
sqlDB, err := DB.DB()
|
||||
if err != nil {
|
||||
log.Fatal("获取数据库连接失败:", err)
|
||||
}
|
||||
// 设置连接池参数
|
||||
sqlDB.SetMaxIdleConns(50) // 最大空闲连接数(提升以减少连接建立开销)
|
||||
sqlDB.SetMaxOpenConns(200) // 最大打开连接数(提升以应对高并发)
|
||||
sqlDB.SetConnMaxLifetime(30 * time.Minute) // 连接最大生命周期(30分钟)
|
||||
sqlDB.SetConnMaxIdleTime(5 * time.Minute) // 连接最大空闲时间(5分钟,减少频繁创建连接)
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工表'").AutoMigrate(&models.Employee{})
|
||||
if err != nil {
|
||||
log.Fatal("Employee表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='书籍信息表'").AutoMigrate(&models.BookInfo{})
|
||||
if err != nil {
|
||||
log.Fatal("BookInfo表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户表'").AutoMigrate(&models.Customer{})
|
||||
if err != nil {
|
||||
log.Fatal("Customer表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存表'").AutoMigrate(&models.Inventory{})
|
||||
if err != nil {
|
||||
log.Fatal("Inventory表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存明细表'").AutoMigrate(&models.InventoryDetail{})
|
||||
if err != nil {
|
||||
log.Fatal("InventoryDetail表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存流水表'").AutoMigrate(&models.InventoryLog{})
|
||||
if err != nil {
|
||||
log.Fatal("InventoryLog表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库位表'").AutoMigrate(&models.Location{})
|
||||
if err != nil {
|
||||
log.Fatal("Location表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产品分类表'").AutoMigrate(&models.ProductCategory{})
|
||||
if err != nil {
|
||||
log.Fatal("ProductCategory表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产品表'").AutoMigrate(&models.Product{})
|
||||
if err != nil {
|
||||
log.Fatal("Product表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产品单位转换表'").AutoMigrate(&models.ProductUnitConversion{})
|
||||
if err != nil {
|
||||
log.Fatal("ProductUnitConversion表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产品序列号表'").AutoMigrate(&models.ProductSerial{})
|
||||
if err != nil {
|
||||
log.Fatal("ProductSerial表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购订单主表'").AutoMigrate(&models.PurchaseOrder{})
|
||||
if err != nil {
|
||||
log.Fatal("PurchaseOrder表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购订单明细表'").AutoMigrate(&models.PurchaseOrderItem{})
|
||||
if err != nil {
|
||||
log.Fatal("PurchaseOrderItem表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='打印任务表'").AutoMigrate(&models.PrintTask{})
|
||||
if err != nil {
|
||||
log.Fatal("PrintTask表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='打印日志表'").AutoMigrate(&models.PrintLog{})
|
||||
if err != nil {
|
||||
log.Fatal("PrintLog表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入库单主表'").AutoMigrate(&models.ReceivingOrder{})
|
||||
if err != nil {
|
||||
log.Fatal("ReceivingOrder表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入库单明细表'").AutoMigrate(&models.ReceivingOrderItem{})
|
||||
if err != nil {
|
||||
log.Fatal("ReceivingOrderItem表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商表'").AutoMigrate(&models.Supplier{})
|
||||
if err != nil {
|
||||
log.Fatal("Supplier表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='销售订单主表'").AutoMigrate(&models.SalesOrder{})
|
||||
if err != nil {
|
||||
log.Fatal("SalesOrder表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='销售订单明细表'").AutoMigrate(&models.SalesOrderItem{})
|
||||
if err != nil {
|
||||
log.Fatal("SalesOrderItem表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='仓库表'").AutoMigrate(&models.Warehouse{})
|
||||
if err != nil {
|
||||
log.Fatal("Warehouse表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='波次单主表'").AutoMigrate(&models.WaveHeader{})
|
||||
if err != nil {
|
||||
log.Fatal("WaveHeader表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='波次任务表'").AutoMigrate(&models.WaveTask{})
|
||||
if err != nil {
|
||||
log.Fatal("WaveTask表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='波次任务明细表'").AutoMigrate(&models.WaveTaskDetail{})
|
||||
if err != nil {
|
||||
log.Fatal("WaveTaskDetail表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='小车表'").AutoMigrate(&models.Car{})
|
||||
if err != nil {
|
||||
log.Fatal("Car表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='小车店铺关联表'").AutoMigrate(&models.CarShop{})
|
||||
if err != nil {
|
||||
log.Fatal("CarShop表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='店铺表'").AutoMigrate(&models.Shop{})
|
||||
if err != nil {
|
||||
log.Fatal("Shop表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工等级表'").AutoMigrate(&models.EmployeeLevel{})
|
||||
if err != nil {
|
||||
log.Fatal("EmployeeLevel表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工等级日志表'").AutoMigrate(&models.EmployeeLevelLog{})
|
||||
if err != nil {
|
||||
log.Fatal("EmployeeLevelLog表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工配置表'").AutoMigrate(&models.EmployeeSettings{})
|
||||
if err != nil {
|
||||
log.Fatal("EmployeeSettings表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='盘库单主表'").AutoMigrate(&models.StockCheck{})
|
||||
if err != nil {
|
||||
log.Fatal("StockCheck表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='盘库单明细表'").AutoMigrate(&models.StockCheckItem{})
|
||||
if err != nil {
|
||||
log.Fatal("StockCheckItem表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部任务表'").AutoMigrate(&models.OutTask{})
|
||||
if err != nil {
|
||||
log.Fatal("OutTask表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部任务异常日志表'").AutoMigrate(&models.OutTaskLog{})
|
||||
if err != nil {
|
||||
log.Fatal("OutTaskLog表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库单主表'").AutoMigrate(&models.OutboundOrder{})
|
||||
if err != nil {
|
||||
log.Fatal("OutboundOrder表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库单明细表'").AutoMigrate(&models.OutboundOrderItem{})
|
||||
if err != nil {
|
||||
log.Fatal("OutboundOrderItem表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发货单主表'").AutoMigrate(&models.ShippingOrder{})
|
||||
if err != nil {
|
||||
log.Fatal("ShippingOrder表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发货单明细表'").AutoMigrate(&models.ShippingOrderItem{})
|
||||
if err != nil {
|
||||
log.Fatal("ShippingOrderItem表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运费模板表'").AutoMigrate(&models.Logistics{})
|
||||
if err != nil {
|
||||
log.Fatal("Logistics表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品日志表'").AutoMigrate(&models.ProductLog{})
|
||||
if err != nil {
|
||||
log.Fatal("ProductLog表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户类型表'").AutoMigrate(&models.UserType{})
|
||||
if err != nil {
|
||||
log.Fatal("UserType表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户映射表'").AutoMigrate(&models.UserMapping{})
|
||||
if err != nil {
|
||||
log.Fatal("UserMapping表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='分账配置表'").AutoMigrate(&models.SplitAccountConfig{})
|
||||
if err != nil {
|
||||
log.Fatal("SplitAccountConfig表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='分账扣钱日志表'").AutoMigrate(&models.SplitAccountDeductionLog{})
|
||||
if err != nil {
|
||||
log.Fatal("SplitAccountDeductionLog表迁移失败:", err)
|
||||
}
|
||||
|
||||
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置表'").AutoMigrate(&models.Config{})
|
||||
if err != nil {
|
||||
log.Fatal("Config表迁移失败:", err)
|
||||
}
|
||||
|
||||
createDefaultAdmin()
|
||||
InitTaskDB()
|
||||
log.Println("MySQL数据库连接成功:", cfg.Database.Host, cfg.Database.Name)
|
||||
}
|
||||
|
||||
func createDefaultAdmin() {
|
||||
var count int64
|
||||
DB.Model(&models.Employee{}).Where("role = ?", 255).Count(&count)
|
||||
if count == 0 {
|
||||
// 创建默认管理员账号:账号init_00000,密码admin123
|
||||
hashedPassword, _ := utils.HashPassword("admin123")
|
||||
admin := models.Employee{
|
||||
EmployeeIDStr: "00000",
|
||||
Username: "init_00000",
|
||||
Password: hashedPassword,
|
||||
Name: "系统管理员",
|
||||
Role: 255,
|
||||
Score: 0,
|
||||
Status: 1,
|
||||
Fid: 0,
|
||||
AboutId: 0,
|
||||
From: "system",
|
||||
CreatedAt: time.Now().Unix(),
|
||||
UpdatedAt: time.Now().Unix(),
|
||||
}
|
||||
DB.Create(&admin)
|
||||
log.Println("已创建默认管理员账号: init_00000 / admin123")
|
||||
}
|
||||
}
|
||||
48
database/task.go
Normal file
48
database/task.go
Normal file
@ -0,0 +1,48 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"log"
|
||||
"psi/config"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
// TaskDB 任务数据库长连接(腾讯云 MySQL,用于导入后写 t_shop_goods_published)
|
||||
var TaskDB *gorm.DB
|
||||
|
||||
func InitTaskDB() {
|
||||
cfg := config.AppConfig
|
||||
|
||||
slowLogger := logger.New(
|
||||
log.New(log.Writer(), "\r\n", log.LstdFlags),
|
||||
logger.Config{
|
||||
SlowThreshold: 200 * time.Millisecond,
|
||||
LogLevel: logger.Warn,
|
||||
IgnoreRecordNotFoundError: true,
|
||||
Colorful: false,
|
||||
},
|
||||
)
|
||||
|
||||
var err error
|
||||
TaskDB, err = gorm.Open(mysql.Open(cfg.GetTaskDSN()), &gorm.Config{
|
||||
Logger: slowLogger,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("任务数据库连接失败:", err)
|
||||
}
|
||||
|
||||
// 配置连接池
|
||||
sqlDB, err := TaskDB.DB()
|
||||
if err != nil {
|
||||
log.Fatal("获取任务数据库连接池失败:", err)
|
||||
}
|
||||
sqlDB.SetMaxIdleConns(10) // 最大空闲连接
|
||||
sqlDB.SetMaxOpenConns(50) // 最大打开连接
|
||||
sqlDB.SetConnMaxLifetime(10 * time.Minute) // 连接最大存活10分钟
|
||||
sqlDB.SetConnMaxIdleTime(2 * time.Minute) // 空闲连接2分钟后关闭
|
||||
|
||||
log.Println("任务数据库连接成功:", cfg.TaskDatabase.Host, cfg.TaskDatabase.Name)
|
||||
}
|
||||
415
database/tenant.go
Normal file
415
database/tenant.go
Normal file
@ -0,0 +1,415 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"psi/config"
|
||||
"psi/models"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TenantManager 租户数据库管理器
|
||||
type TenantManager struct {
|
||||
connections map[int64]*gorm.DB // about_id -> DB连接
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
var tenantManager *TenantManager
|
||||
|
||||
func init() {
|
||||
tenantManager = &TenantManager{
|
||||
connections: make(map[int64]*gorm.DB),
|
||||
}
|
||||
}
|
||||
|
||||
// GetTenantDB 获取租户数据库连接(如果不存在则创建)
|
||||
func GetTenantDB(aboutID int64) (*gorm.DB, error) {
|
||||
if aboutID == 0 {
|
||||
return DB, nil // 主库
|
||||
}
|
||||
|
||||
tenantManager.mu.RLock()
|
||||
conn, exists := tenantManager.connections[aboutID]
|
||||
tenantManager.mu.RUnlock()
|
||||
|
||||
if exists {
|
||||
// 增量迁移:确保现有连接也有新表
|
||||
migrateIncrementalTenantTables(conn)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// 需要创建新连接
|
||||
tenantManager.mu.Lock()
|
||||
defer tenantManager.mu.Unlock()
|
||||
|
||||
// 双重检查
|
||||
if conn, exists = tenantManager.connections[aboutID]; exists {
|
||||
migrateIncrementalTenantTables(conn)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// 创建租户数据库连接
|
||||
dbName := fmt.Sprintf("psi_%d", aboutID)
|
||||
conn, err := createTenantDB(dbName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tenantManager.connections[aboutID] = conn
|
||||
log.Printf("租户数据库连接创建成功: %s", dbName)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// createTenantDB 创建租户数据库(如果不存在则新建库和表)
|
||||
func createTenantDB(dbName string) (*gorm.DB, error) {
|
||||
cfg := config.AppConfig
|
||||
|
||||
// 先用主库连接检查/创建租户数据库
|
||||
mainDSN := cfg.GetDSN()
|
||||
mainDB, err := gorm.Open(mysql.Open(mainDSN), &gorm.Config{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("连接主库失败: %w", err)
|
||||
}
|
||||
|
||||
// 检查数据库是否存在
|
||||
var exists string
|
||||
err = mainDB.Raw(fmt.Sprintf("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '%s'", dbName)).Scan(&exists).Error
|
||||
needCreate := err != nil || exists == ""
|
||||
|
||||
// 创建数据库(如果不存在)
|
||||
if needCreate {
|
||||
createSQL := fmt.Sprintf("CREATE DATABASE `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci", dbName)
|
||||
if err = mainDB.Exec(createSQL).Error; err != nil {
|
||||
// 如果数据库已存在(Error 1007),忽略该错误
|
||||
if strings.Contains(err.Error(), "1007") {
|
||||
log.Printf("租户数据库已存在: %s", dbName)
|
||||
} else {
|
||||
sqlMainDB, _ := mainDB.DB()
|
||||
sqlMainDB.Close()
|
||||
return nil, fmt.Errorf("创建租户数据库失败: %w", err)
|
||||
}
|
||||
} else {
|
||||
log.Printf("租户数据库创建成功: %s", dbName)
|
||||
}
|
||||
}
|
||||
sqlMainDB, _ := mainDB.DB()
|
||||
sqlMainDB.Close()
|
||||
|
||||
// 连接租户数据库
|
||||
tenantDSN := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=10s&readTimeout=30s&writeTimeout=30s&interpolateParams=true&multiStatements=true&allowNativePasswords=true&checkConnLiveness=true",
|
||||
cfg.Database.User, cfg.Database.Password, cfg.Database.Host, cfg.Database.Port, dbName)
|
||||
|
||||
tenantDB, err := gorm.Open(mysql.Open(tenantDSN), &gorm.Config{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("连接租户数据库失败: %w", err)
|
||||
}
|
||||
|
||||
// 配置连接池
|
||||
sqlDB, err := tenantDB.DB()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取租户数据库连接池失败: %w", err)
|
||||
}
|
||||
sqlDB.SetMaxIdleConns(20) // 最大空闲连接数
|
||||
sqlDB.SetMaxOpenConns(100) // 最大打开连接数
|
||||
sqlDB.SetConnMaxLifetime(10 * time.Minute) // 单个连接最大存活10分钟
|
||||
sqlDB.SetConnMaxIdleTime(2 * time.Minute) // 空闲连接2分钟后关闭
|
||||
|
||||
migrateTenantTables(tenantDB)
|
||||
|
||||
return tenantDB, nil
|
||||
}
|
||||
|
||||
// migrateTenantTables 迁移租户业务表
|
||||
func migrateTenantTables(db *gorm.DB) {
|
||||
// 迁移所有业务表(不包括 Employee 表,Employee 在主库)
|
||||
err := db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户表'").AutoMigrate(&models.Customer{})
|
||||
if err != nil {
|
||||
log.Printf("Customer表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存汇总表'").AutoMigrate(&models.Inventory{})
|
||||
if err != nil {
|
||||
log.Printf("Inventory表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存明细表(库位级)'").AutoMigrate(&models.InventoryDetail{})
|
||||
if err != nil {
|
||||
log.Printf("InventoryDetail表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存流水表'").AutoMigrate(&models.InventoryLog{})
|
||||
if err != nil {
|
||||
log.Printf("InventoryLog表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库位表'").AutoMigrate(&models.Location{})
|
||||
if err != nil {
|
||||
log.Printf("Location表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品分类表'").AutoMigrate(&models.ProductCategory{})
|
||||
if err != nil {
|
||||
log.Printf("ProductCategory表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表'").AutoMigrate(&models.Product{})
|
||||
if err != nil {
|
||||
log.Printf("Product表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品单位换算表'").AutoMigrate(&models.ProductUnitConversion{})
|
||||
if err != nil {
|
||||
log.Printf("ProductUnitConversion表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产品序列号表'").AutoMigrate(&models.ProductSerial{})
|
||||
if err != nil {
|
||||
log.Printf("ProductSerial表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购订单主表'").AutoMigrate(&models.PurchaseOrder{})
|
||||
if err != nil {
|
||||
log.Printf("PurchaseOrder表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购订单明细表'").AutoMigrate(&models.PurchaseOrderItem{})
|
||||
if err != nil {
|
||||
log.Printf("PurchaseOrderItem表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入库单主表'").AutoMigrate(&models.ReceivingOrder{})
|
||||
if err != nil {
|
||||
log.Printf("ReceivingOrder表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入库单明细表'").AutoMigrate(&models.ReceivingOrderItem{})
|
||||
if err != nil {
|
||||
log.Printf("ReceivingOrderItem表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商表'").AutoMigrate(&models.Supplier{})
|
||||
if err != nil {
|
||||
log.Printf("Supplier表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='销售订单主表'").AutoMigrate(&models.SalesOrder{})
|
||||
if err != nil {
|
||||
log.Printf("SalesOrder表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='销售订单明细表'").AutoMigrate(&models.SalesOrderItem{})
|
||||
if err != nil {
|
||||
log.Printf("SalesOrderItem表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='仓库表'").AutoMigrate(&models.Warehouse{})
|
||||
if err != nil {
|
||||
log.Printf("Warehouse表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='波次单主表'").AutoMigrate(&models.WaveHeader{})
|
||||
if err != nil {
|
||||
log.Printf("WaveHeader表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='波次任务表'").AutoMigrate(&models.WaveTask{})
|
||||
if err != nil {
|
||||
log.Printf("WaveTask表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='波次任务明细表'").AutoMigrate(&models.WaveTaskDetail{})
|
||||
if err != nil {
|
||||
log.Printf("WaveTaskDetail表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='打印任务表'").AutoMigrate(&models.PrintTask{})
|
||||
if err != nil {
|
||||
log.Printf("PrintTask表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='打印日志表'").AutoMigrate(&models.PrintLog{})
|
||||
if err != nil {
|
||||
log.Printf("PrintLog表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='小车表'").AutoMigrate(&models.Car{})
|
||||
if err != nil {
|
||||
log.Printf("Car表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='小车店铺关联表'").AutoMigrate(&models.CarShop{})
|
||||
if err != nil {
|
||||
log.Printf("CarShop表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='店铺表'").AutoMigrate(&models.Shop{})
|
||||
if err != nil {
|
||||
log.Printf("Shop表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='盘库单主表'").AutoMigrate(&models.StockCheck{})
|
||||
if err != nil {
|
||||
log.Printf("StockCheck表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='盘库单明细表'").AutoMigrate(&models.StockCheckItem{})
|
||||
if err != nil {
|
||||
log.Printf("StockCheckItem表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部任务表'").AutoMigrate(&models.OutTask{})
|
||||
if err != nil {
|
||||
log.Printf("OutTask表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部任务异常日志表'").AutoMigrate(&models.OutTaskLog{})
|
||||
if err != nil {
|
||||
log.Printf("OutTaskLog表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库单主表'").AutoMigrate(&models.OutboundOrder{})
|
||||
if err != nil {
|
||||
log.Printf("OutboundOrder表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库单明细表'").AutoMigrate(&models.OutboundOrderItem{})
|
||||
if err != nil {
|
||||
log.Printf("OutboundOrderItem表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发货单主表'").AutoMigrate(&models.ShippingOrder{})
|
||||
if err != nil {
|
||||
log.Printf("ShippingOrder表迁移失败: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发货单明细表'").AutoMigrate(&models.ShippingOrderItem{})
|
||||
if err != nil {
|
||||
log.Printf("ShippingOrderItem表迁移失败: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运费模板表'").AutoMigrate(&models.Logistics{})
|
||||
if err != nil {
|
||||
log.Printf("Logistics表迁移失败: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统计表'").AutoMigrate(&models.Statist{})
|
||||
if err != nil {
|
||||
log.Printf("Statist表迁移失败: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='分账配置表'").AutoMigrate(&models.SplitAccountConfig{})
|
||||
if err != nil {
|
||||
log.Printf("SplitAccountConfig表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='分账扣钱日志表'").AutoMigrate(&models.SplitAccountDeductionLog{})
|
||||
if err != nil {
|
||||
log.Printf("SplitAccountDeductionLog表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库单库位变更记录表'").AutoMigrate(&models.OutboundOrderLocationLog{})
|
||||
if err != nil {
|
||||
log.Printf("OutboundOrderLocationLog表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置表'").AutoMigrate(&models.Config{})
|
||||
if err != nil {
|
||||
log.Printf("Config表迁移警告: %v", err)
|
||||
}
|
||||
|
||||
log.Println("租户业务表迁移完成")
|
||||
//tableOptions := "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
||||
//
|
||||
//modelsToMigrate := []interface{}{
|
||||
// //&models.BookInfo{},
|
||||
// &models.Customer{},
|
||||
// &models.Inventory{},
|
||||
// &models.InventoryDetail{},
|
||||
// &models.InventoryLog{},
|
||||
// &models.Location{},
|
||||
// &models.ProductCategory{},
|
||||
// &models.Product{},
|
||||
// &models.ProductUnitConversion{},
|
||||
// &models.ProductSerial{},
|
||||
// &models.PurchaseOrder{},
|
||||
// &models.PurchaseOrderItem{},
|
||||
// &models.ReceivingOrder{},
|
||||
// &models.ReceivingOrderItem{},
|
||||
// &models.Supplier{},
|
||||
// &models.SalesOrder{},
|
||||
// &models.SalesOrderItem{},
|
||||
// &models.Warehouse{},
|
||||
// &models.WaveHeader{},
|
||||
// &models.WaveTask{},
|
||||
// &models.WaveTaskDetail{},
|
||||
// &models.PrintTask{},
|
||||
// &models.PrintLog{},
|
||||
// &models.Car{},
|
||||
// &models.CarShop{},
|
||||
// &models.Shop{},
|
||||
// &models.StockCheck{},
|
||||
// &models.StockCheckItem{},
|
||||
// &models.OutTask{},
|
||||
// &models.OutTaskLog{},
|
||||
// &models.OutboundOrder{},
|
||||
// &models.OutboundOrderItem{},
|
||||
//}
|
||||
//
|
||||
//for _, model := range modelsToMigrate {
|
||||
// err := db.Set("gorm:table_options", tableOptions).AutoMigrate(model)
|
||||
// if err != nil {
|
||||
// log.Printf("租户表迁移警告: %v", err)
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
// CloseTenantConnections 关闭所有租户连接(用于优雅关闭)
|
||||
func CloseTenantConnections() {
|
||||
tenantManager.mu.Lock()
|
||||
defer tenantManager.mu.Unlock()
|
||||
|
||||
for aboutID, conn := range tenantManager.connections {
|
||||
sqlDB, err := conn.DB()
|
||||
if err == nil {
|
||||
sqlDB.Close()
|
||||
}
|
||||
delete(tenantManager.connections, aboutID)
|
||||
}
|
||||
log.Println("所有租户数据库连接已关闭")
|
||||
}
|
||||
|
||||
// migrateIncrementalTenantTables 增量迁移:补充已有租户数据库可能存在的新表
|
||||
func migrateIncrementalTenantTables(db *gorm.DB) {
|
||||
// 检查 split_account_config 表是否存在,不存在则创建
|
||||
if !db.Migrator().HasTable(&models.SplitAccountConfig{}) {
|
||||
err := db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='分账配置表'").AutoMigrate(&models.SplitAccountConfig{})
|
||||
if err != nil {
|
||||
log.Printf("SplitAccountConfig表增量迁移警告: %v", err)
|
||||
} else {
|
||||
log.Println("SplitAccountConfig表增量迁移成功")
|
||||
}
|
||||
}
|
||||
|
||||
if !db.Migrator().HasTable(&models.SplitAccountDeductionLog{}) {
|
||||
err := db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='分账扣钱日志表'").AutoMigrate(&models.SplitAccountDeductionLog{})
|
||||
if err != nil {
|
||||
log.Printf("SplitAccountDeductionLog表增量迁移警告: %v", err)
|
||||
} else {
|
||||
log.Println("SplitAccountDeductionLog表增量迁移成功")
|
||||
}
|
||||
}
|
||||
|
||||
if !db.Migrator().HasTable(&models.Config{}) {
|
||||
err := db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置表'").AutoMigrate(&models.Config{})
|
||||
if err != nil {
|
||||
log.Printf("Config表增量迁移警告: %v", err)
|
||||
} else {
|
||||
log.Println("Config表增量迁移成功")
|
||||
}
|
||||
}
|
||||
}
|
||||
162
es/es.go
Normal file
162
es/es.go
Normal file
@ -0,0 +1,162 @@
|
||||
package es
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/elastic/go-elasticsearch/v8"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BookPicObj 用于 book_pic
|
||||
type BookPicObj struct {
|
||||
LocalPath string `json:"localPath"`
|
||||
PddPath string `json:"pddPath"`
|
||||
}
|
||||
|
||||
// BookPicSObj 用于 book_pic_s
|
||||
type BookPicSObj struct {
|
||||
LocalPath string `json:"localPath"`
|
||||
PddResponse string `json:"pddResponse"`
|
||||
}
|
||||
|
||||
// BookDefPicObj 用于 book_def_pic
|
||||
type BookDefPicObj struct {
|
||||
LocalPath string `json:"localPath"`
|
||||
PddPath string `json:"pddPath"`
|
||||
}
|
||||
type CatIdObject struct {
|
||||
PinDuoDuoCatId string `json:"pin_duo_duo_cat_id"` // 拼多多分类 ID
|
||||
KongFuZiCatId string `json:"kong_fu_zi_cat_id"` // 孔夫子分类 ID
|
||||
XianYuCatId string `json:"xian_yu_cat_id"` // 闲鱼分类 ID
|
||||
}
|
||||
|
||||
// FlexibleString 处理可能是字符串或数组的字段
|
||||
type FlexibleString struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
// MarshalJSON 实现自定义 JSON 序列化
|
||||
func (f FlexibleString) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(f.Value)
|
||||
}
|
||||
|
||||
// UnmarshalJSON 实现自定义 JSON 反序列化
|
||||
func (f *FlexibleString) UnmarshalJSON(data []byte) error {
|
||||
var value string
|
||||
if err := json.Unmarshal(data, &value); err != nil {
|
||||
return err
|
||||
}
|
||||
f.Value = value
|
||||
return nil
|
||||
}
|
||||
|
||||
type NumberOrString string
|
||||
|
||||
func (n *NumberOrString) UnmarshalJSON(data []byte) error {
|
||||
s := strings.TrimSpace(string(data))
|
||||
|
||||
// 如果是数字(不以引号开头)
|
||||
if len(s) > 0 && s[0] != '"' {
|
||||
*n = NumberOrString(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 普通字符串
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err != nil {
|
||||
return err
|
||||
}
|
||||
*n = NumberOrString(str)
|
||||
return nil
|
||||
}
|
||||
|
||||
type Float64OrString float64
|
||||
|
||||
func (f *Float64OrString) UnmarshalJSON(b []byte) error {
|
||||
// 去掉空值
|
||||
if string(b) == "null" || len(b) == 0 {
|
||||
*f = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// 尝试解析为 float
|
||||
var num float64
|
||||
if err := json.Unmarshal(b, &num); err == nil {
|
||||
*f = Float64OrString(num)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 尝试解析为 string
|
||||
var str string
|
||||
if err := json.Unmarshal(b, &str); err == nil {
|
||||
if str == "" {
|
||||
*f = 0
|
||||
return nil
|
||||
}
|
||||
parsed, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*f = Float64OrString(parsed)
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("无法解析 fix_price: %s", string(b))
|
||||
}
|
||||
|
||||
// ESClient 封装 Elasticsearch 客户端
|
||||
type ESClient struct {
|
||||
Client *elasticsearch.Client
|
||||
}
|
||||
|
||||
var DefaultClient *ESClient
|
||||
|
||||
// Init 初始化默认 ES 客户端
|
||||
func Init(addresses []string, username, password string) error {
|
||||
client, err := NewESClient(addresses, username, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
DefaultClient = client
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewESClient 初始化客户端
|
||||
func NewESClient(addresses []string, username, password string) (*ESClient, error) {
|
||||
|
||||
cfg := elasticsearch.Config{
|
||||
Addresses: addresses,
|
||||
Username: username,
|
||||
Password: password,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
MaxIdleConnsPerHost: 100,
|
||||
ResponseHeaderTimeout: 60 * time.Second,
|
||||
},
|
||||
CompressRequestBody: true,
|
||||
}
|
||||
|
||||
client, err := elasticsearch.NewClient(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建 ES 客户端失败: %v", err)
|
||||
}
|
||||
|
||||
res, err := client.Info()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ES 连接失败: %v", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.IsError() {
|
||||
return nil, fmt.Errorf("ES 返回错误: %s", res.String())
|
||||
}
|
||||
|
||||
fmt.Println("✅ Elasticsearch 连接成功")
|
||||
return &ESClient{Client: client}, nil
|
||||
}
|
||||
BIN
fonts/youaimoshouheiti-regular.ttf
Normal file
BIN
fonts/youaimoshouheiti-regular.ttf
Normal file
Binary file not shown.
70
go.mod
Normal file
70
go.mod
Normal file
@ -0,0 +1,70 @@
|
||||
module psi
|
||||
|
||||
go 1.25.7
|
||||
|
||||
require (
|
||||
github.com/boombuler/barcode v1.1.0
|
||||
github.com/elastic/go-elasticsearch/v8 v8.19.3
|
||||
github.com/gin-gonic/gin v1.11.0
|
||||
github.com/go-playground/validator/v10 v10.30.1
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.9.4
|
||||
github.com/xuri/excelize/v2 v2.10.1
|
||||
golang.org/x/crypto v0.48.0
|
||||
golang.org/x/image v0.39.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/datatypes v1.2.7
|
||||
gorm.io/driver/mysql v1.6.0
|
||||
gorm.io/gorm v1.31.1
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.2.0 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic v1.15.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/elastic/elastic-transport-go/v8 v8.8.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lestrrat-go/strftime v1.1.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||
github.com/richardlehane/mscfb v1.0.6 // indirect
|
||||
github.com/richardlehane/msoleps v1.0.6 // indirect
|
||||
github.com/tiendc/go-deepcopy v1.7.2 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||
github.com/xuri/efp v0.0.1 // indirect
|
||||
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
|
||||
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
golang.org/x/arch v0.24.0 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
183
go.sum
Normal file
183
go.sum
Normal file
@ -0,0 +1,183 @@
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
|
||||
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
|
||||
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/elastic/elastic-transport-go/v8 v8.8.0 h1:7k1Ua+qluFr6p1jfJjGDl97ssJS/P7cHNInzfxgBQAo=
|
||||
github.com/elastic/elastic-transport-go/v8 v8.8.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk=
|
||||
github.com/elastic/go-elasticsearch/v8 v8.19.3 h1:5LDg0hfGJXBa9Y+2QlUgRTsNJ/7rm7oNidydtFAq0LI=
|
||||
github.com/elastic/go-elasticsearch/v8 v8.19.3/go.mod h1:tHJQdInFa6abmDbDCEH2LJja07l/SIpaGpJcm13nt7s=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
|
||||
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
|
||||
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
|
||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
|
||||
github.com/lestrrat-go/strftime v1.1.1 h1:zgf8QCsgj27GlKBy3SU9/8MMgegZ8UCzlCyHYrUF0QU=
|
||||
github.com/lestrrat-go/strftime v1.1.1/go.mod h1:YDrzHJAODYQ+xxvrn5SG01uFIQAeDTzpxNVppCz7Nmw=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
|
||||
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/richardlehane/mscfb v1.0.6 h1:eN3bvvZCp00bs7Zf52bxNwAx5lJDBK1tCuH19qq5aC8=
|
||||
github.com/richardlehane/mscfb v1.0.6/go.mod h1:pe0+IUIc0AHh0+teNzBlJCtSyZdFOGgV4ZK9bsoV+Jo=
|
||||
github.com/richardlehane/msoleps v1.0.6 h1:9BvkpjvD+iUBalUY4esMwv6uBkfOip/Lzvd93jvR9gg=
|
||||
github.com/richardlehane/msoleps v1.0.6/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrIj44=
|
||||
github.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
|
||||
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
||||
github.com/xuri/excelize/v2 v2.10.1 h1:V62UlqopMqha3kOpnlHy2CcRVw1V8E63jFoWUmMzxN0=
|
||||
github.com/xuri/excelize/v2 v2.10.1/go.mod h1:iG5tARpgaEeIhTqt3/fgXCGoBRt4hNXgCp3tfXKoOIc=
|
||||
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
|
||||
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
|
||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y=
|
||||
golang.org/x/arch v0.24.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
|
||||
golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk=
|
||||
gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
|
||||
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
|
||||
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
|
||||
gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U=
|
||||
gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A=
|
||||
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||
gorm.io/driver/sqlserver v1.6.0 h1:VZOBQVsVhkHU/NzNhRJKoANt5pZGQAS1Bwc6m6dgfnc=
|
||||
gorm.io/driver/sqlserver v1.6.0/go.mod h1:WQzt4IJo/WHKnckU9jXBLMJIVNMVeTu25dnOzehntWw=
|
||||
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||
BIN
linux/go_build_main_go_linux_linux
Normal file
BIN
linux/go_build_main_go_linux_linux
Normal file
Binary file not shown.
43
main.go
Normal file
43
main.go
Normal file
@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"psi/config"
|
||||
"psi/database"
|
||||
router "psi/routes"
|
||||
"psi/utils"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println("=== PSI 服务启动中 ===")
|
||||
|
||||
// 初始化配置
|
||||
log.Println("[1/4] 初始化配置...")
|
||||
config.Init()
|
||||
log.Println("[1/4] 配置初始化完成")
|
||||
|
||||
// 初始化日志
|
||||
log.Println("[2/4] 初始化日志...")
|
||||
if err := utils.InitLogger(); err != nil {
|
||||
log.Printf("[2/4] 日志初始化失败: %v", err)
|
||||
} else {
|
||||
log.Println("[2/4] 日志初始化完成")
|
||||
}
|
||||
|
||||
// 初始化数据库
|
||||
log.Println("[3/4] 初始化数据库...")
|
||||
database.Init()
|
||||
log.Println("[3/4] 数据库初始化完成")
|
||||
|
||||
// 初始化 Elasticsearch
|
||||
//esConfig := config.AppConfig.ES
|
||||
//_ = es.Init([]string{esConfig.Host}, esConfig.Username, esConfig.Password)
|
||||
// 启动 OCR 服务
|
||||
//go func() {
|
||||
// ocr.StartService()
|
||||
//}()
|
||||
|
||||
// 设置路由并启动服务器
|
||||
log.Printf("[4/4] 启动服务器,端口: %s...", config.AppConfig.Server.Port)
|
||||
router.Run()
|
||||
}
|
||||
116
middleware/auth.go
Normal file
116
middleware/auth.go
Normal file
@ -0,0 +1,116 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/database"
|
||||
"psi/models"
|
||||
"psi/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// JWTAuth JWT认证中间件
|
||||
func JWTAuth() gin.HandlerFunc {
|
||||
// 处理JWT认证
|
||||
return func(c *gin.Context) {
|
||||
// 检查认证头
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
// 检查认证头是否为空
|
||||
if authHeader == "" {
|
||||
// 返回错误
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未提供认证令牌"})
|
||||
// 中断处理
|
||||
c.Abort()
|
||||
// 返回
|
||||
return
|
||||
}
|
||||
|
||||
// 提取令牌
|
||||
parts := strings.SplitN(authHeader, " ", 2)
|
||||
// 处理错误
|
||||
if !(len(parts) == 2 && parts[0] == "Bearer") {
|
||||
// 返回错误
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "认证格式错误"})
|
||||
// 中断处理
|
||||
c.Abort()
|
||||
// 返回
|
||||
return
|
||||
}
|
||||
// 解析JWT
|
||||
claims, err := utils.ParseJWT(parts[1])
|
||||
// 处理错误
|
||||
if err != nil {
|
||||
// 返回错误
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的认证令牌"})
|
||||
// 中断处理
|
||||
c.Abort()
|
||||
// 返回
|
||||
return
|
||||
}
|
||||
// 判断是员工还是用户
|
||||
if claims.ID > 0 {
|
||||
// 检查员工状态
|
||||
var employee models.Employee
|
||||
// 查询员工
|
||||
if err := database.DB.Where("id = ? AND status = ? AND deleted_at = ?", claims.ID, 1, 0).First(&employee).Error;
|
||||
// 处理错误
|
||||
err != nil {
|
||||
// 返回错误
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "账号不存在或已被禁用"})
|
||||
// 中断处理
|
||||
c.Abort()
|
||||
// 返回
|
||||
return
|
||||
}
|
||||
|
||||
// 将员工信息存入上下文
|
||||
c.Set("id", employee.ID) // 设置员工ID
|
||||
c.Set("role", employee.Role) // 设置角色
|
||||
c.Set("username", employee.Username) // 设置用户名
|
||||
c.Set("employee", employee) // 添加员工信息
|
||||
c.Set("about_id", claims.AboutID) // 租户ID
|
||||
c.Set("fid", employee.Fid) // 父级ID
|
||||
|
||||
// 如果有租户ID,获取租户数据库连接并存入上下文
|
||||
if claims.AboutID > 0 {
|
||||
tenantDB, err := database.GetTenantDB(claims.AboutID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "租户数据库连接失败"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Set("tenant_db", tenantDB)
|
||||
}
|
||||
} else {
|
||||
// 无效的身份
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的身份标识"})
|
||||
// 中断处理
|
||||
c.Abort()
|
||||
// 返回
|
||||
return
|
||||
}
|
||||
|
||||
// 继续处理
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// AdminRequired 管理员权限
|
||||
func AdminRequired() gin.HandlerFunc {
|
||||
// 处理管理员权限
|
||||
return func(c *gin.Context) {
|
||||
// 检查角色
|
||||
role, exists := c.Get("role")
|
||||
// 处理错误
|
||||
if !exists || role.(int64) != 255 {
|
||||
// 返回错误
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "需要管理员权限"})
|
||||
// 中断处理
|
||||
c.Abort()
|
||||
// 返回
|
||||
return
|
||||
}
|
||||
// 继续处理
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
24
middleware/cors.go
Normal file
24
middleware/cors.go
Normal file
@ -0,0 +1,24 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Cors 跨域中间件
|
||||
func Cors() gin.HandlerFunc {
|
||||
// 处理CORS请求
|
||||
return func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*") // 允许所有源
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") // 允许携带cookie
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") // 允许的请求头
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE, PATCH") // 允许的请求方法
|
||||
|
||||
// 处理OPTIONS请求
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204) // 返回204
|
||||
return
|
||||
}
|
||||
// 处理正常请求
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
260
middleware/sign.go
Normal file
260
middleware/sign.go
Normal file
@ -0,0 +1,260 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"psi/config"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// APISign API签名验证中间件
|
||||
func APISign() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 获取签名相关参数
|
||||
appKey := c.Query("app_key")
|
||||
clientId := c.Query("client_id")
|
||||
sign := c.Query("sign")
|
||||
signMethod := c.Query("sign_method")
|
||||
timestamp := c.Query("timestamp")
|
||||
if appKey == "" {
|
||||
appKey = c.PostForm("app_key")
|
||||
}
|
||||
if clientId == "" {
|
||||
clientId = c.PostForm("client_id")
|
||||
}
|
||||
if sign == "" {
|
||||
sign = c.PostForm("sign")
|
||||
}
|
||||
if signMethod == "" {
|
||||
signMethod = c.PostForm("sign_method")
|
||||
}
|
||||
if timestamp == "" {
|
||||
timestamp = c.PostForm("timestamp")
|
||||
}
|
||||
// 验证必填参数
|
||||
if appKey == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "缺少app_key参数",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if clientId == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "缺少client_id参数",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if sign == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "缺少sign参数",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if timestamp == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "缺少timestamp参数",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 验证app_key
|
||||
if appKey != config.AppConfig.APISign.AppKey {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"error": "无效的app_key",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 验证client_id
|
||||
if clientId != config.AppConfig.APISign.ClientId {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"error": "无效的client_id",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 验证时间戳(防止重放攻击)
|
||||
ts, err := strconv.ParseInt(timestamp, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "时间戳格式错误",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
currentTime := time.Now().Unix()
|
||||
timeDiff := currentTime - ts
|
||||
|
||||
// 检查时间戳是否在允许范围内(默认300秒)
|
||||
tolerance := int64(config.AppConfig.APISign.TimestampTolerance)
|
||||
if tolerance <= 0 {
|
||||
tolerance = 300 // 默认5分钟
|
||||
}
|
||||
|
||||
if timeDiff > tolerance || timeDiff < -tolerance {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"error": "请求已过期,请检查系统时间",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 验证签名方法
|
||||
if signMethod == "" {
|
||||
signMethod = config.AppConfig.APISign.SignMethod
|
||||
}
|
||||
if signMethod != "md5" && signMethod != "sha256" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "不支持的签名方法",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 计算签名
|
||||
calculatedSign, err := calculateSign(c, signMethod)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "签名计算失败",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
// 验证签名
|
||||
if calculatedSign != sign {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"error": "签名验证失败",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// calculateSign 计算签名
|
||||
func calculateSign(c *gin.Context, signMethod string) (string, error) {
|
||||
// 收集所有参数
|
||||
params := make(map[string]string)
|
||||
|
||||
// 获取URL查询参数
|
||||
for key, values := range c.Request.URL.Query() {
|
||||
// 跳过sign参数本身
|
||||
if key == "sign" {
|
||||
continue
|
||||
}
|
||||
if len(values) > 0 {
|
||||
params[key] = values[0]
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是POST、PUT请求,获取表单参数
|
||||
if c.Request.Method == "POST" || c.Request.Method == "PUT" || c.Request.Method == "DELETE" {
|
||||
c.Request.ParseForm()
|
||||
for key, values := range c.Request.PostForm {
|
||||
// 跳过sign参数本身
|
||||
if key == "sign" {
|
||||
continue
|
||||
}
|
||||
if len(values) > 0 {
|
||||
// 如果是数组参数,用逗号拼接所有值
|
||||
if len(values) > 1 {
|
||||
params[key] = strings.Join(values, ",")
|
||||
} else {
|
||||
params[key] = values[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 提取所有键并排序
|
||||
keys := make([]string, 0, len(params))
|
||||
for key := range params {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sortKeysWithIndex(keys)
|
||||
|
||||
// 构建签名字符串:key1=value1&key2=value2...
|
||||
var signStrings []string
|
||||
for _, key := range keys {
|
||||
signStrings = append(signStrings, fmt.Sprintf("%s=%s", key, params[key]))
|
||||
}
|
||||
signStr := strings.Join(signStrings, "&")
|
||||
//fmt.Println("signStr: %s", signStr)
|
||||
// 在字符串前后添加secret
|
||||
appSecret := config.AppConfig.APISign.AppSecret
|
||||
signStr = appSecret + signStr + appSecret
|
||||
// 根据签名方法计算哈希
|
||||
var hash []byte
|
||||
switch signMethod {
|
||||
case "md5":
|
||||
md5Hash := md5.Sum([]byte(signStr))
|
||||
hash = md5Hash[:]
|
||||
case "sha256":
|
||||
sha256Hash := sha256.Sum256([]byte(signStr))
|
||||
hash = sha256Hash[:]
|
||||
default:
|
||||
return "", fmt.Errorf("不支持的签名方法: %s", signMethod)
|
||||
}
|
||||
|
||||
// 转换为大写十六进制字符串
|
||||
return strings.ToUpper(hex.EncodeToString(hash)), nil
|
||||
}
|
||||
|
||||
// sortKeysWithIndex 对参数键进行智能排序,处理带索引的数组参数
|
||||
func sortKeysWithIndex(keys []string) {
|
||||
// 正则表达式匹配带索引的键,如 items[0][product_id]
|
||||
indexPattern := regexp.MustCompile(`^(.+)\[(\d+)\](.*)$`)
|
||||
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
keyI := keys[i]
|
||||
keyJ := keys[j]
|
||||
|
||||
matchI := indexPattern.FindStringSubmatch(keyI)
|
||||
matchJ := indexPattern.FindStringSubmatch(keyJ)
|
||||
|
||||
// 如果两个都是带索引的键
|
||||
if matchI != nil && matchJ != nil {
|
||||
prefixI, idxIStr, suffixI := matchI[1], matchI[2], matchI[3]
|
||||
prefixJ, idxJStr, suffixJ := matchJ[1], matchJ[2], matchJ[3]
|
||||
|
||||
// 先比较前缀(如 items)
|
||||
if prefixI != prefixJ {
|
||||
return prefixI < prefixJ
|
||||
}
|
||||
|
||||
// 前缀相同,比较索引(数值比较)
|
||||
idxI, errI := strconv.Atoi(idxIStr)
|
||||
idxJ, errJ := strconv.Atoi(idxJStr)
|
||||
|
||||
if errI == nil && errJ == nil {
|
||||
if idxI != idxJ {
|
||||
return idxI < idxJ
|
||||
}
|
||||
// 索引相同,比较后缀(如 [product_id])
|
||||
return suffixI < suffixJ
|
||||
}
|
||||
}
|
||||
|
||||
// 至少有一个不是带索引的键,使用默认字典序
|
||||
return keyI < keyJ
|
||||
})
|
||||
}
|
||||
32
models/book_info.go
Normal file
32
models/book_info.go
Normal file
@ -0,0 +1,32 @@
|
||||
package models
|
||||
|
||||
import "gorm.io/datatypes"
|
||||
|
||||
// BookInfo 书籍信息表
|
||||
type BookInfo struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:自增ID"`
|
||||
Fid int64 `json:"fid" gorm:"not null;default:0;comment:父级ID"`
|
||||
Type int8 `json:"type" gorm:"not null;comment:类型 1正常 2套装书 3 一号多书 4无书号"`
|
||||
ISBN string `json:"isbn" gorm:"size:20;not null;default:'';comment:ISBN;index:idx_isbn"`
|
||||
FISBN string `json:"f_isbn" gorm:"size:20;not null;default:'';comment:FISBN;"`
|
||||
BookName string `json:"book_name" gorm:"size:100;not null;default:'';comment:书名"`
|
||||
FBookName string `json:"f_book_name" gorm:"size:100;not null;default:'';comment:副书名"`
|
||||
Author string `json:"author" gorm:"size:100;not null;default:'';comment:作者"`
|
||||
Publishing string `json:"publishing" gorm:"size:50;not null;default:'';comment:出版社"`
|
||||
PublicationTime int64 `json:"publication_time" gorm:"type:bigint;not null;default:0;comment:出版日期时间戳"`
|
||||
Binding string `json:"binding" gorm:"size:10;not null;default:'';comment:装帧"`
|
||||
PagesCount int64 `json:"pages_count" gorm:"not null;default:0;comment:页数"`
|
||||
WordsCount int64 `json:"words_count" gorm:"not null;default:0;comment:字数"`
|
||||
Format int64 `json:"format" gorm:"not null;default:0;comment:开本"`
|
||||
Price int64 `json:"price" gorm:"not null;default:0;comment:价格"`
|
||||
CatID datatypes.JSON `json:"cat_id" gorm:"type:json;not null;comment:类目json"`
|
||||
LiveImage datatypes.JSON `json:"live_image" gorm:"column:live_image;type:json;not null;comment:实拍图json"`
|
||||
}
|
||||
|
||||
func (BookInfo) TableName() string {
|
||||
return "book_info"
|
||||
}
|
||||
|
||||
func (BookInfo) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='书籍信息表'"
|
||||
}
|
||||
24
models/car.go
Normal file
24
models/car.go
Normal file
@ -0,0 +1,24 @@
|
||||
package models
|
||||
|
||||
// Car 小车表
|
||||
type Car struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:小车ID"`
|
||||
WarehouseID int64 `json:"warehouse_id" gorm:"not null;default:0;index;comment:所属仓库ID"`
|
||||
PushType int8 `json:"push_type" gorm:"type:tinyint(1);not null;default:1;comment:推送类型(1:创建波次后随即上架,2:入库后在上架)"`
|
||||
ReleaseType int8 `json:"release_type" gorm:"type:tinyint(1);not null;default:1;comment:发布类型(1:独立库存,2:合并库存)"`
|
||||
Code int64 `json:"code" gorm:"not null;default:0;comment:小车编号"`
|
||||
Name string `json:"name" gorm:"size:50;not null;default:'';comment:小车名称"`
|
||||
Capacity int64 `json:"capacity" gorm:"not null;default:0;comment:容量"`
|
||||
Appearance int64 `json:"appearance" gorm:"type:bigint(20) unsigned;not null;default:0;comment:品相"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (Car) TableName() string {
|
||||
return "car"
|
||||
}
|
||||
|
||||
func (Car) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='小车表'"
|
||||
}
|
||||
24
models/car_shop.go
Normal file
24
models/car_shop.go
Normal file
@ -0,0 +1,24 @@
|
||||
package models
|
||||
|
||||
// CarShop 小车店铺关联表
|
||||
type CarShop struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:关联ID"`
|
||||
CarID int64 `json:"car_id" gorm:"not null;default:0;comment:小车id"`
|
||||
ShopID int64 `json:"shop_id" gorm:"not null;default:0;comment:店铺id"`
|
||||
ShopName string `json:"shop_name" gorm:"size:50;not null;default:'';comment:店铺名称"`
|
||||
ShopType int8 `json:"shop_type" gorm:"type:tinyint(1);not null;default:0;comment:店铺类型"`
|
||||
ClientID string `json:"client_id" gorm:"size:50;not null;comment:客户端id"`
|
||||
AppKey string `json:"app_key" gorm:"size:255;not null;default:'';comment:app_key"`
|
||||
AppSecret string `json:"app_secret" gorm:"size:255;not null;default:'';comment:app_secret"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (CarShop) TableName() string {
|
||||
return "car_shop"
|
||||
}
|
||||
|
||||
func (CarShop) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='小车店铺关联表'"
|
||||
}
|
||||
19
models/config.go
Normal file
19
models/config.go
Normal file
@ -0,0 +1,19 @@
|
||||
package models
|
||||
|
||||
// Config 配置表
|
||||
type Config struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:配置ID"`
|
||||
Key string `json:"key" gorm:"size:200;not null;default:'';uniqueIndex:uk_key;comment:配置键"`
|
||||
Value string `json:"value" gorm:"type:text;comment:配置值"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (Config) TableName() string {
|
||||
return "config"
|
||||
}
|
||||
|
||||
func (Config) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置表'"
|
||||
}
|
||||
23
models/customer.go
Normal file
23
models/customer.go
Normal file
@ -0,0 +1,23 @@
|
||||
package models
|
||||
|
||||
// Customer 客户表
|
||||
type Customer struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:客户ID"`
|
||||
Code string `json:"code" gorm:"size:50;not null;default:'';uniqueIndex:uk_code;comment:客户编码"`
|
||||
Name string `json:"name" gorm:"size:200;not null;default:'';comment:客户名称"`
|
||||
ContactPerson string `json:"contact_person" gorm:"size:50;default:'';comment:联系人"`
|
||||
ContactPhone string `json:"contact_phone" gorm:"size:20;default:'';comment:联系电话"`
|
||||
Address string `json:"address" gorm:"size:255;default:'';comment:地址"`
|
||||
Status int8 `json:"status" gorm:"type:tinyint(1);default:1;comment:状态(0:禁用,1:启用)"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (Customer) TableName() string {
|
||||
return "customer"
|
||||
}
|
||||
|
||||
func (Customer) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户表'"
|
||||
}
|
||||
33
models/employee.go
Normal file
33
models/employee.go
Normal file
@ -0,0 +1,33 @@
|
||||
package models
|
||||
|
||||
// Employee 员工
|
||||
type Employee struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:自增ID"`
|
||||
EmployeeIDStr string `json:"employee_id_str" gorm:"size:10;not null;uniqueIndex"` // 工号
|
||||
Fid int64 `json:"fid" gorm:"not null;default:0;comment:父级id"`
|
||||
AboutId int64 `json:"about_id" gorm:"not null;default:0;comment:关联id"`
|
||||
Code string `json:"code" gorm:"size:64;not null;default:'';comment:机械码"`
|
||||
Username string `json:"username" gorm:"size:50;not null;default:'';comment:登录账号 (init_工号)"`
|
||||
Password string `json:"password" gorm:"size:100;not null;default:'';comment:密码"`
|
||||
Name string `json:"name" gorm:"size:50;not null;default:'';comment:姓名"`
|
||||
Phone string `json:"phone" gorm:"size:11;not null;default:'';comment:手机号"`
|
||||
Role int64 `json:"role" gorm:"not null;default:0;comment:权限"`
|
||||
Status int8 `json:"status" gorm:"not null;default:1;comment:状态 1正常 0禁用"`
|
||||
Score int64 `json:"score" gorm:"not null;default:0;comment:积分"`
|
||||
From string `json:"from" gorm:"size:50;not null;default:'';comment:来源"`
|
||||
TypeId int8 `json:"type_id" gorm:"not null;default:0;comment:关联类型"`
|
||||
LastLoginAt int64 `json:"last_login_at" gorm:"not null;default:0;comment:最后登录时间"`
|
||||
LastLoginIP string `json:"last_login_ip" gorm:"size:20;not null;default:'';comment:最后登录ip"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"not null;default:0;comment:创建时间"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"not null;default:0;comment:更新时间"`
|
||||
DeletedAt int64 `json:"deleted_at" gorm:"not null;default:0;comment:删除时间"`
|
||||
ExpireTime int64 `json:"expire_time" gorm:"not null;default:0;comment:过期时间"`
|
||||
}
|
||||
|
||||
func (Employee) TableName() string {
|
||||
return "employees"
|
||||
}
|
||||
|
||||
func (Employee) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工表'"
|
||||
}
|
||||
22
models/employee_level.go
Normal file
22
models/employee_level.go
Normal file
@ -0,0 +1,22 @@
|
||||
package models
|
||||
|
||||
// EmployeeLevel 员工等级
|
||||
type EmployeeLevel struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:自增ID"`
|
||||
EmpId int64 `json:"emp_id" gorm:"not null;default:0;comment:员工id"`
|
||||
MaxNum int64 `json:"max_num" gorm:"not null;default:0;comment:最大数量"`
|
||||
Level int8 `json:"level" gorm:"not null;default:0;comment:等级"`
|
||||
PayTime int64 `json:"pay_time" gorm:"not null;default:0;comment:最新支付时间"`
|
||||
ExpireTime int64 `json:"expire_time" gorm:"not null;default:0;comment:到期时间"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"not null;default:0;comment:创建时间"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"not null;default:0;comment:更新时间"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (EmployeeLevel) TableName() string {
|
||||
return "employees_level"
|
||||
}
|
||||
|
||||
func (EmployeeLevel) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工等级表'"
|
||||
}
|
||||
29
models/employee_level_log.go
Normal file
29
models/employee_level_log.go
Normal file
@ -0,0 +1,29 @@
|
||||
package models
|
||||
|
||||
// EmployeeLevelLog 员工等级变更日志表
|
||||
type EmployeeLevelLog struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:日志ID"`
|
||||
EmpId int64 `json:"emp_id" gorm:"not null;default:0;index;comment:员工ID"`
|
||||
OperationType int8 `json:"operation_type" gorm:"not null;default:1;comment:操作类型(1:开通,2:升级,3:续费)"`
|
||||
OldLevel int8 `json:"old_level" gorm:"not null;default:0;comment:原等级"`
|
||||
NewLevel int8 `json:"new_level" gorm:"not null;default:0;comment:新等级"`
|
||||
OldMaxNum int64 `json:"old_max_num" gorm:"not null;default:0;comment:原子账号数量上限"`
|
||||
NewMaxNum int64 `json:"new_max_num" gorm:"not null;default:0;comment:新子账号数量上限"`
|
||||
OldExpireTime int64 `json:"old_expire_time" gorm:"not null;default:0;comment:原到期时间"`
|
||||
NewExpireTime int64 `json:"new_expire_time" gorm:"not null;default:0;comment:新到期时间"`
|
||||
Price int64 `json:"price" gorm:"not null;default:0;comment:价格(单位:分)"`
|
||||
PayTime int64 `json:"pay_time" gorm:"not null;default:0;comment:支付时间"`
|
||||
OperatorID int64 `json:"operator_id" gorm:"not null;default:0;comment:操作人ID"`
|
||||
OperatorName string `json:"operator_name" gorm:"size:50;not null;default:'';comment:操作人姓名"`
|
||||
Remark string `json:"remark" gorm:"size:255;not null;default:'';comment:备注"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"not null;default:0;index;comment:创建时间戳"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (EmployeeLevelLog) TableName() string {
|
||||
return "employees_level_log"
|
||||
}
|
||||
|
||||
func (EmployeeLevelLog) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工等级变更日志表'"
|
||||
}
|
||||
87
models/employee_settings.go
Normal file
87
models/employee_settings.go
Normal file
@ -0,0 +1,87 @@
|
||||
package models
|
||||
|
||||
// EmployeeSettings 员工设置表
|
||||
type EmployeeSettings struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:自增ID"`
|
||||
EmpID int64 `json:"emp_id" gorm:"not null;default:0;uniqueIndex;comment:员工ID(关联employees.id)"`
|
||||
Settings string `json:"settings" gorm:"type:text;not null;comment:员工配置(JSON格式)"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"not null;default:0;comment:创建时间"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"not null;default:0;comment:更新时间"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (EmployeeSettings) TableName() string {
|
||||
return "employees_settings"
|
||||
}
|
||||
|
||||
func (EmployeeSettings) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工设置表'"
|
||||
}
|
||||
|
||||
// EmployeeSettingsConfig 员工配置结构体(JSON格式)
|
||||
type EmployeeSettingsConfig struct {
|
||||
// 筛选配置
|
||||
ShowPrice int8 `form:"show_price" json:"show_price"` // 比价结果开关
|
||||
ShowCategory int8 `form:"show_category" json:"show_category"` // 市场竞争开关
|
||||
ShowCache int8 `form:"show_cache" json:"show_cache"` // 缓存开启
|
||||
CompareCount int8 `form:"compare_count" json:"compare_count"` // 比较条数
|
||||
CompareCountEditable int8 `form:"compare_count_editable" json:"compare_count_editable"` // 比较条数是否可编辑
|
||||
PriceCompare string `form:"price_compare" json:"price_compare"` // 价格比较符号
|
||||
PriceValue float64 `form:"price_value" json:"price_value"` // 价格数值
|
||||
PriceEditable int8 `form:"price_editable" json:"price_editable"` // 比较条数是否可编辑
|
||||
SellCountCompare string `form:"sell_count_compare" json:"sell_count_compare"` // 在售数量比较符号
|
||||
SellCountValue float64 `form:"sell_count_value" json:"sell_count_value"` // 在售数量数值
|
||||
SellCountEditable int8 `form:"sell_count_editable" json:"sell_count_editable"` // 在售数量是否可编辑
|
||||
BuyCountCompare string `form:"buy_count_compare" json:"buy_count_compare"` // 已售数量比较符号
|
||||
BuyCountValue float64 `form:"buy_count_value" json:"buy_count_value"` // 已售数量数值
|
||||
BuyCountEditable int8 `form:"buy_count_editable" json:"buy_count_editable"` // 已售数量是否可编辑
|
||||
Condition string `form:"condition" json:"condition"` // 品相
|
||||
ConditionEditable int8 `form:"condition_editable" json:"condition_editable"` // 品相是否可编辑
|
||||
|
||||
// 精品书配置
|
||||
JingpinEnabled int8 `form:"jingpin_enabled" json:"jingpin_enabled"` // 开启精品书开关
|
||||
JingpinPriceCompare string `form:"jingpin_price_compare" json:"jingpin_price_compare"` // 精品书价格比较符号
|
||||
JingpinPriceValue float64 `form:"jingpin_price_value" json:"jingpin_price_value"` // 精品书价格数值
|
||||
JingpinPriceEditable int8 `form:"jingpin_price_editable" json:"jingpin_price_editable"` // 精品书价格是否可编辑
|
||||
JingpinSellCountCompare string `form:"jingpin_sell_count_compare" json:"jingpin_sell_count_compare"` // 精品书在售数量比较符号
|
||||
JingpinSellCountValue float64 `form:"jingpin_sell_count_value" json:"jingpin_sell_count_value"` // 精品书在售数量数值
|
||||
JingpinSellCountEditable int8 `form:"jingpin_sell_count_editable" json:"jingpin_sell_count_editable"` // 精品书在售数量是否可编辑
|
||||
JingpinBuyCountCompare string `form:"jingpin_buy_count_compare" json:"jingpin_buy_count_compare"` // 精品书已售数量比较符号
|
||||
JingpinBuyCountValue float64 `form:"jingpin_buy_count_value" json:"jingpin_buy_count_value"` // 精品书已售数量数值
|
||||
JingpinBuyCountEditable int8 `form:"jingpin_buy_count_editable" json:"jingpin_buy_count_editable"` // 精品书已售数量是否可编辑
|
||||
}
|
||||
|
||||
// GetDefaultEmployeeSettingsConfig 获取默认用户配置
|
||||
func GetDefaultEmployeeSettingsConfig() EmployeeSettingsConfig {
|
||||
return EmployeeSettingsConfig{
|
||||
// 筛选配置
|
||||
ShowPrice: 0, // 比价结果开启
|
||||
ShowCategory: 0, // 市场竞争开启
|
||||
ShowCache: 1, // 缓存开启
|
||||
CompareCount: 1, // 比较条数
|
||||
CompareCountEditable: 0, // 比较条数是否可编辑
|
||||
PriceCompare: ">=", // 价格比较符
|
||||
PriceValue: 4, // 价格阈值
|
||||
PriceEditable: 0, // 价格是否可编辑
|
||||
SellCountCompare: ">=", // 在售数量比较符
|
||||
SellCountValue: 10, // 在售数量阈值
|
||||
SellCountEditable: 0, // 在售数量是否可编辑
|
||||
BuyCountCompare: ">=", // 已售数量比较符
|
||||
BuyCountValue: 10, // 已售数量阈值
|
||||
BuyCountEditable: 0, // 已售数量是否可编辑
|
||||
Condition: "85~", // 品相等级
|
||||
ConditionEditable: 0,
|
||||
|
||||
// 精品书配置
|
||||
JingpinEnabled: 0, // 开启精品书
|
||||
JingpinPriceCompare: ">=", // 精品书价格比较符
|
||||
JingpinPriceValue: 10, // 精品书价格阈值
|
||||
JingpinPriceEditable: 0, // 精品书价格是否可编辑
|
||||
JingpinSellCountCompare: ">=", // 精品书在售数量比较符
|
||||
JingpinSellCountValue: 10, // 精品书在售数量阈值
|
||||
JingpinSellCountEditable: 0, // 精品书在售数量是否可编辑
|
||||
JingpinBuyCountCompare: ">=", // 精品书已售数量比较符
|
||||
JingpinBuyCountValue: 10, // 精品书已售数量阈值
|
||||
JingpinBuyCountEditable: 0, // 精品书已售数量是否可编辑
|
||||
}
|
||||
}
|
||||
25
models/inventory.go
Normal file
25
models/inventory.go
Normal file
@ -0,0 +1,25 @@
|
||||
package models
|
||||
|
||||
// Inventory 库存汇总表
|
||||
type Inventory struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:库存ID"`
|
||||
WarehouseID int64 `json:"warehouse_id" gorm:"not null;default:0;uniqueIndex:uk_warehouse_product_batch;index;comment:仓库ID"`
|
||||
ProductID int64 `json:"product_id" gorm:"not null;default:0;uniqueIndex:uk_warehouse_product_batch;index;comment:商品ID"`
|
||||
BatchNo string `json:"batch_no" gorm:"size:100;not null;default:'';uniqueIndex:uk_warehouse_product_batch;comment:批次号(无批次管理则为空字符串)"`
|
||||
ProductionDate int64 `json:"production_date" gorm:"not null;default:0;comment:生产日期时间戳(秒)"`
|
||||
ExpiryDate int64 `json:"expiry_date" gorm:"not null;default:0;comment:失效日期时间戳(秒)"`
|
||||
Quantity int64 `json:"quantity" gorm:"not null;default:0;comment:库存数量(最小单位,如个)"`
|
||||
LockedQuantity int64 `json:"locked_quantity" gorm:"not null;default:0;comment:锁定数量(已分配但未出库)"`
|
||||
AvailableQuantity int64 `json:"available_quantity" gorm:"->;comment:可用数量(计算字段)"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (Inventory) TableName() string {
|
||||
return "inventory"
|
||||
}
|
||||
|
||||
func (Inventory) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存汇总表'"
|
||||
}
|
||||
26
models/inventory_detail.go
Normal file
26
models/inventory_detail.go
Normal file
@ -0,0 +1,26 @@
|
||||
package models
|
||||
|
||||
// InventoryDetail 库存明细表(库位级)
|
||||
type InventoryDetail struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:明细ID"`
|
||||
WarehouseID int64 `json:"warehouse_id" gorm:"not null;default:0;index;comment:仓库ID"`
|
||||
LocationID int64 `json:"location_id" gorm:"not null;default:0;uniqueIndex:uk_location_product_batch;index;comment:库位ID"`
|
||||
ProductID int64 `json:"product_id" gorm:"not null;default:0;uniqueIndex:uk_location_product_batch;index;comment:商品ID"`
|
||||
BatchNo string `json:"batch_no" gorm:"size:100;not null;default:'';uniqueIndex:uk_location_product_batch;index;comment:批次号"`
|
||||
ProductionDate int64 `json:"production_date" gorm:"not null;default:0;comment:生产日期时间戳(秒)"`
|
||||
ExpiryDate int64 `json:"expiry_date" gorm:"not null;default:0;index;comment:失效日期时间戳(秒)"`
|
||||
Quantity int64 `json:"quantity" gorm:"not null;default:0;comment:当前数量(最小单位)"`
|
||||
LockedQuantity int64 `json:"locked_quantity" gorm:"not null;default:0;comment:锁定数量"`
|
||||
AvailableQuantity int64 `json:"available_quantity" gorm:"->;comment:可用数量"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (InventoryDetail) TableName() string {
|
||||
return "inventory_detail"
|
||||
}
|
||||
|
||||
func (InventoryDetail) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存明细表(库位级)'"
|
||||
}
|
||||
29
models/inventory_log.go
Normal file
29
models/inventory_log.go
Normal file
@ -0,0 +1,29 @@
|
||||
package models
|
||||
|
||||
// InventoryLog 库存流水表
|
||||
type InventoryLog struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:流水ID"`
|
||||
WarehouseID int64 `json:"warehouse_id" gorm:"not null;default:0;index;comment:仓库ID"`
|
||||
LocationID int64 `json:"location_id" gorm:"not null;default:0;comment:库位ID(可为空)"`
|
||||
ProductID int64 `json:"product_id" gorm:"not null;default:0;index;comment:商品ID"`
|
||||
BatchNo string `json:"batch_no" gorm:"size:100;not null;default:'';comment:批次号"`
|
||||
ChangeType int8 `json:"change_type" gorm:"not null;default:0;comment:变动类型(1:入库,2:出库,3:移库,4:盘点调整,5:锁定库存,6:解锁库存)"`
|
||||
ChangeQuantity int64 `json:"change_quantity" gorm:"not null;default:0;comment:变动数量(正增负减,最小单位)"`
|
||||
BeforeQuantity int64 `json:"before_quantity" gorm:"not null;default:0;comment:变动前数量"`
|
||||
AfterQuantity int64 `json:"after_quantity" gorm:"not null;default:0;comment:变动后数量"`
|
||||
RelatedOrderType string `json:"related_order_type" gorm:"size:50;not null;default:'';comment:关联单据类型(如:采购单/purchase, 销售单/sales, 波次单/wave)"`
|
||||
RelatedOrderNo string `json:"related_order_no" gorm:"size:100;not null;index;comment:关联单据号"`
|
||||
Operator string `json:"operator" gorm:"size:100;not null;comment:操作人"`
|
||||
OperatorID int64 `json:"operator_id" gorm:"not null;default:0;comment:操作人ID"`
|
||||
Remark string `json:"remark" gorm:"size:255;not null;comment:备注"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;index;comment:操作时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (InventoryLog) TableName() string {
|
||||
return "inventory_log"
|
||||
}
|
||||
|
||||
func (InventoryLog) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存流水表'"
|
||||
}
|
||||
23
models/location.go
Normal file
23
models/location.go
Normal file
@ -0,0 +1,23 @@
|
||||
package models
|
||||
|
||||
// Location 库位表
|
||||
type Location struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:库位ID"`
|
||||
WarehouseID int64 `json:"warehouse_id" gorm:"not null;default:0;index;uniqueIndex:uk_warehouse_code,priority:1;comment:所属仓库ID"`
|
||||
Code string `json:"code" gorm:"size:50;not null;default:'';uniqueIndex:uk_warehouse_code,priority:2;comment:库位编码(如 A-01-02)"`
|
||||
Type int8 `json:"type" gorm:"not null;default:1;comment:库位类型(1:存储库位,2:拣货库位,3:收货库位,4:发货库位,5:退货库位)"`
|
||||
Capacity int64 `json:"capacity" gorm:"not null;default:0;comment:容量(按体积立方厘米或重量克)"`
|
||||
Sort int `json:"sort" gorm:"not null;default:0;comment:排序值(值越小越靠前)"`
|
||||
Status int8 `json:"status" gorm:"type:tinyint(1);not null;default:1;comment:状态(0:禁用,1:启用)"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;index:idx_is_del;priority:3;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (Location) TableName() string {
|
||||
return "location"
|
||||
}
|
||||
|
||||
func (Location) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库位表'"
|
||||
}
|
||||
40
models/logistics.go
Normal file
40
models/logistics.go
Normal file
@ -0,0 +1,40 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
type Logistics struct {
|
||||
Id uint64 `gorm:"primaryKey;column:id" json:"id"`
|
||||
TemplateName string `gorm:"column:template_name;size:255" json:"templateName"`
|
||||
DeliveryProvince string `gorm:"column:delivery_province;size:255" json:"deliveryProvince"`
|
||||
DeliveryCity string `gorm:"column:delivery_city;size:255" json:"deliveryCity"`
|
||||
DeliveryArea string `gorm:"column:delivery_area;size:255" json:"deliveryArea"`
|
||||
DeliveryAddress string `gorm:"column:delivery_address;size:255" json:"deliveryAddress"`
|
||||
PricingMethod string `gorm:"column:pricing_method;size:1;default:0" json:"pricingMethod"`
|
||||
Shipping string `gorm:"column:shipping;size:1;default:0" json:"shipping"`
|
||||
FirWbv float64 `gorm:"column:fir_wbv;type:double(20,2)" json:"firWbv"`
|
||||
FirPrice float64 `gorm:"column:fir_price;type:decimal(10,2)" json:"firPrice"`
|
||||
ContinueWbv float64 `gorm:"column:continue_wbv;type:double(20,2)" json:"continueWbv"`
|
||||
ContinuePrice float64 `gorm:"column:continue_price;type:decimal(10,2)" json:"continuePrice"`
|
||||
CreateBy uint64 `gorm:"column:create_by" json:"createBy"`
|
||||
CreateTime *time.Time `gorm:"column:create_time" json:"createTime"`
|
||||
UpdateBy uint64 `gorm:"column:update_by" json:"updateBy"`
|
||||
UpdateTime *time.Time `gorm:"column:update_time" json:"updateTime"`
|
||||
Status string `gorm:"column:status;size:1;default:0" json:"status"`
|
||||
DelFlag string `gorm:"column:del_flag;size:1;default:0" json:"delFlag"`
|
||||
TenantId string `gorm:"column:tenant_id;size:20" json:"tenantId"`
|
||||
CreateDept uint64 `gorm:"column:create_dept" json:"createDept"`
|
||||
ShippingRange string `gorm:"column:shipping_range;type:text" json:"shippingRange"`
|
||||
WarehouseId uint64 `gorm:"column:warehouse_id" json:"warehouseId"`
|
||||
Remark string `gorm:"column:remark;size:255" json:"remark"`
|
||||
PhoneNumber uint64 `gorm:"column:phone_number" json:"phoneNumber"`
|
||||
Contact string `gorm:"column:contact;size:14" json:"contact"`
|
||||
FullAddress string `gorm:"column:full_address;size:255" json:"fullAddress"`
|
||||
}
|
||||
|
||||
func (Logistics) TableName() string {
|
||||
return "logistics"
|
||||
}
|
||||
|
||||
func (Logistics) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物流模板表'"
|
||||
}
|
||||
24
models/out_task.go
Normal file
24
models/out_task.go
Normal file
@ -0,0 +1,24 @@
|
||||
package models
|
||||
|
||||
// OutTask 外部任务表
|
||||
type OutTask struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:主键ID"`
|
||||
ShopID int64 `json:"shop_id" gorm:"not null;default:0;index;comment:店铺ID"`
|
||||
WaveTaskID int64 `json:"wave_task_id" gorm:"not null;default:0;index;comment:波次任务ID"`
|
||||
OutTaskID int64 `json:"out_task_id" gorm:"not null;default:0;index;comment:外部任务ID"`
|
||||
ShopType int8 `json:"shop_type" gorm:"not null;default:0;comment:店铺类型 1 拼多多 2 孔夫子 5 闲鱼"`
|
||||
TaskType int8 `json:"task_type" gorm:"not null;default:0;comment:任务类型 1 核价发布 2 表格发布 3 商品拉取 4 商品详情拉取 5 操作商品 6 核价表格发布 10、按照数量删除任务 11、按照时间删除任务"`
|
||||
ImgType int8 `json:"img_type" gorm:"not null;default:0;comment:图片类型 1仅官图 2 仅实拍图 3 优先官图 4 优先实拍图"`
|
||||
TaskCount int64 `json:"task_count" gorm:"not null;default:0;comment:任务数(小车容量)"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (OutTask) TableName() string {
|
||||
return "out_task"
|
||||
}
|
||||
|
||||
func (OutTask) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部任务表'"
|
||||
}
|
||||
31
models/out_task_log.go
Normal file
31
models/out_task_log.go
Normal file
@ -0,0 +1,31 @@
|
||||
package models
|
||||
|
||||
import "gorm.io/datatypes"
|
||||
|
||||
// OutTaskLog 外部任务日志表
|
||||
type OutTaskLog struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:主键ID"`
|
||||
ShopID int64 `json:"shop_id" gorm:"not null;default:0;index;comment:店铺ID"`
|
||||
WaveTaskID int64 `json:"wave_task_id" gorm:"not null;default:0;index;comment:波次任务ID"`
|
||||
OutTaskID int64 `json:"out_task_id" gorm:"not null;default:0;index;comment:外部任务ID"`
|
||||
ProductID int64 `json:"product_id" gorm:"not null;default:0;index;comment:产品ID"`
|
||||
ISBN string `json:"isbn" gorm:"type:varchar(100);not null;default:'';comment:isbn"`
|
||||
LiveImage datatypes.JSON `json:"live_image" gorm:"type:json;not null;comment:实拍图json"`
|
||||
Stock int64 `json:"stock" gorm:"not null;default:0;comment:库存"`
|
||||
SalePrice int64 `json:"sale_price" gorm:"not null;default:0;comment:售价"`
|
||||
Cost int64 `json:"cost" gorm:"not null;default:0;comment:运费"`
|
||||
SkuCode string `json:"sku_code" gorm:"type:varchar(100);not null;default:'';comment:货号"`
|
||||
Status int8 `json:"status" gorm:"type:tinyint(1);not null;comment:状态 0异常 1推送成功 2发布成功"`
|
||||
Msg string `json:"msg" gorm:"type:varchar(255);not null;default:'';comment:错误信息"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (OutTaskLog) TableName() string {
|
||||
return "out_task_log"
|
||||
}
|
||||
|
||||
func (OutTaskLog) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部任务日志表'"
|
||||
}
|
||||
27
models/outbound_order.go
Normal file
27
models/outbound_order.go
Normal file
@ -0,0 +1,27 @@
|
||||
package models
|
||||
|
||||
// OutboundOrder 出库单主表
|
||||
type OutboundOrder struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:出库单ID"`
|
||||
OutNo string `json:"out_no" gorm:"size:100;not null;default:'';uniqueIndex;comment:出库单号"`
|
||||
WaveTaskID int64 `json:"wave_task_id" gorm:"not null;default:0;index;comment:波次任务ID"`
|
||||
WarehouseID int64 `json:"warehouse_id" gorm:"not null;default:0;comment:仓库ID"`
|
||||
CustomerID int64 `json:"customer_id" gorm:"not null;default:0;comment:客户ID"`
|
||||
TotalQuantity int64 `json:"total_quantity" gorm:"not null;default:0;comment:出库总数量"`
|
||||
TotalAmount int64 `json:"total_amount" gorm:"not null;default:0;comment:出库总金额(分)"`
|
||||
Status int8 `json:"status" gorm:"not null;default:1;index;comment:状态(1:已创建,2:拣货中,3:已完成,4:已取消,5:发货中,6:已发货)"`
|
||||
Operator string `json:"operator" gorm:"size:100;not null;default:'';comment:操作员"`
|
||||
OperatorID int64 `json:"operator_id" gorm:"not null;default:0;comment:操作员ID"`
|
||||
Remark string `json:"remark" gorm:"size:255;not null;default:'';comment:备注"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (OutboundOrder) TableName() string {
|
||||
return "outbound_order"
|
||||
}
|
||||
|
||||
func (OutboundOrder) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库单主表'"
|
||||
}
|
||||
26
models/outbound_order_item.go
Normal file
26
models/outbound_order_item.go
Normal file
@ -0,0 +1,26 @@
|
||||
package models
|
||||
|
||||
// OutboundOrderItem 出库单明细表
|
||||
type OutboundOrderItem struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:明细ID"`
|
||||
OutOrderID int64 `json:"out_order_id" gorm:"not null;default:0;index;comment:出库单ID"`
|
||||
SalesOrderID int64 `json:"sales_order_id" gorm:"not null;default:0;index;comment:销售单ID"`
|
||||
ProductID int64 `json:"product_id" gorm:"not null;default:0;index;comment:商品ID"`
|
||||
LocationID int64 `json:"location_id" gorm:"not null;default:0;comment:库位ID"`
|
||||
BatchNo string `json:"batch_no" gorm:"size:100;not null;default:'';comment:批次号"`
|
||||
ProductionDate int64 `json:"production_date" gorm:"not null;default:0;comment:生产日期时间戳(秒)"`
|
||||
ExpiryDate int64 `json:"expiry_date" gorm:"not null;default:0;comment:失效日期时间戳(秒)"`
|
||||
Quantity int64 `json:"quantity" gorm:"not null;default:0;comment:出库数量"`
|
||||
UnitPrice int64 `json:"unit_price" gorm:"not null;default:0;comment:单价(分/基本单位)"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (OutboundOrderItem) TableName() string {
|
||||
return "outbound_order_item"
|
||||
}
|
||||
|
||||
func (OutboundOrderItem) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库单明细表'"
|
||||
}
|
||||
24
models/outbound_order_location_log.go
Normal file
24
models/outbound_order_location_log.go
Normal file
@ -0,0 +1,24 @@
|
||||
package models
|
||||
|
||||
// OutboundOrderLocationLog 出库单库位变更记录表
|
||||
type OutboundOrderLocationLog struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:记录ID"`
|
||||
OutOrderID int64 `json:"out_order_id" gorm:"not null;default:0;index;comment:出库单ID"`
|
||||
OutOrderItemID int64 `json:"out_order_item_id" gorm:"not null;default:0;index;comment:出库单明细ID"`
|
||||
ProductID int64 `json:"product_id" gorm:"not null;default:0;comment:商品ID"`
|
||||
OldLocationID int64 `json:"old_location_id" gorm:"not null;default:0;comment:原库位ID"`
|
||||
NewLocationID int64 `json:"new_location_id" gorm:"not null;default:0;comment:新库位ID"`
|
||||
BatchNo string `json:"batch_no" gorm:"size:100;not null;default:'';comment:批次号"`
|
||||
Operator string `json:"operator" gorm:"size:100;not null;default:'';comment:操作员"`
|
||||
OperatorID int64 `json:"operator_id" gorm:"not null;default:0;comment:操作员ID"`
|
||||
Remark string `json:"remark" gorm:"size:255;not null;default:'';comment:备注"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
}
|
||||
|
||||
func (OutboundOrderLocationLog) TableName() string {
|
||||
return "outbound_order_location_log"
|
||||
}
|
||||
|
||||
func (OutboundOrderLocationLog) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库单库位变更记录表'"
|
||||
}
|
||||
19
models/print_log.go
Normal file
19
models/print_log.go
Normal file
@ -0,0 +1,19 @@
|
||||
package models
|
||||
|
||||
// PrintLog 打印日志表
|
||||
type PrintLog struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:日志ID"`
|
||||
PrintTaskID int64 `json:"print_task_id" gorm:"not null;default:0;index;comment:打印任务ID"`
|
||||
PrintedAt int64 `json:"printed_at" gorm:"default:0;comment:打印时间戳(秒)"`
|
||||
Result int8 `json:"result" gorm:"type:tinyint(1);default:1;comment:打印结果(1:成功,0:失败)"`
|
||||
ErrorInfo string `json:"error_info" gorm:"size:255;default:'';comment:错误信息"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (PrintLog) TableName() string {
|
||||
return "print_log"
|
||||
}
|
||||
|
||||
func (PrintLog) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='打印日志表'"
|
||||
}
|
||||
31
models/print_task.go
Normal file
31
models/print_task.go
Normal file
@ -0,0 +1,31 @@
|
||||
package models
|
||||
|
||||
import "gorm.io/datatypes"
|
||||
|
||||
// PrintTask 打印任务表
|
||||
type PrintTask struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:打印任务ID"`
|
||||
TaskNo string `json:"task_no" gorm:"size:100;not null;default:'';uniqueIndex;comment:打印任务编号"`
|
||||
BusinessType int8 `json:"business_type" gorm:"not null;default:1;comment:业务类型(1:物流面单/shipment_label,2:拣货单/pick_list,3:装箱单/packing_list,4:发票/invoice)"`
|
||||
RelatedID int64 `json:"related_id" gorm:"not null;default:0;index;comment:关联业务ID(如出库单ID)"`
|
||||
RelatedNo string `json:"related_no" gorm:"size:100;not null;default:'';comment:关联业务单号"`
|
||||
PrinterName string `json:"printer_name" gorm:"size:100;not null;default:'';comment:指定打印机"`
|
||||
TemplateCode string `json:"template_code" gorm:"size:50;not null;default:'';comment:打印模板编码"`
|
||||
PrintData datatypes.JSON `json:"print_data" gorm:"type:json;comment:打印数据快照(JSON格式)"`
|
||||
Status int8 `json:"status" gorm:"not null;default:1;index;comment:状态(1:待打印/pending,2:打印中/printing,3:成功/success,4:失败/failed)"`
|
||||
RetryCount int `json:"retry_count" gorm:"not null;default:0;comment:重试次数"`
|
||||
ErrorMessage string `json:"error_message" gorm:"size:255;not null;default:'';comment:失败原因"`
|
||||
Operator string `json:"operator" gorm:"size:100;not null;default:'';comment:操作人"`
|
||||
OperatorID int64 `json:"operator_id" gorm:"not null;default:0;comment:操作人ID"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;index;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (PrintTask) TableName() string {
|
||||
return "print_task"
|
||||
}
|
||||
|
||||
func (PrintTask) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='打印任务表'"
|
||||
}
|
||||
31
models/product.go
Normal file
31
models/product.go
Normal file
@ -0,0 +1,31 @@
|
||||
package models
|
||||
|
||||
import "gorm.io/datatypes"
|
||||
|
||||
// Product 商品表
|
||||
type Product struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:商品ID"`
|
||||
CategoryID int64 `json:"category_id" gorm:"not null;default:0;comment:分类ID"`
|
||||
StandardProductID int64 `json:"standard_product_id" gorm:"not null;default:0;index;comment:关联标品ID"`
|
||||
Name string `json:"name" gorm:"size:255;not null;default:'';comment:商品名称"`
|
||||
Appearance int64 `json:"appearance" gorm:"not null;default:0;comment:品相"`
|
||||
Barcode string `json:"barcode" gorm:"size:100;not null;default:'';index;comment:条码(可唯一,视业务而定)"`
|
||||
Price int64 `json:"price" gorm:"not null;default:0;comment:价格"`
|
||||
SalePrice int64 `json:"sale_price" gorm:"not null;default:0;comment:书价"`
|
||||
Cost int64 `json:"cost" gorm:"not null;default:0;comment:最低运费"`
|
||||
LiveImage datatypes.JSON `json:"live_image" gorm:"type:json;not null;comment:实拍图json"`
|
||||
IsBatchManaged int8 `json:"is_batch_managed" gorm:"type:tinyint(1);not null;default:0;comment:是否批次管理(0:否,1:是)"`
|
||||
IsShelfLifeManaged int8 `json:"is_shelf_life_managed" gorm:"type:tinyint(1);not null;default:0;comment:是否效期管理(0:否,1:是)"`
|
||||
Status int8 `json:"status" gorm:"type:tinyint(1);not null;default:1;comment:状态(0:禁用,1:启用)"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳"`
|
||||
IsDel int8 `json:"is_del" gorm:"not null;default:0;comment:逻辑删除"`
|
||||
}
|
||||
|
||||
func (Product) TableName() string {
|
||||
return "product"
|
||||
}
|
||||
|
||||
func (Product) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表'"
|
||||
}
|
||||
20
models/product_category.go
Normal file
20
models/product_category.go
Normal file
@ -0,0 +1,20 @@
|
||||
package models
|
||||
|
||||
// ProductCategory 商品分类表
|
||||
type ProductCategory struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:分类ID"`
|
||||
ParentID int64 `json:"parent_id" gorm:"not null;default:0;index;comment:上级分类ID,0表示顶级"`
|
||||
Name string `json:"name" gorm:"size:100;not null;default:'';comment:分类名称"`
|
||||
SortOrder int `json:"sort_order" gorm:"not null;default:0;comment:排序"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (ProductCategory) TableName() string {
|
||||
return "product_category"
|
||||
}
|
||||
|
||||
func (ProductCategory) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品分类表'"
|
||||
}
|
||||
44
models/product_log.go
Normal file
44
models/product_log.go
Normal file
@ -0,0 +1,44 @@
|
||||
package models
|
||||
|
||||
import "gorm.io/datatypes"
|
||||
|
||||
// ProductLog 商品表
|
||||
type ProductLog struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:商品ID"`
|
||||
Barcode string `json:"barcode" gorm:"size:100;not null;default:'';index;comment:条码(可唯一,视业务而定)"`
|
||||
Md5Data string `json:"md5_data" gorm:"size:100;not null;default:'';comment:md5"`
|
||||
OldName string `json:"old_name" gorm:"size:100;not null;default:'';comment:老书名"`
|
||||
NewName string `json:"new_name" gorm:"size:100;not null;default:'';comment:新书名"`
|
||||
OldPublisher string `json:"old_publisher" gorm:"size:100;not null;default:'';comment:老出版社"`
|
||||
NewPublisher string `json:"new_publisher" gorm:"size:100;not null;default:'';comment:新出版社"`
|
||||
OldAuthor string `json:"old_author" gorm:"size:100;not null;default:'';comment:老作者"`
|
||||
NewAuthor string `json:"new_author" gorm:"size:100;not null;default:'';comment:新作者"`
|
||||
OldPublicationTime int64 `json:"old_publication_time" gorm:"not null;default:0;comment:老出版时间"`
|
||||
NewPublicationTime int64 `json:"new_publication_time" gorm:"not null;default:0;comment:新出版时间"`
|
||||
OldPrice int64 `json:"old_price" gorm:"not null;default:0;comment:老价格"`
|
||||
NewPrice int64 `json:"new_price" gorm:"not null;default:0;comment:新价格"`
|
||||
OldBindingLayout string `json:"old_binding_layout" gorm:"size:20;not null;default:'';comment:老装帧"`
|
||||
NewBindingLayout string `json:"new_binding_layout" gorm:"size:20;not null;default:'';comment:新装帧"`
|
||||
OldPageCount int64 `json:"old_page_count" gorm:"not null;default:0;comment:老页数"`
|
||||
NewPageCount int64 `json:"new_page_count" gorm:"not null;default:0;comment:新页数"`
|
||||
OldWordCount int64 `json:"old_word_count" gorm:"not null;default:0;comment:老字数"`
|
||||
NewWordCount int64 `json:"new_word_count" gorm:"not null;default:0;comment:新字数"`
|
||||
OldLiveImage datatypes.JSON `json:"old_live_image" gorm:"type:json;not null;comment:老实拍图json"`
|
||||
NewLiveImage datatypes.JSON `json:"new_live_image" gorm:"type:json;not null;comment:新实拍图json"`
|
||||
OldIsSuit int8 `json:"old_is_suit" gorm:"type:tinyint(1);not null;default:0;comment:老是否是套装书(0:否,1:是)"`
|
||||
NewIsSuit int8 `json:"new_is_suit" gorm:"type:tinyint(1);not null;default:0;comment:新是否是套装书(0:否,1:是)"`
|
||||
Status int8 `json:"status" gorm:"type:tinyint(1);not null;default:0;comment:状态(0:审核中,1:通过 2:驳回)"`
|
||||
CreateBy int64 `json:"create_by" gorm:"not null;default:0;comment:创建人"`
|
||||
AuditBy int64 `json:"audit_by" gorm:"not null;default:0;comment:审核人"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳"`
|
||||
IsDel int8 `json:"is_del" gorm:"not null;default:0;comment:逻辑删除"`
|
||||
}
|
||||
|
||||
func (ProductLog) TableName() string {
|
||||
return "product_log"
|
||||
}
|
||||
|
||||
func (ProductLog) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品日志表'"
|
||||
}
|
||||
21
models/product_serial.go
Normal file
21
models/product_serial.go
Normal file
@ -0,0 +1,21 @@
|
||||
package models
|
||||
|
||||
// ProductSerial 商品序列号表
|
||||
type ProductSerial struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:自增ID"`
|
||||
ProductID int64 `json:"product_id" gorm:"not null;default:0;index;comment:商品ID"`
|
||||
SerialNumber string `json:"serial_number" gorm:"size:100;not null;default:'';uniqueIndex:uk_serial;comment:序列号"`
|
||||
Status int8 `json:"status" gorm:"not null;default:1;comment:状态(1:在库/in_stock,2:已售/sold,3:已退货/returned,4:已报废/scrapped)"`
|
||||
InboundDate int64 `json:"inbound_date" gorm:"not null;default:0;comment:入库时间戳(秒)"`
|
||||
OutboundDate int64 `json:"outbound_date" gorm:"not null;default:0;comment:出库时间戳(秒)"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (ProductSerial) TableName() string {
|
||||
return "product_serial"
|
||||
}
|
||||
|
||||
func (ProductSerial) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品序列号表'"
|
||||
}
|
||||
19
models/product_unit_conversion.go
Normal file
19
models/product_unit_conversion.go
Normal file
@ -0,0 +1,19 @@
|
||||
package models
|
||||
|
||||
// ProductUnitConversion 商品单位换算表
|
||||
type ProductUnitConversion struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:自增ID"`
|
||||
ProductID int64 `json:"product_id" gorm:"not null;uniqueIndex:uk_product_unit;comment:商品ID"`
|
||||
FromUnit string `json:"from_unit" gorm:"size:20;not null;default:'';uniqueIndex:uk_product_unit;comment:源单位"`
|
||||
ToUnit string `json:"to_unit" gorm:"size:20;not null;default:'';uniqueIndex:uk_product_unit;comment:目标单位"`
|
||||
ConversionRate int64 `json:"conversion_rate" gorm:"not null;comment:换算率(放大10000倍的整数,如1箱=12个,则存120000)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (ProductUnitConversion) TableName() string {
|
||||
return "product_unit_conversion"
|
||||
}
|
||||
|
||||
func (ProductUnitConversion) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品单位换算表'"
|
||||
}
|
||||
27
models/purchase_order.go
Normal file
27
models/purchase_order.go
Normal file
@ -0,0 +1,27 @@
|
||||
package models
|
||||
|
||||
// PurchaseOrder 采购订单主表
|
||||
type PurchaseOrder struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:采购单ID"`
|
||||
PoNo string `json:"po_no" gorm:"size:100;not null;default:'';uniqueIndex;comment:采购单号(唯一)"`
|
||||
SupplierID int64 `json:"supplier_id" gorm:"not null;default:0;index;comment:供应商ID"`
|
||||
WarehouseID int64 `json:"warehouse_id" gorm:"not null;default:0;comment:预计入库仓库ID"`
|
||||
OrderDate int64 `json:"order_date" gorm:"not null;default:0;comment:订单日期时间戳(秒)"`
|
||||
ExpectedArrivalDate int64 `json:"expected_arrival_date" gorm:"not null;default:0;comment:预计到货日期时间戳(秒)"`
|
||||
TotalAmount int64 `json:"total_amount" gorm:"not null;default:0;comment:采购总金额(单位:分)"`
|
||||
Status int8 `json:"status" gorm:"not null;default:1;index;comment:状态(1:草稿/draft, 2:已提交/submitted, 3:已审核/approved, 4:部分收货/partial_received, 5:已收货/received, 6:已取消/cancelled)"`
|
||||
Creator string `json:"creator" gorm:"size:100;not null;default:'';comment:创建人"`
|
||||
CreatorID int64 `json:"creator_id" gorm:"not null;default:0;comment:创建人ID"`
|
||||
Remark string `json:"remark" gorm:"size:255;not null;default:'';comment:备注"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (PurchaseOrder) TableName() string {
|
||||
return "purchase_order"
|
||||
}
|
||||
|
||||
func (PurchaseOrder) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购订单主表'"
|
||||
}
|
||||
23
models/purchase_order_item.go
Normal file
23
models/purchase_order_item.go
Normal file
@ -0,0 +1,23 @@
|
||||
package models
|
||||
|
||||
// PurchaseOrderItem 采购订单明细表
|
||||
type PurchaseOrderItem struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:明细ID"`
|
||||
PurchaseOrderID int64 `json:"purchase_order_id" gorm:"not null;default:0;index;comment:采购单ID"`
|
||||
ProductID int64 `json:"product_id" gorm:"not null;default:0;index;comment:商品ID"`
|
||||
Quantity int64 `json:"quantity" gorm:"not null;default:0;comment:采购数量(最小单位)"`
|
||||
ReceivedQuantity int64 `json:"received_quantity" gorm:"not null;default:0;comment:已入库数量"`
|
||||
UnitPrice int64 `json:"unit_price" gorm:"not null;default:0;comment:单价(单位:分/基本单位)"`
|
||||
Amount int64 `json:"amount" gorm:"comment:金额(分)"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (PurchaseOrderItem) TableName() string {
|
||||
return "purchase_order_item"
|
||||
}
|
||||
|
||||
func (PurchaseOrderItem) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购订单明细表'"
|
||||
}
|
||||
27
models/receiving_order.go
Normal file
27
models/receiving_order.go
Normal file
@ -0,0 +1,27 @@
|
||||
package models
|
||||
|
||||
// ReceivingOrder 入库单主表
|
||||
type ReceivingOrder struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:入库单ID"`
|
||||
ReceivingNo string `json:"receiving_no" gorm:"size:100;not null;default:'';uniqueIndex;comment:入库单号"`
|
||||
PurchaseOrderID int64 `json:"purchase_order_id" gorm:"not null;default:0;index;comment:关联采购单ID(可为空,支持无来源入库)"`
|
||||
WaveTaskID int64 `json:"wave_task_id" gorm:"not null;default:0;index;comment:关联波次任务ID"`
|
||||
WarehouseID int64 `json:"warehouse_id" gorm:"not null;default:0;index;comment:入库仓库ID"`
|
||||
SupplierID int64 `json:"supplier_id" gorm:"not null;default:0;comment:供应商ID"`
|
||||
ReceivingDate int64 `json:"receiving_date" gorm:"not null;default:0;comment:入库日期时间戳(秒)"`
|
||||
Status int8 `json:"status" gorm:"not null;default:1;index;comment:状态(1:待收货/pending, 2:验收中/checking, 3:已完成/completed, 4:已取消/cancelled)"`
|
||||
Operator string `json:"operator" gorm:"size:100;not null;default:'';comment:操作人"`
|
||||
OperatorID int64 `json:"operator_id" gorm:"not null;default:0;comment:操作人ID"`
|
||||
Remark string `json:"remark" gorm:"size:255;not null;default:'';comment:备注"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (ReceivingOrder) TableName() string {
|
||||
return "receiving_order"
|
||||
}
|
||||
|
||||
func (ReceivingOrder) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入库单主表'"
|
||||
}
|
||||
24
models/receiving_order_item.go
Normal file
24
models/receiving_order_item.go
Normal file
@ -0,0 +1,24 @@
|
||||
package models
|
||||
|
||||
// ReceivingOrderItem 入库单明细表
|
||||
type ReceivingOrderItem struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:明细ID"`
|
||||
ReceivingOrderID int64 `json:"receiving_order_id" gorm:"not null;default:0;index;comment:入库单ID"`
|
||||
ProductID int64 `json:"product_id" gorm:"not null;default:0;index;comment:商品ID"`
|
||||
LocationID int64 `json:"location_id" gorm:"not null;default:0;index;comment:入库库位ID"`
|
||||
BatchNo string `json:"batch_no" gorm:"size:100;not null;default:'';comment:批次号"`
|
||||
ProductionDate int64 `json:"production_date" gorm:"not null;default:0;comment:生产日期时间戳(秒)"`
|
||||
ExpiryDate int64 `json:"expiry_date" gorm:"not null;default:0;comment:失效日期时间戳(秒)"`
|
||||
Quantity int64 `json:"quantity" gorm:"not null;comment:入库数量(最小单位)"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||
}
|
||||
|
||||
func (ReceivingOrderItem) TableName() string {
|
||||
return "receiving_order_item"
|
||||
}
|
||||
|
||||
func (ReceivingOrderItem) TableOptions() string {
|
||||
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入库单明细表'"
|
||||
}
|
||||
64
models/request/Employee.go
Normal file
64
models/request/Employee.go
Normal file
@ -0,0 +1,64 @@
|
||||
package request
|
||||
|
||||
// LoginRequest 登录请求
|
||||
type LoginRequest struct {
|
||||
Username string `form:"username" binding:"required"`
|
||||
Password string `form:"password" binding:"required"`
|
||||
AboutID int64 `form:"about_id"` // 租户ID(从ERP关联)
|
||||
Type int64 `form:"type"` // 类型 1web 2pda
|
||||
Code string `form:"code"` // 机械码
|
||||
}
|
||||
|
||||
// GetEmployeeListRequest 获取员工列表请求
|
||||
type GetEmployeeListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
Fid int64 `form:"fid"`
|
||||
Status string `form:"status"`
|
||||
Keyword string `form:"keyword"`
|
||||
}
|
||||
|
||||
// AddEmployeeRequest 添加员工请求
|
||||
type AddEmployeeRequest struct {
|
||||
Name string `form:"name" binding:"required"`
|
||||
UserName string `form:"username"`
|
||||
AboutId int64 `form:"about_id"`
|
||||
Fid int64 `form:"fid"`
|
||||
Phone string `form:"phone"`
|
||||
Password string `form:"password" binding:"required,min=6"`
|
||||
From string `form:"from"`
|
||||
ExpireTime int64 `form:"expire_time"`
|
||||
}
|
||||
|
||||
// UpdatePasswordEmployeeRequest 修改员工密码请求
|
||||
type UpdatePasswordEmployeeRequest struct {
|
||||
Id int64 `form:"id" binding:"required"`
|
||||
AboutId int64 `form:"about_id"`
|
||||
Password string `form:"password" binding:"required,min=6"`
|
||||
}
|
||||
|
||||
// UpdateExpireTimeEmployeeRequest 修改员工过期时间请求
|
||||
type UpdateExpireTimeEmployeeRequest struct {
|
||||
Id int64 `form:"id" binding:"required"`
|
||||
AboutId int64 `form:"about_id"`
|
||||
ExpireTime int64 `form:"expire_time" binding:"required"`
|
||||
}
|
||||
|
||||
// CheckCodeEmployeeRequest 校验员工机械码
|
||||
type CheckCodeEmployeeRequest struct {
|
||||
UserName string `form:"username" binding:"required"`
|
||||
}
|
||||
|
||||
// ClearCodeEmployeeRequest 清除员工机械码请求
|
||||
type ClearCodeEmployeeRequest struct {
|
||||
UserName string `form:"username" binding:"required"`
|
||||
}
|
||||
|
||||
// SetEmployeeLevelRequest 设置员工等级
|
||||
type SetEmployeeLevelRequest struct {
|
||||
EmpId int64 `form:"emp_id" binding:"required"`
|
||||
Level int8 `form:"level" binding:"required,min=1,max=5"` // 等级(1-5)
|
||||
PayTime int64 `form:"pay_time" binding:"required"`
|
||||
OperationType int8 `form:"operation_type" binding:"required,oneof=1 2 3"` // 操作类型(1:开通,2:升级,3:续费)
|
||||
Duration int64 `form:"duration" binding:"required,min=1"` // 时长(月数),按30天/月计算
|
||||
}
|
||||
5
models/request/barcode.go
Normal file
5
models/request/barcode.go
Normal file
@ -0,0 +1,5 @@
|
||||
package request
|
||||
|
||||
type BarcodeRequest struct {
|
||||
Content string `form:"content" binding:"required"`
|
||||
}
|
||||
35
models/request/book.go
Normal file
35
models/request/book.go
Normal file
@ -0,0 +1,35 @@
|
||||
package request
|
||||
|
||||
type BookRequest struct {
|
||||
Isbn string `form:"isbn" binding:"required"`
|
||||
}
|
||||
|
||||
type GetCodeRequest struct {
|
||||
BookName string `form:"book_name" binding:"required"`
|
||||
Author string `form:"author" binding:"required"`
|
||||
Publisher string `form:"publisher" binding:"required"`
|
||||
}
|
||||
|
||||
type AddBookRequest struct {
|
||||
Fid int64 `form:"fid"` // 父ID
|
||||
Type int8 `form:"type" binding:"required"` // 类型 1:普通图书 2:套装图书 3:一号多书 4:无书号
|
||||
Isbn string `form:"isbn" binding:"required"` // ISBN
|
||||
FIsbn string `form:"f_isbn" binding:"required"` // 副ISBN
|
||||
BookName string `form:"book_name" binding:"required"` // 书名
|
||||
FBookName string `form:"f_book_name"` // 副书名
|
||||
Author string `form:"author"` // 作者
|
||||
Publisher string `form:"publisher"` // 出版社
|
||||
PublicationTime int64 `form:"publication_time"` // 出版时间
|
||||
BindingLayout string `form:"binding_layout"` // 装帧
|
||||
FixPrice int64 `form:"fix_price"` // 定价
|
||||
PageCount int64 `form:"page_count"` // 页数
|
||||
WordCount int64 `form:"word_count"` // 字数
|
||||
BookFormat int64 `form:"book_format"` // 图书格式
|
||||
LiveImage []string `form:"live_image"` // 图片
|
||||
}
|
||||
|
||||
type GetNoIsbnBookRequest struct {
|
||||
BookName string `form:"book_name" binding:"required"` // 书名
|
||||
Author string `form:"author"` // 作者
|
||||
Publisher string `form:"publisher"` // 出版社
|
||||
}
|
||||
7
models/request/cancel_logistics.go
Normal file
7
models/request/cancel_logistics.go
Normal file
@ -0,0 +1,7 @@
|
||||
package request
|
||||
|
||||
// CancelLogisticsRequest 取消物流单号请求
|
||||
type CancelLogisticsRequest struct {
|
||||
UserID int64 `form:"user_id" binding:"required"`
|
||||
LogisticsNo string `form:"logistics_no" binding:"required"`
|
||||
}
|
||||
32
models/request/car.go
Normal file
32
models/request/car.go
Normal file
@ -0,0 +1,32 @@
|
||||
package request
|
||||
|
||||
type QueryCarRequest struct {
|
||||
Keyword string `form:"keyword"`
|
||||
Page int `form:"page,default=1"`
|
||||
PageSize int `form:"page_size,default=10"`
|
||||
}
|
||||
|
||||
type CreateCarRequest struct {
|
||||
WarehouseID int64 `form:"warehouse_id" binding:"required"`
|
||||
PushType int8 `form:"push_type" binding:"required"`
|
||||
ReleaseType int8 `form:"release_type" binding:"required"`
|
||||
Code int64 `form:"code" binding:"required"`
|
||||
Name string `form:"name" binding:"required,max=50"`
|
||||
Capacity int64 `form:"capacity" binding:"required"`
|
||||
Appearance int64 `form:"appearance" binding:"required"`
|
||||
ShopInfo []map[string]interface{} `form:"shop_info[]"`
|
||||
}
|
||||
|
||||
type UpdateCarRequest struct {
|
||||
PushType *int8 `form:"push_type" binding:"required"`
|
||||
ReleaseType *int8 `form:"release_type" binding:"required"`
|
||||
ID int64 `form:"id" binding:"required"`
|
||||
Name string `form:"name" binding:"omitempty,max=50"`
|
||||
Capacity *int64 `form:"capacity" binding:"required"`
|
||||
Appearance *int64 `form:"appearance" binding:"required"`
|
||||
ShopInfo []map[string]interface{} `form:"shop_info[]"`
|
||||
}
|
||||
|
||||
type DeleteCarRequest struct {
|
||||
ID int64 `form:"id" binding:"required"`
|
||||
}
|
||||
26
models/request/config.go
Normal file
26
models/request/config.go
Normal file
@ -0,0 +1,26 @@
|
||||
package request
|
||||
|
||||
// GetConfigListRequest 获取配置列表请求
|
||||
type GetConfigListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
Keyword string `form:"keyword"`
|
||||
}
|
||||
|
||||
// AddConfigRequest 添加配置请求
|
||||
type AddConfigRequest struct {
|
||||
Key string `form:"key" binding:"required"`
|
||||
Value string `form:"value" binding:"required"`
|
||||
}
|
||||
|
||||
// UpdateConfigRequest 更新配置请求
|
||||
type UpdateConfigRequest struct {
|
||||
ID int64 `form:"id" binding:"required"`
|
||||
Key string `form:"key"`
|
||||
Value string `form:"value"`
|
||||
}
|
||||
|
||||
// DeleteConfigRequest 删除配置请求
|
||||
type DeleteConfigRequest struct {
|
||||
ID int64 `form:"id" binding:"required"`
|
||||
}
|
||||
14
models/request/employee_settings.go
Normal file
14
models/request/employee_settings.go
Normal file
@ -0,0 +1,14 @@
|
||||
package request
|
||||
|
||||
import "psi/models"
|
||||
|
||||
// GetEmployeeSettingsRequest 获取员工设置请求
|
||||
type GetEmployeeSettingsRequest struct {
|
||||
EmpId int64 `form:"emp_id"`
|
||||
}
|
||||
|
||||
// SaveEmployeeSettingsRequest 保存员工设置请求
|
||||
type SaveEmployeeSettingsRequest struct {
|
||||
EmpId int64 `form:"emp_id"`
|
||||
models.EmployeeSettingsConfig
|
||||
}
|
||||
34
models/request/goods_import.go
Normal file
34
models/request/goods_import.go
Normal file
@ -0,0 +1,34 @@
|
||||
package request
|
||||
|
||||
// GoodsImportRow Excel 表格中每一行的数据结构(对应货号批量修改工具导出格式)
|
||||
type GoodsImportRow struct {
|
||||
GoodsNo string // 商品编号
|
||||
GoodsName string // 商品名称
|
||||
Huohao string // 货号(原)
|
||||
HuohaoNew string // 货号[新](新货号,用作库位编码)
|
||||
ISBN string // ISBN
|
||||
Author string // 作者
|
||||
Publisher string // 出版社
|
||||
PriceListing string // 商品定价
|
||||
PriceSale string // 商品售价
|
||||
Category string // 商品分类
|
||||
ShopCategory string // 本店分类
|
||||
Appearance string // 品相
|
||||
Inventory string // 库存
|
||||
ProductID string // 商品ID
|
||||
ShopID string // 店铺ID
|
||||
LiveImage string // 商品图片(工具导出时已处理为URL)
|
||||
}
|
||||
|
||||
// GoodsImportResult 导入结果
|
||||
type GoodsImportResult struct {
|
||||
SuccessCount int
|
||||
FailCount int
|
||||
FailDetails []string
|
||||
Message string
|
||||
}
|
||||
|
||||
func (r *GoodsImportResult) AddFail(detail string) {
|
||||
r.FailCount++
|
||||
r.FailDetails = append(r.FailDetails, detail)
|
||||
}
|
||||
61
models/request/inventory.go
Normal file
61
models/request/inventory.go
Normal file
@ -0,0 +1,61 @@
|
||||
package request
|
||||
|
||||
// GetInventoryListRequest 获取库存汇总列表请求
|
||||
type GetInventoryListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
ProductId int64 `form:"product_id"`
|
||||
WarehouseID int64 `form:"warehouse_id"`
|
||||
ISBN string `form:"isbn"`
|
||||
Name string `form:"name"`
|
||||
}
|
||||
|
||||
// GetInventoryGroupedListRequest 获取按仓库库位分组的库存列表请求
|
||||
type GetInventoryGroupedListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
ProductId int64 `form:"product_id"`
|
||||
WarehouseID int64 `form:"warehouse_id"`
|
||||
}
|
||||
|
||||
// GetInventoryDetailRequest 获取库存明细请求
|
||||
type GetInventoryDetailRequest struct {
|
||||
ProductID int64 `form:"product_id" binding:"required"`
|
||||
}
|
||||
|
||||
// GetInventoryLogListRequest 获取库存流水列表请求
|
||||
type GetInventoryLogListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
ISBN string `form:"isbn"`
|
||||
BookName string `form:"book_name"`
|
||||
WarehouseID int64 `form:"warehouse_id"`
|
||||
ChangeType int8 `form:"change_type"`
|
||||
RelatedOrderNo string `form:"related_order_no"`
|
||||
StartDate int64 `form:"start_date"`
|
||||
EndDate int64 `form:"end_date"`
|
||||
}
|
||||
|
||||
// InventoryStatistRequest 获取条码/品相库存总数请求
|
||||
type InventoryStatistRequest struct {
|
||||
Barcode string `form:"barcode"`
|
||||
Appearance int64 `form:"appearance"`
|
||||
}
|
||||
|
||||
// GetStockCheckListRequest 获取盘库列表请求
|
||||
type GetStockCheckListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
WarehouseID int64 `form:"warehouse_id"`
|
||||
Status int8 `form:"status"`
|
||||
CheckNo string `form:"check_no"`
|
||||
StartDate int64 `form:"start_date"`
|
||||
EndDate int64 `form:"end_date"`
|
||||
}
|
||||
|
||||
// GetStockCheckDetailRequest 获取盘库明细列表请求
|
||||
type GetStockCheckDetailRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
StockCheckID int64 `form:"stock_check_id" binding:"required"`
|
||||
}
|
||||
125
models/request/location.go
Normal file
125
models/request/location.go
Normal file
@ -0,0 +1,125 @@
|
||||
package request
|
||||
|
||||
// QueryAllLocationRequest 查询所有库位请求(仓库ID可选)
|
||||
type QueryAllLocationRequest struct {
|
||||
WarehouseID *int64 `json:"warehouse_id" form:"warehouse_id"`
|
||||
Code string `json:"code" form:"code"`
|
||||
Type *int8 `json:"type" form:"type"`
|
||||
Status *int8 `json:"status" form:"status"`
|
||||
Page int `json:"page" form:"page,default=1"`
|
||||
PageSize int `json:"page_size" form:"page_size,default=10"`
|
||||
}
|
||||
|
||||
type QueryLocationRequest struct {
|
||||
WarehouseID int64 `json:"warehouse_id" form:"warehouse_id" binding:"required"`
|
||||
Code string `json:"code" form:"code"`
|
||||
Type *int8 `json:"type" form:"type"`
|
||||
Status *int8 `json:"status" form:"status"`
|
||||
Page int `json:"page" form:"page,default=1"`
|
||||
PageSize int `json:"page_size" form:"page_size,default=10"`
|
||||
}
|
||||
|
||||
type CreateLocationRequest struct {
|
||||
WarehouseID int64 `form:"warehouse_id" binding:"required"`
|
||||
Code string `form:"code" binding:"required,max=50"`
|
||||
Type int8 `form:"type" binding:"required"`
|
||||
Capacity int64 `form:"capacity" binding:"required"`
|
||||
Status int8 `form:"status" binding:"required"`
|
||||
}
|
||||
|
||||
type BatchGenerateLocationRequest struct {
|
||||
WarehouseID int64 `form:"warehouse_id" binding:"required"`
|
||||
Groups []GroupConfig `form:"groups[]"`
|
||||
Type int8 `form:"type"`
|
||||
Capacity int64 `form:"capacity"`
|
||||
Status int8 `form:"status"`
|
||||
}
|
||||
|
||||
type GroupConfig struct {
|
||||
FormatType int `form:"format_type" binding:"required,oneof=1 2 3 4"`
|
||||
StartValue string `form:"start_value" binding:"required"`
|
||||
EndValue string `form:"end_value" binding:"required"`
|
||||
PaddingLen int `form:"padding_len"`
|
||||
Separator string `form:"separator" binding:"max=5"`
|
||||
}
|
||||
|
||||
type UpdateLocationRequest struct {
|
||||
WarehouseID int64 `form:"warehouse_id" binding:"required"`
|
||||
IDs []int64 `form:"ids[]"`
|
||||
Code string `form:"code"`
|
||||
Type *int8 `form:"type"`
|
||||
Capacity *int64 `form:"capacity"`
|
||||
Sort *int `form:"sort"`
|
||||
Status *int8 `form:"status"`
|
||||
}
|
||||
|
||||
type DeleteLocationRequest struct {
|
||||
IDs []int64 `form:"ids[]"`
|
||||
}
|
||||
type GetLocationIdRequest struct {
|
||||
Code string `form:"code"`
|
||||
WarehouseCode string `form:"warehouse_code"`
|
||||
}
|
||||
|
||||
type SyncLocationRequest struct {
|
||||
UserID int64 `json:"user_id" binding:"required"`
|
||||
Data []SyncLocationAreaRequest `json:"data" binding:"required"`
|
||||
}
|
||||
|
||||
type SyncLocationAreaRequest struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
TemplateId int64 `json:"templateId" binding:"required"`
|
||||
Status string `json:"status" binding:"required"`
|
||||
Logistics SyncLogisticsRequest `json:"logistics" binding:"required"`
|
||||
Data []SyncLocationItemRequest `json:"data" binding:"required"`
|
||||
}
|
||||
|
||||
type SyncLogisticsRequest struct {
|
||||
Id int64 `json:"id"`
|
||||
TemplateName string `json:"templateName" binding:"required"`
|
||||
DeliveryProvince string `json:"deliveryProvince" binding:"required"`
|
||||
DeliveryCity string `json:"deliveryCity" binding:"required"`
|
||||
DeliveryArea string `json:"deliveryArea" binding:"required"`
|
||||
DeliveryAddress string `json:"deliveryAddress" binding:"required"`
|
||||
PricingMethod string `json:"pricingMethod" binding:"required"`
|
||||
Shipping string `json:"shipping" binding:"required"`
|
||||
FirWbv float64 `json:"firWbv"`
|
||||
FirPrice float64 `json:"firPrice"`
|
||||
ContinueWbv float64 `json:"continueWbv"`
|
||||
ContinuePrice float64 `json:"continuePrice"`
|
||||
CreateBy int64 `json:"createBy"`
|
||||
CreateTime string `json:"createTime"`
|
||||
UpdateBy int64 `json:"updateBy"`
|
||||
UpdateTime string `json:"updateTime"`
|
||||
Status string `json:"status"`
|
||||
DelFlag string `json:"delFlag"`
|
||||
TenantId string `json:"tenantId"`
|
||||
CreateDept int64 `json:"createDept"`
|
||||
ShippingRange string `json:"shippingRange"`
|
||||
WarehouseId int64 `json:"warehouseId"`
|
||||
Remark string `json:"remark"`
|
||||
PhoneNumber int64 `json:"phoneNumber"`
|
||||
Contact string `json:"contact"`
|
||||
FullAddress string `json:"fullAddress"`
|
||||
}
|
||||
|
||||
type SyncLocationItemRequest struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
SheQuantityMax int64 `json:"sheQuantityMax" binding:"required"`
|
||||
}
|
||||
|
||||
type SyncGoodsRequest struct {
|
||||
UserID int64 `json:"user_id" binding:"required"`
|
||||
Data []SyncGoodsItem `json:"data" binding:"required"`
|
||||
}
|
||||
|
||||
type SyncGoodsItem struct {
|
||||
GoodsName string `json:"goods_name" binding:"required"`
|
||||
ISBN string `json:"isbn" binding:"required"`
|
||||
Price int64 `json:"price" binding:"required"`
|
||||
Appearance string `json:"appearance" binding:"required"`
|
||||
LiveImage []string `json:"live_image" binding:"required"`
|
||||
LocationCode string `json:"location_code" binding:"required"`
|
||||
Inventory int64 `json:"inventory" binding:"required"`
|
||||
}
|
||||
6
models/request/location_import.go
Normal file
6
models/request/location_import.go
Normal file
@ -0,0 +1,6 @@
|
||||
package request
|
||||
|
||||
// LocationImportRow Excel中单行库位数据
|
||||
type LocationImportRow struct {
|
||||
Code string // 库位编码
|
||||
}
|
||||
55
models/request/logistics.go
Normal file
55
models/request/logistics.go
Normal file
@ -0,0 +1,55 @@
|
||||
package request
|
||||
|
||||
type QueryLogisticsRequest struct {
|
||||
Keyword string `form:"keyword"`
|
||||
Status *int `form:"status"`
|
||||
Page int `form:"page,default=1"`
|
||||
PageSize int `form:"page_size,default=10"`
|
||||
}
|
||||
|
||||
type CreateLogisticsRequest struct {
|
||||
TemplateName string `form:"template_name" binding:"required,max=255"`
|
||||
DeliveryProvince string `form:"delivery_province" binding:"required,max=255"`
|
||||
DeliveryCity string `form:"delivery_city" binding:"required,max=255"`
|
||||
DeliveryArea string `form:"delivery_area" binding:"required,max=255"`
|
||||
DeliveryAddress string `form:"delivery_address" binding:"required,max=255"`
|
||||
PricingMethod string `form:"pricing_method" binding:"required,oneof=0 1 2 3"`
|
||||
Shipping string `form:"shipping" binding:"required,oneof=0 1 2"`
|
||||
FirWbv float64 `form:"fir_wbv"`
|
||||
FirPrice float64 `form:"fir_price"`
|
||||
ContinueWbv float64 `form:"continue_wbv"`
|
||||
ContinuePrice float64 `form:"continue_price"`
|
||||
Contact string `form:"contact" binding:"required,max=14"`
|
||||
PhoneNumber uint64 `form:"phone_number" binding:"required"`
|
||||
FullAddress string `form:"full_address" binding:"required,max=255"`
|
||||
ShippingRange string `form:"shipping_range" binding:"required"`
|
||||
WarehouseId uint64 `form:"warehouse_id"`
|
||||
Remark string `form:"remark" binding:"max=255"`
|
||||
Status string `form:"status" binding:"oneof=0 1"`
|
||||
}
|
||||
|
||||
type UpdateLogisticsRequest struct {
|
||||
Id uint64 `form:"id" binding:"required"`
|
||||
TemplateName string `form:"template_name" binding:"required,max=255"`
|
||||
DeliveryProvince string `form:"delivery_province" binding:"required,max=255"`
|
||||
DeliveryCity string `form:"delivery_city" binding:"required,max=255"`
|
||||
DeliveryArea string `form:"delivery_area" binding:"required,max=255"`
|
||||
DeliveryAddress string `form:"delivery_address" binding:"required,max=255"`
|
||||
PricingMethod string `form:"pricing_method" binding:"required,oneof=0 1 2 3"`
|
||||
Shipping string `form:"shipping" binding:"required,oneof=0 1 2"`
|
||||
FirWbv float64 `form:"fir_wbv"`
|
||||
FirPrice float64 `form:"fir_price"`
|
||||
ContinueWbv float64 `form:"continue_wbv"`
|
||||
ContinuePrice float64 `form:"continue_price"`
|
||||
Contact string `form:"contact" binding:"required,max=14"`
|
||||
PhoneNumber uint64 `form:"phone_number" binding:"required"`
|
||||
FullAddress string `form:"full_address" binding:"required,max=255"`
|
||||
ShippingRange string `form:"shipping_range" binding:"required"`
|
||||
WarehouseId uint64 `form:"warehouse_id"`
|
||||
Remark string `form:"remark" binding:"max=255"`
|
||||
Status string `form:"status" binding:"oneof=0 1"`
|
||||
}
|
||||
|
||||
type DeleteLogisticsRequest struct {
|
||||
Id uint64 `form:"id" binding:"required"`
|
||||
}
|
||||
44
models/request/out_task.go
Normal file
44
models/request/out_task.go
Normal file
@ -0,0 +1,44 @@
|
||||
package request
|
||||
|
||||
// GetOutTaskListRequest 获取外部任务列表请求
|
||||
type GetOutTaskListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
ShopID int64 `form:"shop_id"`
|
||||
WaveTaskID int64 `form:"wave_task_id"`
|
||||
ShopType int8 `form:"shop_type"`
|
||||
TaskType int8 `form:"task_type"`
|
||||
Status int8 `form:"status"`
|
||||
StartDate int64 `form:"start_date"`
|
||||
EndDate int64 `form:"end_date"`
|
||||
}
|
||||
|
||||
// GetOutTaskLogListRequest 获取外部任务日志列表请求
|
||||
type GetOutTaskLogListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
ShopID int64 `form:"shop_id"`
|
||||
WaveTaskID int64 `form:"wave_task_id"`
|
||||
OutTaskID int64 `form:"out_task_id" binding:"required"`
|
||||
ProductID int64 `form:"product_id"`
|
||||
Status int8 `form:"status"` // 1正常 0异常
|
||||
StartDate int64 `form:"start_date"`
|
||||
EndDate int64 `form:"end_date"`
|
||||
}
|
||||
|
||||
// UpdateOutTaskLogRequest 修改外部任务日志
|
||||
type UpdateOutTaskLogRequest struct {
|
||||
UserID int64 `form:"user_id" binding:"required"`
|
||||
OutTaskID int64 `form:"out_task_id" binding:"required"`
|
||||
ProductID int64 `form:"product_id" binding:"required"`
|
||||
}
|
||||
|
||||
// GetOutTaskByShopRequest 按店铺获取外部任务列表请求
|
||||
type GetOutTaskByShopRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
ShopID int64 `form:"shop_id"`
|
||||
ShopType int8 `form:"shop_type"`
|
||||
StartDate int64 `form:"start_date"`
|
||||
EndDate int64 `form:"end_date"`
|
||||
}
|
||||
24
models/request/outbound.go
Normal file
24
models/request/outbound.go
Normal file
@ -0,0 +1,24 @@
|
||||
package request
|
||||
|
||||
// GetOutboundOrderListRequest 获取出库单列表请求
|
||||
type GetOutboundOrderListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
OutNo string `form:"out_no"`
|
||||
Status int8 `form:"status"`
|
||||
CustomerID int64 `form:"customer_id"`
|
||||
WarehouseID int64 `form:"warehouse_id"`
|
||||
StartDate int64 `form:"start_date"`
|
||||
EndDate int64 `form:"end_date"`
|
||||
}
|
||||
|
||||
// GetOutboundOrderDetailRequest 获取出库单详情请求
|
||||
type GetOutboundOrderDetailRequest struct {
|
||||
ID int64 `form:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// ChangeLocationRequest 出库单切换库位请求
|
||||
type ChangeLocationRequest struct {
|
||||
OutOrderItemID int64 `form:"out_order_item_id" binding:"required"`
|
||||
Remark string `form:"remark"`
|
||||
}
|
||||
156
models/request/process.go
Normal file
156
models/request/process.go
Normal file
@ -0,0 +1,156 @@
|
||||
package request
|
||||
|
||||
type PurchaseOrderCreateRequest struct {
|
||||
CarID int64 `form:"car_id" binding:"required"` // 采购订单ID
|
||||
CarCode int64 `form:"car_code" binding:"required"` // 采购订单编号
|
||||
WarehouseID int64 `form:"warehouse_id" binding:"required"` // 仓库ID
|
||||
SupplierID int64 `form:"supplier_id"` // 供应商ID
|
||||
Direction int8 `form:"direction"` // 采购订单方向
|
||||
ExpectedArrivalDate int64 `form:"expected_arrival_date"` // 预计到达时间
|
||||
Remark string `form:"remark"` // 备注
|
||||
Items []PurchaseOrderItemRequest `form:"items[]"` // 采购订单项
|
||||
}
|
||||
|
||||
type PurchaseOrderItemRequest struct {
|
||||
ProductID int64 `form:"product_id" binding:"required"` // 商品ID
|
||||
Quantity int64 `form:"quantity" binding:"required,gt=0"` // 数量
|
||||
UnitPrice int64 `form:"unit_price" binding:"required,gt=0"` // 单价
|
||||
}
|
||||
|
||||
type WaveRequest struct {
|
||||
WaveID int64 `form:"wave_id" binding:"required"`
|
||||
CarID int64 `form:"car_id"`
|
||||
CarCode int64 `form:"car_code"`
|
||||
RelatedOrderID int64 `form:"related_order_id" binding:"required"`
|
||||
Assignee string `form:"assignee"`
|
||||
AssigneeId int64 `form:"assignee_id"`
|
||||
Items []WaveItemRequest `form:"items[]"`
|
||||
}
|
||||
|
||||
type WaveItemRequest struct {
|
||||
ProductID int64 `form:"product_id" binding:"required"`
|
||||
Quantity int64 `form:"quantity" binding:"required,gt=0"`
|
||||
UnitPrice int64 `form:"unit_price" binding:"required,gt=0"`
|
||||
}
|
||||
|
||||
type BindWaveRequest struct {
|
||||
WaveNo string `form:"wave_no" binding:"required"`
|
||||
Operator string `form:"operator" binding:"required"`
|
||||
OperatorID int64 `form:"operator_id" binding:"required"`
|
||||
Remark string `form:"remark"`
|
||||
}
|
||||
|
||||
type GetIdRequest struct {
|
||||
ID int64 `form:"id" binding:"required"`
|
||||
}
|
||||
|
||||
type ReceivingSubmitRequest struct {
|
||||
ReceivingOrderID int64 `form:"receiving_order_id" binding:"required"`
|
||||
WaveTaskID int64 `form:"wave_task_id" binding:"required"`
|
||||
Force int8 `form:"force"`
|
||||
Items []ReceivingItemRequest `form:"items[]"`
|
||||
}
|
||||
|
||||
type ReceivingItemRequest struct {
|
||||
ProductID int64 `form:"product_id" binding:"required"`
|
||||
LocationID int64 `form:"location_id" binding:"required"`
|
||||
BatchNo string `form:"batch_no" binding:"required"`
|
||||
ProductionDate int64 `form:"production_date"`
|
||||
ExpiryDate int64 `form:"expiry_date"`
|
||||
Quantity int64 `form:"quantity" binding:"required,gt=0"`
|
||||
//SerialNumbers []string `form:"serial_numbers"`
|
||||
}
|
||||
|
||||
type SalesOrderCreateRequest struct {
|
||||
AboutId int64 `form:"about_id"`
|
||||
AssociationOrderID int64 `form:"association_order_id" binding:"required"`
|
||||
AssociationOrderNo string `form:"association_order_no" binding:"required"`
|
||||
FromType int8 `form:"from_type"`
|
||||
ShopType int8 `form:"shop_type"`
|
||||
CustomerID int64 `form:"customer_id"`
|
||||
RequiredDeliveryDate int64 `form:"required_delivery_date"`
|
||||
Remark string `form:"remark"`
|
||||
SalesPerson string `form:"sales_person"`
|
||||
SalesPersonID int64 `form:"sales_person_id"`
|
||||
ReceiverName string `form:"receiver_name"`
|
||||
ReceiverPhone string `form:"receiver_phone"`
|
||||
ReceiverAddress string `form:"receiver_address"`
|
||||
IsDistribution int8 `form:"is_distribution"`
|
||||
Items []SalesOrderItemRequest `form:"items[]"`
|
||||
}
|
||||
|
||||
// CreateOutboundOrderRequest 基于销售订单创建出库单请求
|
||||
type CreateOutboundOrderRequest struct {
|
||||
SalesOrderIDs []int64 `form:"order_ids[]"`
|
||||
Total int `form:"total"`
|
||||
Remark string `form:"remark"`
|
||||
}
|
||||
|
||||
type CreateOutboundWaveRequest struct {
|
||||
OutboundOrderID int64 `form:"outbound_order_id" binding:"required"`
|
||||
}
|
||||
|
||||
type SalesOrderItemRequest struct {
|
||||
ProductID int64 `form:"product_id" binding:"required"`
|
||||
Quantity int64 `form:"quantity" binding:"required,gt=0"`
|
||||
UnitPrice int64 `form:"unit_price" binding:"required,gt=0"`
|
||||
}
|
||||
|
||||
type OutboundSubmitRequest struct {
|
||||
OutboundOrderID int64 `form:"outbound_order_id" binding:"required"`
|
||||
WaveTaskID int64 `form:"wave_task_id" binding:"required"`
|
||||
Force int8 `form:"force"`
|
||||
Items []OutboundItemRequest `form:"items[]"`
|
||||
}
|
||||
|
||||
type OutboundItemRequest struct {
|
||||
ProductID int64 `form:"product_id" binding:"required"`
|
||||
LocationID int64 `form:"location_id" binding:"required"`
|
||||
BatchNo string `form:"batch_no"`
|
||||
ProductionDate int64 `form:"production_date"`
|
||||
ExpiryDate int64 `form:"expiry_date"`
|
||||
Quantity int64 `form:"quantity" binding:"required,gt=0"`
|
||||
}
|
||||
|
||||
// CreateShippingOrderRequest 基于出库单创建发货单请求
|
||||
type CreateShippingOrderRequest struct {
|
||||
OutboundOrderIDs []int64 `form:"order_ids[]"`
|
||||
Total int `form:"total"`
|
||||
ExpectedArriveTime *int64 `form:"expected_arrive_time"`
|
||||
Remark string `form:"remark"`
|
||||
}
|
||||
|
||||
// UpdateShippingLogisticsRequest 更新发货单物流信息请求
|
||||
type UpdateShippingLogisticsRequest struct {
|
||||
ShippingOrderID int64 `form:"shipping_order_id" binding:"required"` // 发货单ID
|
||||
Total int `form:"total"` // 发货单总数
|
||||
SalesOrderItemID int64 `form:"sales_order_item_id" binding:"required"` // 销售订单项ID
|
||||
LogisticsCompany string `form:"logistics_company" binding:"required"` // 物流公司名称
|
||||
LogisticsNo string `form:"logistics_no" binding:"required"` // 物流单号
|
||||
}
|
||||
|
||||
type CancelSalesOrderRequest struct {
|
||||
OrderID int64 `form:"order_id" binding:"required"`
|
||||
}
|
||||
|
||||
type CancelOutboundWaveRequest struct {
|
||||
WaveID int64 `form:"wave_id" binding:"required"`
|
||||
}
|
||||
|
||||
// StockCheckAdjustRequest 盘库调整请求(加库存/减库存)
|
||||
type StockCheckAdjustRequest struct {
|
||||
WarehouseID int64 `form:"warehouse_id" binding:"required"` // 仓库ID
|
||||
ProductID int64 `form:"product_id" binding:"required"` // 商品ID
|
||||
LocationID int64 `form:"location_id" binding:"required"` // 库位ID
|
||||
BatchNo string `form:"batch_no"` // 批次号(可选)
|
||||
Quantity int64 `form:"quantity" binding:"required,min=1"` // 调整数量(正数)
|
||||
AdjustType int8 `form:"adjust_type" binding:"required,oneof=1 2"` // 调整类型:1=加库存,2=减库存
|
||||
Remark string `form:"remark"` // 备注
|
||||
}
|
||||
|
||||
// StockCheckReturnRequest 盘库退货请求
|
||||
type StockCheckReturnRequest struct {
|
||||
SalesOrderID int64 `form:"sales_order_id" binding:"required"` // 销售订单ID
|
||||
SalesOrderItemID int64 `form:"sales_order_item_id" binding:"required"` // 销售订单明细ID
|
||||
Remark string `form:"remark"` // 备注
|
||||
}
|
||||
181
models/request/product.go
Normal file
181
models/request/product.go
Normal file
@ -0,0 +1,181 @@
|
||||
package request
|
||||
|
||||
// ProductRequest 创建商品请求
|
||||
type ProductRequest struct {
|
||||
ID int64 `form:"id"` // 商品ID
|
||||
CategoryID int64 `form:"category_id"` // 分类ID
|
||||
StandardProductID int64 `form:"standard_product_id"` // 标准商品ID
|
||||
Name string `form:"name" binding:"required"` // 商品名称
|
||||
Appearance int64 `form:"appearance" binding:"required"` // 商品外观
|
||||
Barcode string `form:"barcode" binding:"required"` // 商品条码
|
||||
Price int64 `form:"price"` // 商品价格
|
||||
LiveImage []string `form:"live_image[]"` // 商品图片
|
||||
IsBatchManaged int8 `form:"is_batch_managed"` // 批次管理
|
||||
IsShelfLifeManaged int8 `form:"is_shelf_life_managed"` // 保质期管理
|
||||
Status int8 `form:"status"` // 商品状态
|
||||
}
|
||||
|
||||
// GetProductListRequest 获取商品列表请求
|
||||
type GetProductListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
Keyword string `form:"keyword"`
|
||||
Name string `form:"name"`
|
||||
Barcode string `form:"barcode"`
|
||||
Status string `form:"status"`
|
||||
IDs []int64 `form:"ids[]"`
|
||||
StartCreatedAt int64 `form:"start_created_at"`
|
||||
EndCreatedAt int64 `form:"end_created_at"`
|
||||
MinSalePrice int64 `form:"min_sale_price"`
|
||||
MaxSalePrice int64 `form:"max_sale_price"`
|
||||
MinStock int64 `form:"min_stock"`
|
||||
MaxStock int64 `form:"max_stock"`
|
||||
WarehouseID int64 `form:"warehouse_id"`
|
||||
LocationID int64 `form:"location_id"`
|
||||
}
|
||||
|
||||
// GetDistributionProductListRequest 获取分销商品列表请求
|
||||
type GetDistributionProductListRequest struct {
|
||||
UserID int64 `form:"user_id" binding:"required"`
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
Keyword string `form:"keyword"`
|
||||
Name string `form:"name"`
|
||||
Barcode string `form:"barcode"`
|
||||
StartCreatedAt int64 `form:"start_created_at"`
|
||||
EndCreatedAt int64 `form:"end_created_at"`
|
||||
MinSalePrice int64 `form:"min_sale_price"`
|
||||
MaxSalePrice int64 `form:"max_sale_price"`
|
||||
MinStock int64 `form:"min_stock"`
|
||||
MaxStock int64 `form:"max_stock"`
|
||||
StockOperator string `form:"stock_operator"` // 库存运算符: ">", "<", "=", ">=", "<="
|
||||
StockValue int64 `form:"stock_value"` // 库存数值
|
||||
SalePriceOperator string `form:"saleprice_operator"` // 售价运算符: ">", "<", "=", ">=", "<="
|
||||
SalePriceValue int64 `form:"saleprice_value"` // 售价数值
|
||||
WarehouseID int64 `form:"warehouse_id"`
|
||||
LocationID int64 `form:"location_id"`
|
||||
Appearance int64 `form:"appearance"`
|
||||
StartStockAt int64 `form:"start_stock_at"`
|
||||
EndStockAt int64 `form:"end_stock_at"`
|
||||
}
|
||||
|
||||
// UpdatePriceRequest 修改售价请求
|
||||
type UpdatePriceRequest struct {
|
||||
ProductID int64 `form:"product_id" binding:"required"` // 商品ID
|
||||
UserID int64 `form:"user_id" binding:"required"` // 关联ID
|
||||
SalePrice int64 `form:"sale_price" binding:"required"` // 售价
|
||||
Cost int64 `form:"cost"` // 运费
|
||||
}
|
||||
|
||||
// RetryOutTaskRequest 重试外部任务请求
|
||||
type RetryOutTaskRequest struct {
|
||||
ShopID int64 `form:"shop_id" binding:"required"` // 店铺ID
|
||||
ShopType int8 `form:"shop_type" binding:"required"` // 店铺类型
|
||||
ProductID int64 `form:"product_id" binding:"required"` // 商品ID
|
||||
}
|
||||
|
||||
// BatchPushProductRequest 批量推送商品到店铺请求
|
||||
type BatchPushProductRequest struct {
|
||||
UserID int64 `form:"user_id" json:"user_id" binding:"required"` // 用户ID(租户)
|
||||
ShopIDs []int64 `form:"shop_ids" json:"shop_ids" binding:"required"` // 商家ID数组
|
||||
ShopTypes []int8 `form:"shop_types" json:"shop_types" binding:"required"` // 商家类型数组(与shop_ids一一对应)
|
||||
ProductIDs []int64 `form:"product_ids" json:"product_ids" binding:"required"` // 商品ID数组
|
||||
}
|
||||
|
||||
// ExportProductRequest 导出商品请求
|
||||
type ExportProductRequest struct {
|
||||
StartCreatedAt int64 `form:"start_created_at"` // 创建时间开始
|
||||
EndCreatedAt int64 `form:"end_created_at"` // 创建时间结束
|
||||
MinSalePrice int64 `form:"min_sale_price"` // 售价最小值
|
||||
MaxSalePrice int64 `form:"max_sale_price"` // 售价最大值
|
||||
MinStock int64 `form:"min_stock"` // 库存最小值
|
||||
MaxStock int64 `form:"max_stock"` // 库存最大值
|
||||
WarehouseID int64 `form:"warehouse_id"` // 仓库ID
|
||||
Type int8 `form:"type" binding:"oneof=0 1"` // 0: 仓库商品, 1: 标准商品
|
||||
}
|
||||
|
||||
// DeleteProductRequest 删除商品请求
|
||||
type DeleteProductRequest struct {
|
||||
ID int64 `form:"id" binding:"required"` // 商品ID
|
||||
}
|
||||
|
||||
type ProductLogRequest struct {
|
||||
ID int64 `form:"id"`
|
||||
Barcode string `form:"barcode"`
|
||||
OldName string `form:"old_name"`
|
||||
NewName string `form:"new_name"`
|
||||
OldPublisher string `form:"old_publisher"`
|
||||
NewPublisher string `form:"new_publisher"`
|
||||
OldAuthor string `form:"old_author"`
|
||||
NewAuthor string `form:"new_author"`
|
||||
OldPublicationTime int64 `form:"old_publication_time"`
|
||||
NewPublicationTime int64 `form:"new_publication_time"`
|
||||
OldPrice int64 `form:"old_price"`
|
||||
NewPrice int64 `form:"new_price"`
|
||||
OldBindingLayout string `form:"old_binding_layout"`
|
||||
NewBindingLayout string `form:"new_binding_layout"`
|
||||
OldPageCount int64 `form:"old_page_count"`
|
||||
NewPageCount int64 `form:"new_page_count"`
|
||||
OldWordCount int64 `form:"old_word_count"`
|
||||
NewWordCount int64 `form:"new_word_count"`
|
||||
OldLiveImage []string `form:"old_live_image[]"`
|
||||
NewLiveImage []string `form:"new_live_image[]"`
|
||||
OldIsSuit int8 `form:"old_is_suit"`
|
||||
NewIsSuit int8 `form:"new_is_suit"`
|
||||
}
|
||||
|
||||
type GetProductLogListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
Barcode string `form:"barcode"`
|
||||
Status *int8 `form:"status"`
|
||||
}
|
||||
|
||||
type AuditProductLogRequest struct {
|
||||
ID int64 `form:"id" binding:"required"`
|
||||
Status int8 `form:"status" binding:"required,oneof=1 2"`
|
||||
NewName string `form:"new_name"`
|
||||
NewPublisher string `form:"new_publisher"`
|
||||
NewAuthor string `form:"new_author"`
|
||||
NewPublicationTime int64 `form:"new_publication_time"`
|
||||
NewPrice int64 `form:"new_price"`
|
||||
NewBindingLayout string `form:"new_binding_layout"`
|
||||
NewPageCount int64 `form:"new_page_count"`
|
||||
NewWordCount int64 `form:"new_word_count"`
|
||||
NewLiveImage []string `form:"new_live_image[]"`
|
||||
NewIsSuit int8 `form:"new_is_suit"`
|
||||
}
|
||||
|
||||
type DeleteProductLogRequest struct {
|
||||
ID int64 `form:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// GetProductInventoryRequest 获取商品库存请求
|
||||
type GetProductInventoryRequest struct {
|
||||
UserID int64 `form:"user_id" binding:"required"` // 用户ID
|
||||
ProductID int64 `form:"product_id" binding:"required"` // 商品ID
|
||||
}
|
||||
|
||||
type GetShopProductDetailRequest struct {
|
||||
ShopID int64 `form:"shop_id" binding:"required"`
|
||||
Status int8 `form:"status"`
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
}
|
||||
|
||||
type GetProductDetailRequest struct {
|
||||
ID int64 `form:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// PushProductToShopRequest 上架到商铺请求
|
||||
type PushProductToShopRequest struct {
|
||||
UserID int64 `form:"user_id" json:"user_id" binding:"required"` // 用户ID(租户)
|
||||
WarehouseID int64 `form:"warehouse_id" json:"warehouse_id" binding:"required"` // 仓库ID
|
||||
LocationID int64 `form:"location_id" json:"location_id" binding:"required"` // 货位ID
|
||||
ISBN string `form:"isbn" json:"isbn" binding:"required"` // ISBN
|
||||
ProductName string `form:"product_name" json:"product_name"` // 图书名称
|
||||
Price int64 `form:"price" json:"price" binding:"required"` // 价格(分)
|
||||
Stock int64 `form:"stock" json:"stock" binding:"required"` // 存量
|
||||
Photos []string `form:"photos" json:"photos"` // 照片数组
|
||||
Appearance int64 `form:"appearance" json:"appearance" binding:"required"` // 品相
|
||||
}
|
||||
18
models/request/purchase.go
Normal file
18
models/request/purchase.go
Normal file
@ -0,0 +1,18 @@
|
||||
package request
|
||||
|
||||
// GetPurchaseOrderListRequest 获取采购订单列表请求
|
||||
type GetPurchaseOrderListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
PoNo string `form:"po_no"`
|
||||
Status int8 `form:"status"`
|
||||
SupplierID int64 `form:"supplier_id"`
|
||||
WarehouseID int64 `form:"warehouse_id"`
|
||||
StartDate int64 `form:"start_date"`
|
||||
EndDate int64 `form:"end_date"`
|
||||
}
|
||||
|
||||
// GetPurchaseOrderDetailRequest 获取采购订单详情请求
|
||||
type GetPurchaseOrderDetailRequest struct {
|
||||
ID int64 `form:"id" binding:"required"`
|
||||
}
|
||||
45
models/request/receiving.go
Normal file
45
models/request/receiving.go
Normal file
@ -0,0 +1,45 @@
|
||||
package request
|
||||
|
||||
// GetReceivingOrderListRequest 获取入库单列表请求
|
||||
type GetReceivingOrderListRequest struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
ReceivingNo string `form:"receiving_no"`
|
||||
Status int8 `form:"status"`
|
||||
SupplierID int64 `form:"supplier_id"`
|
||||
WarehouseID int64 `form:"warehouse_id"`
|
||||
PurchaseOrderID int64 `form:"purchase_order_id"`
|
||||
WaveTaskID int64 `form:"wave_task_id"`
|
||||
StartDate int64 `form:"start_date"`
|
||||
EndDate int64 `form:"end_date"`
|
||||
}
|
||||
|
||||
// GetReceivingOrderDetailRequest 获取入库单详情请求
|
||||
type GetReceivingOrderDetailRequest struct {
|
||||
ID int64 `form:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// ExternalProductSyncRequest 外部商品同步请求
|
||||
type ExternalProductSyncRequest struct {
|
||||
ShopIDs string `json:"shopIds"` // 店铺id,多个用逗号分隔
|
||||
Data []ExternalProductSyncData `json:"data"` // 商品数据数组
|
||||
}
|
||||
|
||||
// ExternalProductSyncData 外部商品同步数据项
|
||||
type ExternalProductSyncData struct {
|
||||
ProductID int64 `json:"productId"` // 商品id
|
||||
WarehouseID int64 `json:"warehouseId"` // 仓库id
|
||||
TrilateralID int64 `json:"trilateralId"` // 平台商品id
|
||||
Img string `json:"img"` // 图片地址
|
||||
FinishTime int64 `json:"finishTime"` // 创建时间
|
||||
TotalPrice int64 `json:"totalPrice"` // 价格 单位分
|
||||
ISBN string `json:"isbn"` // ISBN
|
||||
Title string `json:"title"` // 标题
|
||||
Quality int64 `json:"quality"` // 品相 八五品就是85 全新就是100
|
||||
GoodsCode string `json:"goodsCode"` // 商品编码
|
||||
ShopType int8 `json:"shopType"` // 店铺类型 1拼多多 2孔夫子 5闲鱼
|
||||
Stock int64 `json:"stock"` // 库存
|
||||
IsOnSale int8 `json:"isOnSale"` // 上下架状态 1上架 0下架
|
||||
SkuCode string `json:"skuCode"` // sku编号
|
||||
SkuID int64 `json:"skuId"` // skuId
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user