feat(production): add production management service with plan collaboration workflow

This commit is contained in:
Chenwei Jiang 2026-07-04 02:29:54 +00:00
parent e7e5bc2386
commit e9cdad0063
71 changed files with 7481 additions and 3 deletions

View File

@ -1,6 +1,7 @@
stages:
- build-1
- build-2
- build-3
variables:
HARBOR_REGISTRY: harbor-in-k3s.cheverjohn.me
@ -23,14 +24,18 @@ variables:
- |
if [ -n "$CI_COMMIT_TAG" ]; then
TAG="$CI_COMMIT_TAG"
else
elif [ "$CI_COMMIT_BRANCH" = "main" ]; then
TAG="main-${CI_COMMIT_SHORT_SHA}"
else
TAG="feat-${CI_COMMIT_SHORT_SHA}"
fi
- docker build --network host -t $HARBOR_REGISTRY/$HARBOR_PROJECT/$IMAGE_NAME:$TAG -f $DOCKERFILE_PATH .
- docker push $HARBOR_REGISTRY/$HARBOR_PROJECT/$IMAGE_NAME:$TAG
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_TAG =~ /^v/
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH =~ /^feat\//
build-muyu-gateway:
<<: *build-template
@ -52,3 +57,17 @@ build-muyu-inventory-rpc:
variables:
IMAGE_NAME: muyu-inventory-rpc
DOCKERFILE_PATH: deploy/Dockerfile.inventory
build-muyu-purchase-rpc:
<<: *build-template
stage: build-2
variables:
IMAGE_NAME: muyu-purchase-rpc
DOCKERFILE_PATH: deploy/Dockerfile.purchase
build-muyu-production-rpc:
<<: *build-template
stage: build-3
variables:
IMAGE_NAME: muyu-production-rpc
DOCKERFILE_PATH: deploy/Dockerfile.production

View File

@ -0,0 +1,21 @@
ARG BASE_REGISTRY=harbor-in-k3s.cheverjohn.me/library
FROM ${BASE_REGISTRY}/golang:1.24-alpine AS builder
ENV GOPROXY=https://goproxy.cn,direct
RUN sed -i "s|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g" /etc/apk/repositories
RUN apk add --no-cache git
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /server ./rpc/production/production.go
FROM ${BASE_REGISTRY}/alpine:3.20
RUN sed -i "s|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g" /etc/apk/repositories
RUN apk add --no-cache ca-certificates tzdata gettext
WORKDIR /app
COPY --from=builder /server ./production
COPY deploy/etc/production.yaml etc/production.yaml
COPY deploy/entrypoint.sh /entrypoint.sh
EXPOSE 9004
ENTRYPOINT ["/entrypoint.sh"]
CMD ["./production", "-f", "etc/production.yaml"]

View File

@ -1,7 +1,19 @@
FROM alpine:3.19
ARG BASE_REGISTRY=harbor-in-k3s.cheverjohn.me/library
FROM ${BASE_REGISTRY}/golang:1.24-alpine AS builder
ENV GOPROXY=https://goproxy.cn,direct
RUN sed -i "s|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g" /etc/apk/repositories
RUN apk add --no-cache git
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /server ./rpc/purchase/purchase.go
FROM ${BASE_REGISTRY}/alpine:3.20
RUN sed -i "s|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g" /etc/apk/repositories
RUN apk add --no-cache ca-certificates tzdata gettext
WORKDIR /app
COPY deploy/bin/purchase .
COPY --from=builder /server ./purchase
COPY deploy/etc/purchase.yaml etc/purchase.yaml
COPY deploy/entrypoint.sh /entrypoint.sh
EXPOSE 9003

View File

@ -126,6 +126,27 @@ services:
TZ: Asia/Shanghai
MYSQL_DSN: "root:${MYSQL_ROOT_PASSWORD}@tcp(mysql:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai"
production-rpc:
build:
context: ..
dockerfile: deploy/Dockerfile.production
container_name: muyu-production-rpc
restart: always
ports:
- "9004:9004"
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
etcd:
condition: service_healthy
volumes:
- ./etc/production.yaml:/etc/muyu/production.yaml
environment:
TZ: Asia/Shanghai
MYSQL_DSN: "root:${MYSQL_ROOT_PASSWORD}@tcp(mysql:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai"
gateway:
build:
context: ..
@ -141,6 +162,8 @@ services:
condition: service_started
purchase-rpc:
condition: service_started
production-rpc:
condition: service_started
postgres:
condition: service_healthy
environment:

View File

@ -34,6 +34,14 @@ PurchaseRpc:
NonBlock: true
Timeout: 60000
ProductionRpc:
Etcd:
Hosts:
- etcd:2379
Key: production.rpc
NonBlock: true
Timeout: 60000
Log:
ServiceName: gateway-api
Mode: console

View File

@ -0,0 +1,17 @@
Name: production.rpc
ListenOn: 0.0.0.0:9004
Etcd:
Hosts:
- etcd:2379
Key: production.rpc
DataSource: ${MYSQL_DSN}
Cache:
- Host: redis:6379
Log:
ServiceName: production-rpc
Mode: console
Level: info

View File

@ -0,0 +1,79 @@
-- End-to-End Validation Queries for Production Plan Collaboration
-- Run these after testing the full flow to verify data consistency
-- 1. Verify production plan exists and has correct structure
SELECT p.plan_id, p.plan_code, p.product_name, p.target_quantity,
p.completed_quantity, p.status,
ROUND(p.completed_quantity * 100.0 / NULLIF(p.target_quantity, 0), 1) as progress_pct
FROM pro_production_plan p
WHERE p.tenant_id = 'TEST_TENANT'
ORDER BY p.created_at DESC LIMIT 5;
-- 2. Verify yarn ratios sum to 100%
SELECT yr.plan_id,
COUNT(*) as ratio_count,
SUM(yr.ratio) as total_ratio,
CASE WHEN ABS(SUM(yr.ratio) - 1.0) < 0.01 THEN 'OK' ELSE 'MISMATCH' END as check_result
FROM pro_plan_yarn_ratio yr
GROUP BY yr.plan_id;
-- 3. Verify process steps — each plan should have exactly 5 steps
SELECT ps.plan_id,
COUNT(*) as step_count,
SUM(CASE WHEN ps.status = 2 THEN 1 ELSE 0 END) as completed_steps,
CASE WHEN COUNT(*) = 5 THEN 'OK' ELSE 'MISSING_STEPS' END as check_result
FROM pro_plan_process_step ps
GROUP BY ps.plan_id;
-- 4. Verify share link lifecycle
SELECT sl.short_code, sl.status, sl.click_count,
sl.expires_at, NOW() as current_time,
CASE WHEN sl.expires_at > NOW() THEN 'VALID' ELSE 'EXPIRED' END as expiry_check,
pf.factory_tenant_id
FROM pro_share_link sl
LEFT JOIN pro_plan_factory pf ON pf.plan_id = sl.plan_id
ORDER BY sl.created_at DESC LIMIT 5;
-- 5. CRITICAL: Verify data consistency — completed_quantity must equal SUM(inbound)
SELECT p.plan_id, p.plan_code,
p.completed_quantity as plan_completed,
COALESCE(SUM(ir.quantity), 0) as inbound_sum,
CASE
WHEN ABS(p.completed_quantity - COALESCE(SUM(ir.quantity), 0)) < 0.01 THEN 'CONSISTENT'
ELSE 'DRIFT_DETECTED'
END as consistency_check
FROM pro_production_plan p
LEFT JOIN pro_inbound_record ir ON ir.plan_id = p.plan_id
GROUP BY p.plan_id, p.plan_code, p.completed_quantity;
-- 6. Verify auto-completion: plans with completed_quantity >= target_quantity should be status=3
SELECT p.plan_id, p.plan_code,
p.target_quantity, p.completed_quantity, p.status,
CASE
WHEN p.completed_quantity >= p.target_quantity AND p.status != 3 THEN 'SHOULD_BE_COMPLETED'
WHEN p.completed_quantity < p.target_quantity AND p.status = 3 THEN 'PREMATURE_COMPLETION'
ELSE 'OK'
END as auto_complete_check
FROM pro_production_plan p
WHERE p.status IN (2, 3);
-- 7. Verify inventory logs match inbound records
SELECT ir.record_id, ir.quantity,
il.change_qty, il.ref_type, il.ref_id,
CASE
WHEN il.log_id IS NULL THEN 'MISSING_LOG'
WHEN ABS(ir.quantity - il.change_qty) < 0.01 THEN 'OK'
ELSE 'QUANTITY_MISMATCH'
END as log_check
FROM pro_inbound_record ir
LEFT JOIN inv_inventory_log il ON il.ref_id = ir.record_id AND il.ref_type = 'PRODUCTION';
-- 8. Summary report
SELECT
(SELECT COUNT(*) FROM pro_production_plan) as total_plans,
(SELECT COUNT(*) FROM pro_production_plan WHERE status = 3) as completed_plans,
(SELECT COUNT(*) FROM pro_share_link) as total_links,
(SELECT COUNT(*) FROM pro_share_link WHERE status = 2) as confirmed_links,
(SELECT COUNT(*) FROM pro_inbound_record) as total_inbound_records,
(SELECT COALESCE(SUM(quantity), 0) FROM pro_inbound_record) as total_inbound_meters,
(SELECT COUNT(*) FROM inv_inventory_log WHERE ref_type = 'PRODUCTION') as production_inv_logs;

View File

@ -0,0 +1,133 @@
-- Phase 3: Production Plan Collaboration + Batch Inbound
-- ==================== Production Plan ====================
CREATE TABLE IF NOT EXISTS `pro_production_plan` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`plan_id` VARCHAR(32) NOT NULL,
`tenant_id` VARCHAR(32) NOT NULL DEFAULT '',
`plan_code` VARCHAR(32) NOT NULL COMMENT 'PP-YYYYMMDD-XXXX',
`product_id` VARCHAR(32) NOT NULL,
`product_name` VARCHAR(100) NOT NULL DEFAULT '',
`color` VARCHAR(50) NOT NULL DEFAULT '',
`fabric_code` VARCHAR(50) NOT NULL DEFAULT '',
`color_code` VARCHAR(20) NOT NULL DEFAULT '',
`target_quantity` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT 'target meters',
`completed_quantity` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT 'completed meters = SUM(inbound)',
`yarn_usage_per_meter` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 'grams per meter',
`production_price` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 'yuan per meter',
`supplier_id` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'pur_supplier.supplier_id as textile factory',
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '0:draft 1:pending 2:producing 3:completed 4:cancelled',
`remark` TEXT,
`start_time` DATETIME DEFAULT NULL,
`creator` VARCHAR(32) NOT NULL DEFAULT '',
`operator` VARCHAR(32) NOT NULL DEFAULT '',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_plan_id` (`plan_id`),
UNIQUE KEY `uk_plan_code_tenant` (`plan_code`, `tenant_id`),
KEY `idx_tenant_status` (`tenant_id`, `status`),
KEY `idx_product` (`product_id`),
KEY `idx_supplier` (`supplier_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='生产计划';
-- ==================== Yarn Ratio ====================
CREATE TABLE IF NOT EXISTS `pro_plan_yarn_ratio` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`ratio_id` VARCHAR(32) NOT NULL,
`plan_id` VARCHAR(32) NOT NULL,
`yarn_name` VARCHAR(100) NOT NULL DEFAULT '',
`ratio` DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '0.00-1.00',
`amount_per_meter` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 'grams',
`total_amount` DECIMAL(14,2) NOT NULL DEFAULT 0.00 COMMENT 'grams',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_ratio_id` (`ratio_id`),
KEY `idx_plan` (`plan_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='纱线配比';
-- ==================== Process Step ====================
CREATE TABLE IF NOT EXISTS `pro_plan_process_step` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`step_id` VARCHAR(32) NOT NULL,
`plan_id` VARCHAR(32) NOT NULL,
`step_type` VARCHAR(30) NOT NULL COMMENT 'confirm|yarn_purchase|dyeing|machine_start|fabric_warehouse',
`step_order` TINYINT NOT NULL DEFAULT 0,
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '0:pending 1:in_progress 2:completed',
`operator_id` VARCHAR(32) NOT NULL DEFAULT '',
`completed_at` DATETIME DEFAULT NULL,
`remark` TEXT,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_step_id` (`step_id`),
UNIQUE KEY `uk_plan_step` (`plan_id`, `step_type`),
KEY `idx_plan` (`plan_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='生产流程节点';
-- ==================== Share Link ====================
CREATE TABLE IF NOT EXISTS `pro_share_link` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`link_id` VARCHAR(32) NOT NULL,
`tenant_id` VARCHAR(32) NOT NULL DEFAULT '',
`plan_id` VARCHAR(32) NOT NULL,
`short_code` VARCHAR(16) NOT NULL,
`token` VARCHAR(512) NOT NULL DEFAULT '',
`factory_type` VARCHAR(20) NOT NULL DEFAULT 'textile',
`hide_sensitive` TINYINT NOT NULL DEFAULT 0,
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '0:pending 1:clicked 2:confirmed 3:rejected 4:expired',
`click_count` INT NOT NULL DEFAULT 0,
`clicked_at` DATETIME DEFAULT NULL,
`responded_at` DATETIME DEFAULT NULL,
`reject_reason` VARCHAR(500) NOT NULL DEFAULT '',
`expires_at` DATETIME NOT NULL,
`created_by` VARCHAR(32) NOT NULL DEFAULT '',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_link_id` (`link_id`),
UNIQUE KEY `uk_short_code` (`short_code`),
KEY `idx_plan` (`plan_id`),
KEY `idx_tenant` (`tenant_id`),
KEY `idx_status_expires` (`status`, `expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='分享链接';
-- ==================== Plan Factory ====================
CREATE TABLE IF NOT EXISTS `pro_plan_factory` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`plan_id` VARCHAR(32) NOT NULL,
`factory_tenant_id` VARCHAR(32) NOT NULL,
`factory_type` VARCHAR(20) NOT NULL DEFAULT 'textile',
`share_link_id` VARCHAR(32) NOT NULL DEFAULT '',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_plan_factory` (`plan_id`, `factory_tenant_id`),
KEY `idx_factory` (`factory_tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='计划-工厂关联';
-- ==================== Inbound Record ====================
CREATE TABLE IF NOT EXISTS `pro_inbound_record` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`record_id` VARCHAR(32) NOT NULL,
`tenant_id` VARCHAR(32) NOT NULL DEFAULT '',
`plan_id` VARCHAR(32) NOT NULL,
`product_id` VARCHAR(32) NOT NULL DEFAULT '',
`batch_no` VARCHAR(50) NOT NULL DEFAULT '',
`quantity` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT 'meters',
`rolls` INT NOT NULL DEFAULT 0,
`price_per_meter` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`operator_id` VARCHAR(32) NOT NULL DEFAULT '',
`remark` TEXT,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_record_id` (`record_id`),
KEY `idx_plan` (`plan_id`),
KEY `idx_tenant` (`tenant_id`),
KEY `idx_product` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='按计划分批入库';

View File

@ -20,4 +20,5 @@ type Config struct {
SystemRpc zrpc.RpcClientConf
InventoryRpc zrpc.RpcClientConf
PurchaseRpc zrpc.RpcClientConf
ProductionRpc zrpc.RpcClientConf
}

View File

@ -0,0 +1,28 @@
package inbound
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"muyu-apiserver/gateway/internal/logic/production/inbound"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
)
func CreateInboundHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GatewayCreateInboundReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := inbound.NewCreateInboundLogic(r.Context(), svcCtx)
resp, err := l.CreateInbound(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@ -0,0 +1,28 @@
package inbound
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"muyu-apiserver/gateway/internal/logic/production/inbound"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
)
func ListInboundHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GatewayListInboundReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := inbound.NewListInboundLogic(r.Context(), svcCtx)
resp, err := l.ListInbound(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@ -0,0 +1,28 @@
package plan
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"muyu-apiserver/gateway/internal/logic/production/plan"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
)
func ConfirmPlanHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ConfirmPlanReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := plan.NewConfirmPlanLogic(r.Context(), svcCtx)
err := l.ConfirmPlan(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, types.IdResp{})
}
}
}

View File

@ -0,0 +1,28 @@
package plan
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"muyu-apiserver/gateway/internal/logic/production/plan"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
)
func CreateProductionPlanHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CreateProductionPlanReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := plan.NewCreateProductionPlanLogic(r.Context(), svcCtx)
resp, err := l.CreateProductionPlan(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@ -0,0 +1,24 @@
package plan
import (
"context"
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zeromicro/go-zero/rest/pathvar"
"muyu-apiserver/gateway/internal/logic/production/plan"
"muyu-apiserver/gateway/internal/svc"
)
func GetProductionPlanHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), "pathId", pathvar.Vars(r)["planId"])
l := plan.NewGetProductionPlanLogic(ctx, svcCtx)
resp, err := l.GetProductionPlan()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@ -0,0 +1,28 @@
package plan
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"muyu-apiserver/gateway/internal/logic/production/plan"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
)
func ListProductionPlanHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ListProductionPlanReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := plan.NewListProductionPlanLogic(r.Context(), svcCtx)
resp, err := l.ListProductionPlan(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@ -0,0 +1,28 @@
package process
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"muyu-apiserver/gateway/internal/logic/production/process"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
)
func CompleteStepHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GatewayCompleteStepReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := process.NewCompleteStepLogic(r.Context(), svcCtx)
err := l.CompleteStep(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, types.IdResp{})
}
}
}

View File

@ -0,0 +1,28 @@
package share
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"muyu-apiserver/gateway/internal/logic/production/share"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
)
func ClickShareLinkHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GatewayClickShareLinkReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := share.NewClickShareLinkLogic(r.Context(), svcCtx)
err := l.ClickShareLink(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, types.IdResp{})
}
}
}

View File

@ -0,0 +1,28 @@
package share
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"muyu-apiserver/gateway/internal/logic/production/share"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
)
func ConfirmShareLinkHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GatewayConfirmShareLinkReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := share.NewConfirmShareLinkLogic(r.Context(), svcCtx)
err := l.ConfirmShareLink(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, types.IdResp{})
}
}
}

View File

@ -0,0 +1,28 @@
package share
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"muyu-apiserver/gateway/internal/logic/production/share"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
)
func CreateShareLinkHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GatewayCreateShareLinkReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := share.NewCreateShareLinkLogic(r.Context(), svcCtx)
resp, err := l.CreateShareLink(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@ -0,0 +1,24 @@
package share
import (
"context"
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zeromicro/go-zero/rest/pathvar"
"muyu-apiserver/gateway/internal/logic/production/share"
"muyu-apiserver/gateway/internal/svc"
)
func GetShareLinkPlanViewHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), "pathId", pathvar.Vars(r)["shortCode"])
l := share.NewGetShareLinkPlanViewLogic(ctx, svcCtx)
resp, err := l.GetShareLinkPlanView()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@ -0,0 +1,28 @@
package share
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"muyu-apiserver/gateway/internal/logic/production/share"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
)
func RejectShareLinkHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GatewayRejectShareLinkReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := share.NewRejectShareLinkLogic(r.Context(), svcCtx)
err := l.RejectShareLink(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, types.IdResp{})
}
}
}

View File

@ -14,6 +14,10 @@ import (
inventorypanbolt "muyu-apiserver/gateway/internal/handler/inventory/panbolt"
inventoryproduct "muyu-apiserver/gateway/internal/handler/inventory/product"
inventorystock "muyu-apiserver/gateway/internal/handler/inventory/stock"
productioninbound "muyu-apiserver/gateway/internal/handler/production/inbound"
productionplan "muyu-apiserver/gateway/internal/handler/production/plan"
productionprocess "muyu-apiserver/gateway/internal/handler/production/process"
productionshare "muyu-apiserver/gateway/internal/handler/production/share"
purchaseorder "muyu-apiserver/gateway/internal/handler/purchase/order"
purchasepayment "muyu-apiserver/gateway/internal/handler/purchase/payment"
purchasereceipt "muyu-apiserver/gateway/internal/handler/purchase/receipt"
@ -634,4 +638,120 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
rest.WithPrefix("/api/v1/inventory"),
)
// ─── Production Plan (JWT + Authority) ──────────────────────────────────
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AuthorityMiddleware},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/plan",
Handler: productionplan.CreateProductionPlanHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/plan",
Handler: productionplan.ListProductionPlanHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/plan/:planId",
Handler: productionplan.GetProductionPlanHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/plan/confirm",
Handler: productionplan.ConfirmPlanHandler(serverCtx),
},
}...,
),
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
rest.WithPrefix("/api/v1/production"),
)
// ─── Production Share (JWT + Authority) ─────────────────────────────────
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AuthorityMiddleware},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/share",
Handler: productionshare.CreateShareLinkHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/share/confirm",
Handler: productionshare.ConfirmShareLinkHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/share/reject",
Handler: productionshare.RejectShareLinkHandler(serverCtx),
},
}...,
),
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
rest.WithPrefix("/api/v1/production"),
)
// ─── Production Process (JWT + Authority) ───────────────────────────────
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AuthorityMiddleware},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/process/step",
Handler: productionprocess.CompleteStepHandler(serverCtx),
},
}...,
),
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
rest.WithPrefix("/api/v1/production"),
)
// ─── Production Inbound (JWT + Authority) ───────────────────────────────
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AuthorityMiddleware},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/inbound",
Handler: productioninbound.CreateInboundHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/inbound",
Handler: productioninbound.ListInboundHandler(serverCtx),
},
}...,
),
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
rest.WithPrefix("/api/v1/production"),
)
// ─── Production Public Share (NO JWT) ───────────────────────────────────
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodGet,
Path: "/share/:shortCode",
Handler: productionshare.GetShareLinkPlanViewHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/share/click",
Handler: productionshare.ClickShareLinkHandler(serverCtx),
},
},
rest.WithPrefix("/api/v1/public/production"),
)
}

View File

@ -0,0 +1,43 @@
package inbound
import (
"context"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/pkg/ctxdata"
"muyu-apiserver/rpc/production/productionservice"
"github.com/zeromicro/go-zero/core/logx"
)
type CreateInboundLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCreateInboundLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateInboundLogic {
return &CreateInboundLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateInboundLogic) CreateInbound(req *types.GatewayCreateInboundReq) (*types.IdResp, error) {
resp, err := l.svcCtx.ProductionRpc.CreateInbound(l.ctx, &productionservice.CreateInboundReq{
PlanId: req.PlanId,
ProductId: req.ProductId,
BatchNo: req.BatchNo,
Quantity: req.Quantity,
Rolls: req.Rolls,
PricePerMeter: req.PricePerMeter,
OperatorId: ctxdata.GetUserId(l.ctx),
Remark: req.Remark,
})
if err != nil {
return nil, err
}
return &types.IdResp{Id: resp.Id}, nil
}

View File

@ -0,0 +1,54 @@
package inbound
import (
"context"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/rpc/production/productionservice"
"github.com/zeromicro/go-zero/core/logx"
)
type ListInboundLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewListInboundLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListInboundLogic {
return &ListInboundLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ListInboundLogic) ListInbound(req *types.GatewayListInboundReq) (*types.ListInboundResp, error) {
rpcResp, err := l.svcCtx.ProductionRpc.ListInbound(l.ctx, &productionservice.ListInboundReq{
PlanId: req.PlanId,
Page: req.Page,
PageSize: req.PageSize,
})
if err != nil {
return nil, err
}
list := make([]types.InboundRecordInfo, 0, len(rpcResp.List))
for _, ir := range rpcResp.List {
list = append(list, types.InboundRecordInfo{
RecordId: ir.RecordId,
TenantId: ir.TenantId,
PlanId: ir.PlanId,
ProductId: ir.ProductId,
BatchNo: ir.BatchNo,
Quantity: ir.Quantity,
Rolls: ir.Rolls,
PricePerMeter: ir.PricePerMeter,
OperatorId: ir.OperatorId,
Remark: ir.Remark,
CreatedAt: ir.CreatedAt,
})
}
return &types.ListInboundResp{Total: rpcResp.Total, List: list}, nil
}

View File

@ -0,0 +1,34 @@
package plan
import (
"context"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/pkg/ctxdata"
"muyu-apiserver/rpc/production/productionservice"
"github.com/zeromicro/go-zero/core/logx"
)
type ConfirmPlanLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewConfirmPlanLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ConfirmPlanLogic {
return &ConfirmPlanLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ConfirmPlanLogic) ConfirmPlan(req *types.ConfirmPlanReq) error {
_, err := l.svcCtx.ProductionRpc.ConfirmPlan(l.ctx, &productionservice.ConfirmPlanReq{
PlanId: req.PlanId,
Operator: ctxdata.GetUserId(l.ctx),
})
return err
}

View File

@ -0,0 +1,68 @@
package plan
import (
"context"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/pkg/ctxdata"
"muyu-apiserver/rpc/production/productionservice"
"github.com/zeromicro/go-zero/core/logx"
)
type CreateProductionPlanLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCreateProductionPlanLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateProductionPlanLogic {
return &CreateProductionPlanLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateProductionPlanLogic) CreateProductionPlan(req *types.CreateProductionPlanReq) (*types.IdResp, error) {
yarnRatios := make([]*productionservice.YarnRatioReq, 0, len(req.YarnRatios))
for _, yr := range req.YarnRatios {
yarnRatios = append(yarnRatios, &productionservice.YarnRatioReq{
YarnName: yr.YarnName,
Ratio: yr.Ratio,
AmountPerMeter: yr.AmountPerMeter,
TotalAmount: yr.TotalAmount,
})
}
processSteps := make([]*productionservice.ProcessStepReq, 0, len(req.ProcessSteps))
for _, ps := range req.ProcessSteps {
processSteps = append(processSteps, &productionservice.ProcessStepReq{
StepType: ps.StepType,
StepOrder: ps.StepOrder,
Remark: ps.Remark,
})
}
resp, err := l.svcCtx.ProductionRpc.CreateProductionPlan(l.ctx, &productionservice.CreateProductionPlanReq{
ProductId: req.ProductId,
ProductName: req.ProductName,
Color: req.Color,
FabricCode: req.FabricCode,
ColorCode: req.ColorCode,
TargetQuantity: req.TargetQuantity,
YarnUsagePerMeter: req.YarnUsagePerMeter,
ProductionPrice: req.ProductionPrice,
SupplierId: req.SupplierId,
Remark: req.Remark,
StartTime: req.StartTime,
Creator: ctxdata.GetUserId(l.ctx),
YarnRatios: yarnRatios,
ProcessSteps: processSteps,
})
if err != nil {
return nil, err
}
return &types.IdResp{Id: resp.Id}, nil
}

View File

@ -0,0 +1,37 @@
package plan
import (
"context"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/pkg/ctxdata"
"muyu-apiserver/rpc/production/productionservice"
"github.com/zeromicro/go-zero/core/logx"
)
type GetProductionPlanLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetProductionPlanLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProductionPlanLogic {
return &GetProductionPlanLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetProductionPlanLogic) GetProductionPlan() (*types.ProductionPlanInfo, error) {
planId := ctxdata.GetPathId(l.ctx)
resp, err := l.svcCtx.ProductionRpc.GetProductionPlan(l.ctx, &productionservice.GetProductionPlanReq{
PlanId: planId,
})
if err != nil {
return nil, err
}
return productionPlanInfoFromPB(resp), nil
}

View File

@ -0,0 +1,122 @@
package plan
import (
"context"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/rpc/production/productionservice"
"github.com/zeromicro/go-zero/core/logx"
)
type ListProductionPlanLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewListProductionPlanLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListProductionPlanLogic {
return &ListProductionPlanLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ListProductionPlanLogic) ListProductionPlan(req *types.ListProductionPlanReq) (*types.ListProductionPlanResp, error) {
rpcResp, err := l.svcCtx.ProductionRpc.ListProductionPlan(l.ctx, &productionservice.ListProductionPlanReq{
Page: req.Page,
PageSize: req.PageSize,
Status: req.Status,
ProductName: req.ProductName,
PlanCode: req.PlanCode,
})
if err != nil {
return nil, err
}
list := make([]types.ProductionPlanInfo, 0, len(rpcResp.List))
for _, p := range rpcResp.List {
list = append(list, *productionPlanInfoFromPB(p))
}
return &types.ListProductionPlanResp{Total: rpcResp.Total, List: list}, nil
}
func productionPlanInfoFromPB(p *productionservice.ProductionPlanInfo) *types.ProductionPlanInfo {
if p == nil {
return &types.ProductionPlanInfo{}
}
yarnRatios := make([]types.YarnRatioInfo, 0, len(p.YarnRatios))
for _, yr := range p.YarnRatios {
yarnRatios = append(yarnRatios, types.YarnRatioInfo{
RatioId: yr.RatioId,
PlanId: yr.PlanId,
YarnName: yr.YarnName,
Ratio: yr.Ratio,
AmountPerMeter: yr.AmountPerMeter,
TotalAmount: yr.TotalAmount,
CreatedAt: yr.CreatedAt,
})
}
processSteps := make([]types.ProcessStepInfo, 0, len(p.ProcessSteps))
for _, ps := range p.ProcessSteps {
processSteps = append(processSteps, types.ProcessStepInfo{
StepId: ps.StepId,
PlanId: ps.PlanId,
StepType: ps.StepType,
StepOrder: ps.StepOrder,
Status: ps.Status,
OperatorId: ps.OperatorId,
CompletedAt: ps.CompletedAt,
Remark: ps.Remark,
CreatedAt: ps.CreatedAt,
UpdatedAt: ps.UpdatedAt,
})
}
inboundRecords := make([]types.InboundRecordInfo, 0, len(p.InboundRecords))
for _, ir := range p.InboundRecords {
inboundRecords = append(inboundRecords, types.InboundRecordInfo{
RecordId: ir.RecordId,
TenantId: ir.TenantId,
PlanId: ir.PlanId,
ProductId: ir.ProductId,
BatchNo: ir.BatchNo,
Quantity: ir.Quantity,
Rolls: ir.Rolls,
PricePerMeter: ir.PricePerMeter,
OperatorId: ir.OperatorId,
Remark: ir.Remark,
CreatedAt: ir.CreatedAt,
})
}
return &types.ProductionPlanInfo{
PlanId: p.PlanId,
TenantId: p.TenantId,
PlanCode: p.PlanCode,
ProductId: p.ProductId,
ProductName: p.ProductName,
Color: p.Color,
FabricCode: p.FabricCode,
ColorCode: p.ColorCode,
TargetQuantity: p.TargetQuantity,
CompletedQuantity: p.CompletedQuantity,
YarnUsagePerMeter: p.YarnUsagePerMeter,
ProductionPrice: p.ProductionPrice,
SupplierId: p.SupplierId,
Status: p.Status,
Remark: p.Remark,
StartTime: p.StartTime,
Creator: p.Creator,
Operator: p.Operator,
CreatedAt: p.CreatedAt,
UpdatedAt: p.UpdatedAt,
YarnRatios: yarnRatios,
ProcessSteps: processSteps,
InboundRecords: inboundRecords,
}
}

View File

@ -0,0 +1,35 @@
package process
import (
"context"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/pkg/ctxdata"
"muyu-apiserver/rpc/production/productionservice"
"github.com/zeromicro/go-zero/core/logx"
)
type CompleteStepLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCompleteStepLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CompleteStepLogic {
return &CompleteStepLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CompleteStepLogic) CompleteStep(req *types.GatewayCompleteStepReq) error {
_, err := l.svcCtx.ProductionRpc.CompleteStep(l.ctx, &productionservice.CompleteStepReq{
PlanId: req.PlanId,
StepType: req.StepType,
OperatorId: ctxdata.GetUserId(l.ctx),
})
return err
}

View File

@ -0,0 +1,32 @@
package share
import (
"context"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/rpc/production/productionservice"
"github.com/zeromicro/go-zero/core/logx"
)
type ClickShareLinkLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewClickShareLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ClickShareLinkLogic {
return &ClickShareLinkLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ClickShareLinkLogic) ClickShareLink(req *types.GatewayClickShareLinkReq) error {
_, err := l.svcCtx.ProductionRpc.ClickShareLink(l.ctx, &productionservice.ClickShareLinkReq{
ShortCode: req.ShortCode,
})
return err
}

View File

@ -0,0 +1,35 @@
package share
import (
"context"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/pkg/ctxdata"
"muyu-apiserver/rpc/production/productionservice"
"github.com/zeromicro/go-zero/core/logx"
)
type ConfirmShareLinkLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewConfirmShareLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ConfirmShareLinkLogic {
return &ConfirmShareLinkLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ConfirmShareLinkLogic) ConfirmShareLink(req *types.GatewayConfirmShareLinkReq) error {
_, err := l.svcCtx.ProductionRpc.ConfirmShareLink(l.ctx, &productionservice.ConfirmShareLinkReq{
ShortCode: req.ShortCode,
Token: req.Token,
FactoryTenantId: ctxdata.GetTenantId(l.ctx),
})
return err
}

View File

@ -0,0 +1,108 @@
package share
import (
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/rpc/production/productionservice"
)
func productionPlanInfoFromPB(p *productionservice.ProductionPlanInfo) *types.ProductionPlanInfo {
if p == nil {
return &types.ProductionPlanInfo{}
}
yarnRatios := make([]types.YarnRatioInfo, 0, len(p.YarnRatios))
for _, yr := range p.YarnRatios {
yarnRatios = append(yarnRatios, types.YarnRatioInfo{
RatioId: yr.RatioId,
PlanId: yr.PlanId,
YarnName: yr.YarnName,
Ratio: yr.Ratio,
AmountPerMeter: yr.AmountPerMeter,
TotalAmount: yr.TotalAmount,
CreatedAt: yr.CreatedAt,
})
}
processSteps := make([]types.ProcessStepInfo, 0, len(p.ProcessSteps))
for _, ps := range p.ProcessSteps {
processSteps = append(processSteps, types.ProcessStepInfo{
StepId: ps.StepId,
PlanId: ps.PlanId,
StepType: ps.StepType,
StepOrder: ps.StepOrder,
Status: ps.Status,
OperatorId: ps.OperatorId,
CompletedAt: ps.CompletedAt,
Remark: ps.Remark,
CreatedAt: ps.CreatedAt,
UpdatedAt: ps.UpdatedAt,
})
}
inboundRecords := make([]types.InboundRecordInfo, 0, len(p.InboundRecords))
for _, ir := range p.InboundRecords {
inboundRecords = append(inboundRecords, types.InboundRecordInfo{
RecordId: ir.RecordId,
TenantId: ir.TenantId,
PlanId: ir.PlanId,
ProductId: ir.ProductId,
BatchNo: ir.BatchNo,
Quantity: ir.Quantity,
Rolls: ir.Rolls,
PricePerMeter: ir.PricePerMeter,
OperatorId: ir.OperatorId,
Remark: ir.Remark,
CreatedAt: ir.CreatedAt,
})
}
return &types.ProductionPlanInfo{
PlanId: p.PlanId,
TenantId: p.TenantId,
PlanCode: p.PlanCode,
ProductId: p.ProductId,
ProductName: p.ProductName,
Color: p.Color,
FabricCode: p.FabricCode,
ColorCode: p.ColorCode,
TargetQuantity: p.TargetQuantity,
CompletedQuantity: p.CompletedQuantity,
YarnUsagePerMeter: p.YarnUsagePerMeter,
ProductionPrice: p.ProductionPrice,
SupplierId: p.SupplierId,
Status: p.Status,
Remark: p.Remark,
StartTime: p.StartTime,
Creator: p.Creator,
Operator: p.Operator,
CreatedAt: p.CreatedAt,
UpdatedAt: p.UpdatedAt,
YarnRatios: yarnRatios,
ProcessSteps: processSteps,
InboundRecords: inboundRecords,
}
}
func shareLinkInfoFromPB(s *productionservice.ShareLinkInfo) *types.ShareLinkInfoResp {
if s == nil {
return &types.ShareLinkInfoResp{}
}
return &types.ShareLinkInfoResp{
LinkId: s.LinkId,
TenantId: s.TenantId,
PlanId: s.PlanId,
ShortCode: s.ShortCode,
Token: s.Token,
FactoryType: s.FactoryType,
HideSensitive: s.HideSensitive,
Status: s.Status,
ClickCount: s.ClickCount,
ClickedAt: s.ClickedAt,
RespondedAt: s.RespondedAt,
RejectReason: s.RejectReason,
ExpiresAt: s.ExpiresAt,
CreatedBy: s.CreatedBy,
CreatedAt: s.CreatedAt,
UpdatedAt: s.UpdatedAt,
}
}

View File

@ -0,0 +1,40 @@
package share
import (
"context"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/pkg/ctxdata"
"muyu-apiserver/rpc/production/productionservice"
"github.com/zeromicro/go-zero/core/logx"
)
type CreateShareLinkLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCreateShareLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateShareLinkLogic {
return &CreateShareLinkLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateShareLinkLogic) CreateShareLink(req *types.GatewayCreateShareLinkReq) (*types.ShareLinkInfoResp, error) {
resp, err := l.svcCtx.ProductionRpc.CreateShareLink(l.ctx, &productionservice.CreateShareLinkReq{
PlanId: req.PlanId,
FactoryType: req.FactoryType,
HideSensitive: req.HideSensitive,
ExpiresHours: req.ExpiresHours,
CreatedBy: ctxdata.GetUserId(l.ctx),
})
if err != nil {
return nil, err
}
return shareLinkInfoFromPB(resp), nil
}

View File

@ -0,0 +1,46 @@
package share
import (
"context"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/pkg/ctxdata"
"muyu-apiserver/rpc/production/productionservice"
"github.com/zeromicro/go-zero/core/logx"
)
type GetShareLinkPlanViewLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetShareLinkPlanViewLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetShareLinkPlanViewLogic {
return &GetShareLinkPlanViewLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetShareLinkPlanViewLogic) GetShareLinkPlanView() (*types.ShareLinkPlanViewResp, error) {
shortCode := ctxdata.GetPathId(l.ctx)
resp, err := l.svcCtx.ProductionRpc.GetShareLinkPlanView(l.ctx, &productionservice.GetShareLinkReq{
ShortCode: shortCode,
})
if err != nil {
return nil, err
}
result := &types.ShareLinkPlanViewResp{}
if resp.Plan != nil {
plan := productionPlanInfoFromPB(resp.Plan)
result.Plan = plan
}
if resp.Link != nil {
result.Link = shareLinkInfoFromPB(resp.Link)
}
return result, nil
}

View File

@ -0,0 +1,34 @@
package share
import (
"context"
"muyu-apiserver/gateway/internal/svc"
"muyu-apiserver/gateway/internal/types"
"muyu-apiserver/rpc/production/productionservice"
"github.com/zeromicro/go-zero/core/logx"
)
type RejectShareLinkLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewRejectShareLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RejectShareLinkLogic {
return &RejectShareLinkLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *RejectShareLinkLogic) RejectShareLink(req *types.GatewayRejectShareLinkReq) error {
_, err := l.svcCtx.ProductionRpc.RejectShareLink(l.ctx, &productionservice.RejectShareLinkReq{
ShortCode: req.ShortCode,
Token: req.Token,
RejectReason: req.RejectReason,
})
return err
}

View File

@ -13,6 +13,7 @@ import (
pkgcasbin "muyu-apiserver/pkg/casbin"
"muyu-apiserver/pkg/tenantctx"
"muyu-apiserver/rpc/inventory/inventoryservice"
"muyu-apiserver/rpc/production/productionservice"
"muyu-apiserver/rpc/purchase/purchaseservice"
"muyu-apiserver/rpc/system/systemservice"
)
@ -25,6 +26,7 @@ type ServiceContext struct {
SystemRpc systemservice.SystemService
InventoryRpc inventoryservice.InventoryService
PurchaseRpc purchaseservice.PurchaseService
ProductionRpc productionservice.ProductionService
}
func NewServiceContext(c config.Config) *ServiceContext {
@ -62,5 +64,6 @@ func NewServiceContext(c config.Config) *ServiceContext {
SystemRpc: systemservice.NewSystemService(zrpc.MustNewClient(c.SystemRpc, zrpc.WithUnaryClientInterceptor(tenantctx.UnaryClientInterceptor()))),
InventoryRpc: inventoryservice.NewInventoryService(zrpc.MustNewClient(c.InventoryRpc, zrpc.WithUnaryClientInterceptor(tenantctx.UnaryClientInterceptor()))),
PurchaseRpc: purchaseservice.NewPurchaseService(zrpc.MustNewClient(c.PurchaseRpc, zrpc.WithUnaryClientInterceptor(tenantctx.UnaryClientInterceptor()))),
ProductionRpc: productionservice.NewProductionService(zrpc.MustNewClient(c.ProductionRpc, zrpc.WithUnaryClientInterceptor(tenantctx.UnaryClientInterceptor()))),
}
}

View File

@ -859,3 +859,192 @@ type PurchaseStatsByProductItem struct {
type PurchaseStatsByProductResp struct {
List []PurchaseStatsByProductItem `json:"list"`
}
// ─── Production Plan ────────────────────────────────────────────────────────
type GatewayYarnRatioReq struct {
YarnName string `json:"yarnName"`
Ratio string `json:"ratio"`
AmountPerMeter string `json:"amountPerMeter,optional"`
TotalAmount string `json:"totalAmount,optional"`
}
type GatewayProcessStepReq struct {
StepType string `json:"stepType"`
StepOrder int64 `json:"stepOrder"`
Remark string `json:"remark,optional"`
}
type CreateProductionPlanReq struct {
ProductId string `json:"productId"`
ProductName string `json:"productName"`
Color string `json:"color,optional"`
FabricCode string `json:"fabricCode,optional"`
ColorCode string `json:"colorCode,optional"`
TargetQuantity string `json:"targetQuantity"`
YarnUsagePerMeter string `json:"yarnUsagePerMeter,optional"`
ProductionPrice string `json:"productionPrice,optional"`
SupplierId string `json:"supplierId,optional"`
Remark string `json:"remark,optional"`
StartTime string `json:"startTime,optional"`
YarnRatios []GatewayYarnRatioReq `json:"yarnRatios,optional"`
ProcessSteps []GatewayProcessStepReq `json:"processSteps,optional"`
}
type ListProductionPlanReq struct {
Page int64 `form:"page,default=1"`
PageSize int64 `form:"pageSize,default=20"`
Status int64 `form:"status,optional,default=-1"`
ProductName string `form:"productName,optional"`
PlanCode string `form:"planCode,optional"`
}
type YarnRatioInfo struct {
RatioId string `json:"ratioId"`
PlanId string `json:"planId"`
YarnName string `json:"yarnName"`
Ratio string `json:"ratio"`
AmountPerMeter string `json:"amountPerMeter"`
TotalAmount string `json:"totalAmount"`
CreatedAt string `json:"createdAt"`
}
type ProcessStepInfo struct {
StepId string `json:"stepId"`
PlanId string `json:"planId"`
StepType string `json:"stepType"`
StepOrder int64 `json:"stepOrder"`
Status int64 `json:"status"`
OperatorId string `json:"operatorId"`
CompletedAt string `json:"completedAt"`
Remark string `json:"remark"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type InboundRecordInfo struct {
RecordId string `json:"recordId"`
TenantId string `json:"tenantId"`
PlanId string `json:"planId"`
ProductId string `json:"productId"`
BatchNo string `json:"batchNo"`
Quantity string `json:"quantity"`
Rolls int64 `json:"rolls"`
PricePerMeter string `json:"pricePerMeter"`
OperatorId string `json:"operatorId"`
Remark string `json:"remark"`
CreatedAt string `json:"createdAt"`
}
type ProductionPlanInfo struct {
PlanId string `json:"planId"`
TenantId string `json:"tenantId"`
PlanCode string `json:"planCode"`
ProductId string `json:"productId"`
ProductName string `json:"productName"`
Color string `json:"color"`
FabricCode string `json:"fabricCode"`
ColorCode string `json:"colorCode"`
TargetQuantity string `json:"targetQuantity"`
CompletedQuantity string `json:"completedQuantity"`
YarnUsagePerMeter string `json:"yarnUsagePerMeter"`
ProductionPrice string `json:"productionPrice"`
SupplierId string `json:"supplierId"`
Status int64 `json:"status"`
Remark string `json:"remark"`
StartTime string `json:"startTime"`
Creator string `json:"creator"`
Operator string `json:"operator"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
YarnRatios []YarnRatioInfo `json:"yarnRatios"`
ProcessSteps []ProcessStepInfo `json:"processSteps"`
InboundRecords []InboundRecordInfo `json:"inboundRecords"`
}
type ListProductionPlanResp struct {
Total int64 `json:"total"`
List []ProductionPlanInfo `json:"list"`
}
type ConfirmPlanReq struct {
PlanId string `json:"planId"`
}
// ─── Share Link ─────────────────────────────────────────────────────────────
type GatewayCreateShareLinkReq struct {
PlanId string `json:"planId"`
FactoryType string `json:"factoryType,optional"`
HideSensitive int64 `json:"hideSensitive,optional"`
ExpiresHours int64 `json:"expiresHours,optional"`
}
type ShareLinkInfoResp struct {
LinkId string `json:"linkId"`
TenantId string `json:"tenantId"`
PlanId string `json:"planId"`
ShortCode string `json:"shortCode"`
Token string `json:"token"`
FactoryType string `json:"factoryType"`
HideSensitive int64 `json:"hideSensitive"`
Status int64 `json:"status"`
ClickCount int64 `json:"clickCount"`
ClickedAt string `json:"clickedAt"`
RespondedAt string `json:"respondedAt"`
RejectReason string `json:"rejectReason"`
ExpiresAt string `json:"expiresAt"`
CreatedBy string `json:"createdBy"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type ShareLinkPlanViewResp struct {
Plan *ProductionPlanInfo `json:"plan"`
Link *ShareLinkInfoResp `json:"link"`
}
type GatewayConfirmShareLinkReq struct {
ShortCode string `json:"shortCode"`
Token string `json:"token,optional"`
}
type GatewayRejectShareLinkReq struct {
ShortCode string `json:"shortCode"`
Token string `json:"token,optional"`
RejectReason string `json:"rejectReason,optional"`
}
type GatewayClickShareLinkReq struct {
ShortCode string `json:"shortCode"`
}
// ─── Process Step ───────────────────────────────────────────────────────────
type GatewayCompleteStepReq struct {
PlanId string `json:"planId"`
StepType string `json:"stepType"`
}
// ─── Inbound ────────────────────────────────────────────────────────────────
type GatewayCreateInboundReq struct {
PlanId string `json:"planId"`
ProductId string `json:"productId"`
BatchNo string `json:"batchNo,optional"`
Quantity string `json:"quantity"`
Rolls int64 `json:"rolls,optional"`
PricePerMeter string `json:"pricePerMeter,optional"`
Remark string `json:"remark,optional"`
}
type GatewayListInboundReq struct {
PlanId string `form:"planId,optional"`
Page int64 `form:"page,default=1"`
PageSize int64 `form:"pageSize,default=20"`
}
type ListInboundResp struct {
Total int64 `json:"total"`
List []InboundRecordInfo `json:"list"`
}

View File

@ -0,0 +1,56 @@
package model
import (
"context"
"fmt"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var _ ProInboundRecordModel = (*customProInboundRecordModel)(nil)
type (
ProInboundRecordModel interface {
proInboundRecordModel
FindByPlanId(ctx context.Context, planId string, page, pageSize int64) ([]*ProInboundRecord, int64, error)
SumQuantityByPlan(ctx context.Context, planId string) (float64, int64, error)
}
customProInboundRecordModel struct {
*defaultProInboundRecordModel
}
)
func NewProInboundRecordModel(conn sqlx.SqlConn) ProInboundRecordModel {
return &customProInboundRecordModel{
defaultProInboundRecordModel: newProInboundRecordModel(conn),
}
}
func (m *customProInboundRecordModel) FindByPlanId(ctx context.Context, planId string, page, pageSize int64) ([]*ProInboundRecord, int64, error) {
var total int64
countQuery := fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE plan_id = ?", m.table)
if err := m.conn.QueryRowCtx(ctx, &total, countQuery, planId); err != nil {
return nil, 0, err
}
var list []*ProInboundRecord
query := fmt.Sprintf("SELECT %s FROM %s WHERE plan_id = ? ORDER BY created_at DESC LIMIT ? OFFSET ?",
proInboundRecordRows, m.table)
if err := m.conn.QueryRowsCtx(ctx, &list, query, planId, pageSize, (page-1)*pageSize); err != nil {
return nil, 0, err
}
return list, total, nil
}
func (m *customProInboundRecordModel) SumQuantityByPlan(ctx context.Context, planId string) (float64, int64, error) {
var result struct {
TotalQty float64 `db:"total_qty"`
TotalRolls int64 `db:"total_rolls"`
}
query := fmt.Sprintf("SELECT COALESCE(SUM(quantity),0) as total_qty, COALESCE(SUM(rolls),0) as total_rolls FROM %s WHERE plan_id = ?", m.table)
if err := m.conn.QueryRowCtx(ctx, &result, query, planId); err != nil {
return 0, 0, err
}
return result.TotalQty, result.TotalRolls, nil
}

View File

@ -0,0 +1,57 @@
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"github.com/zeromicro/go-zero/core/stores/builder"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/stringx"
)
var (
proInboundRecordFieldNames = builder.RawFieldNames(&ProInboundRecord{})
proInboundRecordRows = strings.Join(proInboundRecordFieldNames, ",")
proInboundRecordRowsExpectAutoSet = strings.Join(stringx.Remove(proInboundRecordFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",")
)
type (
proInboundRecordModel interface {
Insert(ctx context.Context, data *ProInboundRecord) (sql.Result, error)
}
defaultProInboundRecordModel struct {
conn sqlx.SqlConn
table string
}
ProInboundRecord struct {
Id int64 `db:"id"`
RecordId string `db:"record_id"`
TenantId string `db:"tenant_id"`
PlanId string `db:"plan_id"`
ProductId string `db:"product_id"`
BatchNo string `db:"batch_no"`
Quantity float64 `db:"quantity"`
Rolls int64 `db:"rolls"`
PricePerMeter float64 `db:"price_per_meter"`
OperatorId string `db:"operator_id"`
Remark sql.NullString `db:"remark"`
CreatedAt time.Time `db:"created_at"`
}
)
func newProInboundRecordModel(conn sqlx.SqlConn) *defaultProInboundRecordModel {
return &defaultProInboundRecordModel{conn: conn, table: "`pro_inbound_record`"}
}
func (m *defaultProInboundRecordModel) Insert(ctx context.Context, data *ProInboundRecord) (sql.Result, error) {
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
m.table, proInboundRecordRowsExpectAutoSet)
return m.conn.ExecCtx(ctx, query, data.RecordId, data.TenantId, data.PlanId,
data.ProductId, data.BatchNo, data.Quantity, data.Rolls,
data.PricePerMeter, data.OperatorId, data.Remark)
}

View File

@ -0,0 +1,36 @@
package model
import (
"context"
"fmt"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var _ ProPlanFactoryModel = (*customProPlanFactoryModel)(nil)
type (
ProPlanFactoryModel interface {
proPlanFactoryModel
FindByPlanId(ctx context.Context, planId string) ([]*ProPlanFactory, error)
}
customProPlanFactoryModel struct {
*defaultProPlanFactoryModel
}
)
func NewProPlanFactoryModel(conn sqlx.SqlConn) ProPlanFactoryModel {
return &customProPlanFactoryModel{
defaultProPlanFactoryModel: newProPlanFactoryModel(conn),
}
}
func (m *customProPlanFactoryModel) FindByPlanId(ctx context.Context, planId string) ([]*ProPlanFactory, error) {
var list []*ProPlanFactory
query := fmt.Sprintf("SELECT %s FROM %s WHERE plan_id = ?", proPlanFactoryRows, m.table)
if err := m.conn.QueryRowsCtx(ctx, &list, query, planId); err != nil {
return nil, err
}
return list, nil
}

View File

@ -0,0 +1,49 @@
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"github.com/zeromicro/go-zero/core/stores/builder"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/stringx"
)
var (
proPlanFactoryFieldNames = builder.RawFieldNames(&ProPlanFactory{})
proPlanFactoryRows = strings.Join(proPlanFactoryFieldNames, ",")
proPlanFactoryRowsExpectAutoSet = strings.Join(stringx.Remove(proPlanFactoryFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",")
)
type (
proPlanFactoryModel interface {
Insert(ctx context.Context, data *ProPlanFactory) (sql.Result, error)
}
defaultProPlanFactoryModel struct {
conn sqlx.SqlConn
table string
}
ProPlanFactory struct {
Id int64 `db:"id"`
PlanId string `db:"plan_id"`
FactoryTenantId string `db:"factory_tenant_id"`
FactoryType string `db:"factory_type"`
ShareLinkId string `db:"share_link_id"`
CreatedAt time.Time `db:"created_at"`
}
)
func newProPlanFactoryModel(conn sqlx.SqlConn) *defaultProPlanFactoryModel {
return &defaultProPlanFactoryModel{conn: conn, table: "`pro_plan_factory`"}
}
func (m *defaultProPlanFactoryModel) Insert(ctx context.Context, data *ProPlanFactory) (sql.Result, error) {
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?, ?, ?, ?)",
m.table, proPlanFactoryRowsExpectAutoSet)
return m.conn.ExecCtx(ctx, query, data.PlanId, data.FactoryTenantId, data.FactoryType, data.ShareLinkId)
}

View File

@ -0,0 +1,67 @@
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var _ ProPlanProcessStepModel = (*customProPlanProcessStepModel)(nil)
type (
ProPlanProcessStepModel interface {
proPlanProcessStepModel
FindByPlanId(ctx context.Context, planId string) ([]*ProPlanProcessStep, error)
BulkInsert(ctx context.Context, data []*ProPlanProcessStep) error
UpdateStepStatus(ctx context.Context, planId, stepType string, status int64, operatorId string) error
}
customProPlanProcessStepModel struct {
*defaultProPlanProcessStepModel
}
)
func NewProPlanProcessStepModel(conn sqlx.SqlConn) ProPlanProcessStepModel {
return &customProPlanProcessStepModel{
defaultProPlanProcessStepModel: newProPlanProcessStepModel(conn),
}
}
func (m *customProPlanProcessStepModel) FindByPlanId(ctx context.Context, planId string) ([]*ProPlanProcessStep, error) {
var list []*ProPlanProcessStep
query := fmt.Sprintf("SELECT %s FROM %s WHERE plan_id = ? ORDER BY step_order", proPlanProcessStepRows, m.table)
if err := m.conn.QueryRowsCtx(ctx, &list, query, planId); err != nil {
return nil, err
}
return list, nil
}
func (m *customProPlanProcessStepModel) BulkInsert(ctx context.Context, data []*ProPlanProcessStep) error {
if len(data) == 0 {
return nil
}
values := make([]string, 0, len(data))
args := make([]interface{}, 0, len(data)*8)
for _, d := range data {
values = append(values, "(?, ?, ?, ?, ?, ?, ?, ?)")
args = append(args, d.StepId, d.PlanId, d.StepType, d.StepOrder, d.Status,
d.OperatorId, d.CompletedAt, d.Remark)
}
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES %s", m.table, proPlanProcessStepRowsExpectAutoSet, strings.Join(values, ","))
_, err := m.conn.ExecCtx(ctx, query, args...)
return err
}
func (m *customProPlanProcessStepModel) UpdateStepStatus(ctx context.Context, planId, stepType string, status int64, operatorId string) error {
var completedAt sql.NullTime
if status == 2 {
completedAt = sql.NullTime{Time: time.Now(), Valid: true}
}
query := fmt.Sprintf("UPDATE %s SET status = ?, operator_id = ?, completed_at = ? WHERE plan_id = ? AND step_type = ?", m.table)
_, err := m.conn.ExecCtx(ctx, query, status, operatorId, completedAt, planId, stepType)
return err
}

View File

@ -0,0 +1,69 @@
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"github.com/zeromicro/go-zero/core/stores/builder"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/stringx"
)
var (
proPlanProcessStepFieldNames = builder.RawFieldNames(&ProPlanProcessStep{})
proPlanProcessStepRows = strings.Join(proPlanProcessStepFieldNames, ",")
proPlanProcessStepRowsExpectAutoSet = strings.Join(stringx.Remove(proPlanProcessStepFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",")
)
type (
proPlanProcessStepModel interface {
Insert(ctx context.Context, data *ProPlanProcessStep) (sql.Result, error)
Delete(ctx context.Context, id int64) error
}
defaultProPlanProcessStepModel struct {
conn sqlx.SqlConn
table string
}
ProPlanProcessStep struct {
Id int64 `db:"id"`
StepId string `db:"step_id"`
PlanId string `db:"plan_id"`
StepType string `db:"step_type"`
StepOrder int64 `db:"step_order"`
Status int64 `db:"status"`
OperatorId string `db:"operator_id"`
CompletedAt sql.NullTime `db:"completed_at"`
Remark sql.NullString `db:"remark"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
)
func newProPlanProcessStepModel(conn sqlx.SqlConn) *defaultProPlanProcessStepModel {
return &defaultProPlanProcessStepModel{
conn: conn,
table: "`pro_plan_process_step`",
}
}
func (m *defaultProPlanProcessStepModel) Insert(ctx context.Context, data *ProPlanProcessStep) (sql.Result, error) {
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", m.table, proPlanProcessStepRowsExpectAutoSet)
return m.conn.ExecCtx(ctx, query,
data.StepId, data.PlanId, data.StepType, data.StepOrder, data.Status,
data.OperatorId, data.CompletedAt, data.Remark)
}
func (m *defaultProPlanProcessStepModel) Delete(ctx context.Context, id int64) error {
query := fmt.Sprintf("DELETE FROM %s WHERE `id` = ?", m.table)
_, err := m.conn.ExecCtx(ctx, query, id)
return err
}
func (m *defaultProPlanProcessStepModel) tableName() string {
return m.table
}

View File

@ -0,0 +1,53 @@
package model
import (
"context"
"fmt"
"strings"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var _ ProPlanYarnRatioModel = (*customProPlanYarnRatioModel)(nil)
type (
ProPlanYarnRatioModel interface {
proPlanYarnRatioModel
FindByPlanId(ctx context.Context, planId string) ([]*ProPlanYarnRatio, error)
BulkInsert(ctx context.Context, data []*ProPlanYarnRatio) error
}
customProPlanYarnRatioModel struct {
*defaultProPlanYarnRatioModel
}
)
func NewProPlanYarnRatioModel(conn sqlx.SqlConn) ProPlanYarnRatioModel {
return &customProPlanYarnRatioModel{
defaultProPlanYarnRatioModel: newProPlanYarnRatioModel(conn),
}
}
func (m *customProPlanYarnRatioModel) FindByPlanId(ctx context.Context, planId string) ([]*ProPlanYarnRatio, error) {
var list []*ProPlanYarnRatio
query := fmt.Sprintf("SELECT %s FROM %s WHERE plan_id = ? ORDER BY id", proPlanYarnRatioRows, m.table)
if err := m.conn.QueryRowsCtx(ctx, &list, query, planId); err != nil {
return nil, err
}
return list, nil
}
func (m *customProPlanYarnRatioModel) BulkInsert(ctx context.Context, data []*ProPlanYarnRatio) error {
if len(data) == 0 {
return nil
}
values := make([]string, 0, len(data))
args := make([]interface{}, 0, len(data)*6)
for _, d := range data {
values = append(values, "(?, ?, ?, ?, ?, ?)")
args = append(args, d.RatioId, d.PlanId, d.YarnName, d.Ratio, d.AmountPerMeter, d.TotalAmount)
}
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES %s", m.table, proPlanYarnRatioRowsExpectAutoSet, strings.Join(values, ","))
_, err := m.conn.ExecCtx(ctx, query, args...)
return err
}

View File

@ -0,0 +1,65 @@
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"github.com/zeromicro/go-zero/core/stores/builder"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/stringx"
)
var (
proPlanYarnRatioFieldNames = builder.RawFieldNames(&ProPlanYarnRatio{})
proPlanYarnRatioRows = strings.Join(proPlanYarnRatioFieldNames, ",")
proPlanYarnRatioRowsExpectAutoSet = strings.Join(stringx.Remove(proPlanYarnRatioFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",")
)
type (
proPlanYarnRatioModel interface {
Insert(ctx context.Context, data *ProPlanYarnRatio) (sql.Result, error)
Delete(ctx context.Context, id int64) error
}
defaultProPlanYarnRatioModel struct {
conn sqlx.SqlConn
table string
}
ProPlanYarnRatio struct {
Id int64 `db:"id"`
RatioId string `db:"ratio_id"`
PlanId string `db:"plan_id"`
YarnName string `db:"yarn_name"`
Ratio float64 `db:"ratio"`
AmountPerMeter float64 `db:"amount_per_meter"`
TotalAmount float64 `db:"total_amount"`
CreatedAt time.Time `db:"created_at"`
}
)
func newProPlanYarnRatioModel(conn sqlx.SqlConn) *defaultProPlanYarnRatioModel {
return &defaultProPlanYarnRatioModel{
conn: conn,
table: "`pro_plan_yarn_ratio`",
}
}
func (m *defaultProPlanYarnRatioModel) Insert(ctx context.Context, data *ProPlanYarnRatio) (sql.Result, error) {
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?, ?, ?, ?, ?, ?)", m.table, proPlanYarnRatioRowsExpectAutoSet)
return m.conn.ExecCtx(ctx, query,
data.RatioId, data.PlanId, data.YarnName, data.Ratio, data.AmountPerMeter, data.TotalAmount)
}
func (m *defaultProPlanYarnRatioModel) Delete(ctx context.Context, id int64) error {
query := fmt.Sprintf("DELETE FROM %s WHERE `id` = ?", m.table)
_, err := m.conn.ExecCtx(ctx, query, id)
return err
}
func (m *defaultProPlanYarnRatioModel) tableName() string {
return m.table
}

View File

@ -0,0 +1,89 @@
package model
import (
"context"
"fmt"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var _ ProProductionPlanModel = (*customProProductionPlanModel)(nil)
type (
ProProductionPlanModel interface {
proProductionPlanModel
FindList(ctx context.Context, tenantId string, page, pageSize int64, status int64, productName, planCode string) ([]*ProProductionPlan, int64, error)
CountTodayPlans(ctx context.Context, tenantId, dateStr string) (int64, error)
UpdateCompletedQuantity(ctx context.Context, planId string, operator string) error
UpdateStatus(ctx context.Context, planId string, status int64) error
}
customProProductionPlanModel struct {
*defaultProProductionPlanModel
}
)
func NewProProductionPlanModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) ProProductionPlanModel {
return &customProProductionPlanModel{
defaultProProductionPlanModel: newProProductionPlanModel(conn, c, opts...),
}
}
func (m *customProProductionPlanModel) FindList(ctx context.Context, tenantId string, page, pageSize int64, status int64, productName, planCode string) ([]*ProProductionPlan, int64, error) {
where := "WHERE tenant_id = ?"
args := []interface{}{tenantId}
if status != -1 {
where += " AND status = ?"
args = append(args, status)
}
if productName != "" {
where += " AND product_name LIKE ?"
args = append(args, "%"+productName+"%")
}
if planCode != "" {
where += " AND plan_code LIKE ?"
args = append(args, "%"+planCode+"%")
}
var total int64
countQuery := fmt.Sprintf("SELECT COUNT(*) FROM %s %s", m.table, where)
if err := m.QueryRowNoCacheCtx(ctx, &total, countQuery, args...); err != nil {
return nil, 0, err
}
var list []*ProProductionPlan
query := fmt.Sprintf("SELECT %s FROM %s %s ORDER BY created_at DESC LIMIT ? OFFSET ?",
proProductionPlanRows, m.table, where)
args = append(args, pageSize, (page-1)*pageSize)
if err := m.QueryRowsNoCacheCtx(ctx, &list, query, args...); err != nil {
return nil, 0, err
}
return list, total, nil
}
func (m *customProProductionPlanModel) CountTodayPlans(ctx context.Context, tenantId, dateStr string) (int64, error) {
var count int64
query := fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE tenant_id = ? AND DATE(created_at) = ?", m.table)
if err := m.QueryRowNoCacheCtx(ctx, &count, query, tenantId, dateStr); err != nil {
return 0, err
}
return count, nil
}
func (m *customProProductionPlanModel) UpdateCompletedQuantity(ctx context.Context, planId string, operator string) error {
// Use SUM to recalculate, not increment — prevents drift
query := fmt.Sprintf(`UPDATE %s SET
completed_quantity = COALESCE((SELECT SUM(quantity) FROM pro_inbound_record WHERE plan_id = ?), 0),
operator = ?
WHERE plan_id = ?`, m.table)
_, err := m.ExecNoCacheCtx(ctx, query, planId, operator, planId)
return err
}
func (m *customProProductionPlanModel) UpdateStatus(ctx context.Context, planId string, status int64) error {
query := fmt.Sprintf("UPDATE %s SET status = ? WHERE plan_id = ?", m.table)
_, err := m.ExecNoCacheCtx(ctx, query, status, planId)
return err
}

View File

@ -0,0 +1,167 @@
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"github.com/zeromicro/go-zero/core/stores/builder"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlc"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/stringx"
)
var (
proProductionPlanFieldNames = builder.RawFieldNames(&ProProductionPlan{})
proProductionPlanRows = strings.Join(proProductionPlanFieldNames, ",")
proProductionPlanRowsExpectAutoSet = strings.Join(stringx.Remove(proProductionPlanFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",")
proProductionPlanRowsWithPlaceHolder = strings.Join(stringx.Remove(proProductionPlanFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), "=?,") + "=?"
cacheProProductionPlanIdPrefix = "cache:proProductionPlan:id:"
cacheProProductionPlanPlanIdPrefix = "cache:proProductionPlan:planId:"
)
type (
proProductionPlanModel interface {
Insert(ctx context.Context, data *ProProductionPlan) (sql.Result, error)
FindOne(ctx context.Context, id int64) (*ProProductionPlan, error)
FindOneByPlanId(ctx context.Context, planId string) (*ProProductionPlan, error)
Update(ctx context.Context, data *ProProductionPlan) error
Delete(ctx context.Context, id int64) error
}
defaultProProductionPlanModel struct {
sqlc.CachedConn
table string
}
ProProductionPlan struct {
Id int64 `db:"id"`
PlanId string `db:"plan_id"`
TenantId string `db:"tenant_id"`
PlanCode string `db:"plan_code"`
ProductId string `db:"product_id"`
ProductName string `db:"product_name"`
Color string `db:"color"`
FabricCode string `db:"fabric_code"`
ColorCode string `db:"color_code"`
TargetQuantity float64 `db:"target_quantity"`
CompletedQuantity float64 `db:"completed_quantity"`
YarnUsagePerMeter float64 `db:"yarn_usage_per_meter"`
ProductionPrice float64 `db:"production_price"`
SupplierId string `db:"supplier_id"`
Status int64 `db:"status"`
Remark sql.NullString `db:"remark"`
StartTime sql.NullTime `db:"start_time"`
Creator string `db:"creator"`
Operator string `db:"operator"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
)
func newProProductionPlanModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) *defaultProProductionPlanModel {
return &defaultProProductionPlanModel{
CachedConn: sqlc.NewConn(conn, c, opts...),
table: "`pro_production_plan`",
}
}
func (m *defaultProProductionPlanModel) Insert(ctx context.Context, data *ProProductionPlan) (sql.Result, error) {
proProductionPlanIdKey := fmt.Sprintf("%s%v", cacheProProductionPlanIdPrefix, data.Id)
proProductionPlanPlanIdKey := fmt.Sprintf("%s%v", cacheProProductionPlanPlanIdPrefix, data.PlanId)
ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, proProductionPlanRowsExpectAutoSet)
return conn.ExecCtx(ctx, query,
data.PlanId, data.TenantId, data.PlanCode, data.ProductId, data.ProductName,
data.Color, data.FabricCode, data.ColorCode, data.TargetQuantity, data.CompletedQuantity,
data.YarnUsagePerMeter, data.ProductionPrice, data.SupplierId, data.Status, data.Remark,
data.StartTime, data.Creator, data.Operator)
}, proProductionPlanIdKey, proProductionPlanPlanIdKey)
return ret, err
}
func (m *defaultProProductionPlanModel) FindOne(ctx context.Context, id int64) (*ProProductionPlan, error) {
proProductionPlanIdKey := fmt.Sprintf("%s%v", cacheProProductionPlanIdPrefix, id)
var resp ProProductionPlan
err := m.QueryRowCtx(ctx, &resp, proProductionPlanIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error {
query := fmt.Sprintf("SELECT %s FROM %s WHERE `id` = ? LIMIT 1", proProductionPlanRows, m.table)
return conn.QueryRowCtx(ctx, v, query, id)
})
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultProProductionPlanModel) FindOneByPlanId(ctx context.Context, planId string) (*ProProductionPlan, error) {
proProductionPlanPlanIdKey := fmt.Sprintf("%s%v", cacheProProductionPlanPlanIdPrefix, planId)
var resp ProProductionPlan
err := m.QueryRowIndexCtx(ctx, &resp, proProductionPlanPlanIdKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v any) (i any, e error) {
query := fmt.Sprintf("SELECT %s FROM %s WHERE `plan_id` = ? LIMIT 1", proProductionPlanRows, m.table)
if err := conn.QueryRowCtx(ctx, &resp, query, planId); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultProProductionPlanModel) Update(ctx context.Context, newData *ProProductionPlan) error {
data, err := m.FindOne(ctx, newData.Id)
if err != nil {
return err
}
proProductionPlanIdKey := fmt.Sprintf("%s%v", cacheProProductionPlanIdPrefix, data.Id)
proProductionPlanPlanIdKey := fmt.Sprintf("%s%v", cacheProProductionPlanPlanIdPrefix, data.PlanId)
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("UPDATE %s SET %s WHERE `id` = ?", m.table, proProductionPlanRowsWithPlaceHolder)
return conn.ExecCtx(ctx, query,
newData.PlanId, newData.TenantId, newData.PlanCode, newData.ProductId, newData.ProductName,
newData.Color, newData.FabricCode, newData.ColorCode, newData.TargetQuantity, newData.CompletedQuantity,
newData.YarnUsagePerMeter, newData.ProductionPrice, newData.SupplierId, newData.Status, newData.Remark,
newData.StartTime, newData.Creator, newData.Operator, newData.Id)
}, proProductionPlanIdKey, proProductionPlanPlanIdKey)
return err
}
func (m *defaultProProductionPlanModel) Delete(ctx context.Context, id int64) error {
data, err := m.FindOne(ctx, id)
if err != nil {
return err
}
proProductionPlanIdKey := fmt.Sprintf("%s%v", cacheProProductionPlanIdPrefix, id)
proProductionPlanPlanIdKey := fmt.Sprintf("%s%v", cacheProProductionPlanPlanIdPrefix, data.PlanId)
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("DELETE FROM %s WHERE `id` = ?", m.table)
return conn.ExecCtx(ctx, query, id)
}, proProductionPlanIdKey, proProductionPlanPlanIdKey)
return err
}
func (m *defaultProProductionPlanModel) formatPrimary(primary any) string {
return fmt.Sprintf("%s%v", cacheProProductionPlanIdPrefix, primary)
}
func (m *defaultProProductionPlanModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary any) error {
query := fmt.Sprintf("SELECT %s FROM %s WHERE `id` = ? LIMIT 1", proProductionPlanRows, m.table)
return conn.QueryRowCtx(ctx, v, query, primary)
}
func (m *defaultProProductionPlanModel) tableName() string {
return m.table
}

View File

@ -0,0 +1,74 @@
package model
import (
"context"
"database/sql"
"fmt"
"time"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var _ ProShareLinkModel = (*customProShareLinkModel)(nil)
type (
ProShareLinkModel interface {
proShareLinkModel
FindByShortCode(ctx context.Context, shortCode string) (*ProShareLink, error)
FindByLinkId(ctx context.Context, linkId string) (*ProShareLink, error)
FindActiveLinkByPlan(ctx context.Context, planId string) (*ProShareLink, error)
UpdateClick(ctx context.Context, shortCode string) error
UpdateStatus(ctx context.Context, shortCode string, status int64, rejectReason string) error
}
customProShareLinkModel struct {
*defaultProShareLinkModel
}
)
func NewProShareLinkModel(conn sqlx.SqlConn) ProShareLinkModel {
return &customProShareLinkModel{
defaultProShareLinkModel: newProShareLinkModel(conn),
}
}
func (m *customProShareLinkModel) FindByShortCode(ctx context.Context, shortCode string) (*ProShareLink, error) {
var resp ProShareLink
query := fmt.Sprintf("SELECT %s FROM %s WHERE short_code = ? LIMIT 1", proShareLinkRows, m.table)
if err := m.conn.QueryRowCtx(ctx, &resp, query, shortCode); err != nil {
return nil, err
}
return &resp, nil
}
func (m *customProShareLinkModel) FindByLinkId(ctx context.Context, linkId string) (*ProShareLink, error) {
var resp ProShareLink
query := fmt.Sprintf("SELECT %s FROM %s WHERE link_id = ? LIMIT 1", proShareLinkRows, m.table)
if err := m.conn.QueryRowCtx(ctx, &resp, query, linkId); err != nil {
return nil, err
}
return &resp, nil
}
func (m *customProShareLinkModel) FindActiveLinkByPlan(ctx context.Context, planId string) (*ProShareLink, error) {
var resp ProShareLink
query := fmt.Sprintf("SELECT %s FROM %s WHERE plan_id = ? AND status IN (0, 1) AND expires_at > NOW() ORDER BY created_at DESC LIMIT 1",
proShareLinkRows, m.table)
if err := m.conn.QueryRowCtx(ctx, &resp, query, planId); err != nil {
return nil, err
}
return &resp, nil
}
func (m *customProShareLinkModel) UpdateClick(ctx context.Context, shortCode string) error {
query := fmt.Sprintf("UPDATE %s SET status = 1, click_count = click_count + 1, clicked_at = ? WHERE short_code = ? AND status = 0",
m.table)
_, err := m.conn.ExecCtx(ctx, query, sql.NullTime{Time: time.Now(), Valid: true}, shortCode)
return err
}
func (m *customProShareLinkModel) UpdateStatus(ctx context.Context, shortCode string, status int64, rejectReason string) error {
query := fmt.Sprintf("UPDATE %s SET status = ?, responded_at = ?, reject_reason = ? WHERE short_code = ?", m.table)
_, err := m.conn.ExecCtx(ctx, query, status, sql.NullTime{Time: time.Now(), Valid: true}, rejectReason, shortCode)
return err
}

View File

@ -0,0 +1,70 @@
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"github.com/zeromicro/go-zero/core/stores/builder"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/stringx"
)
var (
proShareLinkFieldNames = builder.RawFieldNames(&ProShareLink{})
proShareLinkRows = strings.Join(proShareLinkFieldNames, ",")
proShareLinkRowsExpectAutoSet = strings.Join(stringx.Remove(proShareLinkFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",")
)
type (
proShareLinkModel interface {
Insert(ctx context.Context, data *ProShareLink) (sql.Result, error)
Delete(ctx context.Context, id int64) error
}
defaultProShareLinkModel struct {
conn sqlx.SqlConn
table string
}
ProShareLink struct {
Id int64 `db:"id"`
LinkId string `db:"link_id"`
TenantId string `db:"tenant_id"`
PlanId string `db:"plan_id"`
ShortCode string `db:"short_code"`
Token string `db:"token"`
FactoryType string `db:"factory_type"`
HideSensitive int64 `db:"hide_sensitive"`
Status int64 `db:"status"`
ClickCount int64 `db:"click_count"`
ClickedAt sql.NullTime `db:"clicked_at"`
RespondedAt sql.NullTime `db:"responded_at"`
RejectReason string `db:"reject_reason"`
ExpiresAt time.Time `db:"expires_at"`
CreatedBy string `db:"created_by"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
)
func newProShareLinkModel(conn sqlx.SqlConn) *defaultProShareLinkModel {
return &defaultProShareLinkModel{conn: conn, table: "`pro_share_link`"}
}
func (m *defaultProShareLinkModel) Insert(ctx context.Context, data *ProShareLink) (sql.Result, error) {
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
m.table, proShareLinkRowsExpectAutoSet)
return m.conn.ExecCtx(ctx, query, data.LinkId, data.TenantId, data.PlanId,
data.ShortCode, data.Token, data.FactoryType, data.HideSensitive,
data.Status, data.ClickCount, data.ClickedAt, data.RespondedAt,
data.RejectReason, data.ExpiresAt, data.CreatedBy)
}
func (m *defaultProShareLinkModel) Delete(ctx context.Context, id int64) error {
query := fmt.Sprintf("DELETE FROM %s WHERE `id` = ?", m.table)
_, err := m.conn.ExecCtx(ctx, query, id)
return err
}

View File

@ -0,0 +1,13 @@
package config
import (
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/zrpc"
)
type Config struct {
zrpc.RpcServerConf
DataSource string
Cache cache.CacheConf
PrometheusAddr string `json:",default=:9095"`
}

View File

@ -0,0 +1,33 @@
package logic
import (
"context"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type ClickShareLinkLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewClickShareLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ClickShareLinkLogic {
return &ClickShareLinkLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *ClickShareLinkLogic) ClickShareLink(in *pb.ClickShareLinkReq) (*pb.Empty, error) {
_, err := l.svcCtx.ShareLinkModel.FindByShortCode(l.ctx, in.ShortCode)
if err != nil {
return nil, ErrLinkNotFound
}
if err := l.svcCtx.ShareLinkModel.UpdateClick(l.ctx, in.ShortCode); err != nil {
return nil, err
}
return &pb.Empty{}, nil
}

View File

@ -0,0 +1,66 @@
package logic
import (
"context"
"muyu-apiserver/pkg/tenantctx"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type CompleteStepLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCompleteStepLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CompleteStepLogic {
return &CompleteStepLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *CompleteStepLogic) CompleteStep(in *pb.CompleteStepReq) (*pb.Empty, error) {
tenantId := tenantctx.ExtractTenantId(l.ctx)
// Validate plan exists and belongs to tenant
plan, err := l.svcCtx.PlanModel.FindOneByPlanId(l.ctx, in.PlanId)
if err != nil {
return nil, ErrPlanNotFound
}
if plan.TenantId != tenantId {
return nil, ErrPermissionDenied
}
// Find steps for this plan and check the target step
steps, err := l.svcCtx.ProcessStepModel.FindByPlanId(l.ctx, in.PlanId)
if err != nil {
return nil, ErrStepNotFound
}
found := false
for _, s := range steps {
if s.StepType == in.StepType {
found = true
if s.Status == 2 {
return nil, ErrStepAlreadyComplete
}
break
}
}
if !found {
return nil, ErrStepNotFound
}
operatorId := in.OperatorId
if operatorId == "" {
operatorId = tenantId
}
// Update step status to 2 (completed)
if err := l.svcCtx.ProcessStepModel.UpdateStepStatus(l.ctx, in.PlanId, in.StepType, 2, operatorId); err != nil {
return nil, err
}
return &pb.Empty{}, nil
}

View File

@ -0,0 +1,47 @@
package logic
import (
"context"
"muyu-apiserver/pkg/tenantctx"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type ConfirmPlanLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewConfirmPlanLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ConfirmPlanLogic {
return &ConfirmPlanLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *ConfirmPlanLogic) ConfirmPlan(in *pb.ConfirmPlanReq) (*pb.Empty, error) {
tenantId := tenantctx.ExtractTenantId(l.ctx)
plan, err := l.svcCtx.PlanModel.FindOneByPlanId(l.ctx, in.PlanId)
if err != nil {
return nil, ErrPlanNotFound
}
if plan.TenantId != tenantId {
return nil, ErrPermissionDenied
}
if plan.Status != 0 {
return nil, ErrPlanNotDraft
}
if err := l.svcCtx.PlanModel.UpdateStatus(l.ctx, in.PlanId, 1); err != nil {
return nil, err
}
// Mark confirm step as completed
if err := l.svcCtx.ProcessStepModel.UpdateStepStatus(l.ctx, in.PlanId, "confirm", 2, in.Operator); err != nil {
l.Errorf("update confirm step failed: %v", err)
}
return &pb.Empty{}, nil
}

View File

@ -0,0 +1,65 @@
package logic
import (
"context"
"time"
"muyu-apiserver/model"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type ConfirmShareLinkLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewConfirmShareLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ConfirmShareLinkLogic {
return &ConfirmShareLinkLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *ConfirmShareLinkLogic) ConfirmShareLink(in *pb.ConfirmShareLinkReq) (*pb.Empty, error) {
link, err := l.svcCtx.ShareLinkModel.FindByShortCode(l.ctx, in.ShortCode)
if err != nil {
return nil, ErrLinkNotFound
}
// Check not expired
if time.Now().After(link.ExpiresAt) {
return nil, ErrLinkExpired
}
// Check status is 0 (created) or 1 (clicked)
if link.Status != 0 && link.Status != 1 {
return nil, ErrLinkAlreadyUsed
}
// Create plan_factory record linking factory to the plan
_, err = l.svcCtx.PlanFactoryModel.Insert(l.ctx, &model.ProPlanFactory{
PlanId: link.PlanId,
FactoryTenantId: in.FactoryTenantId,
FactoryType: link.FactoryType,
ShareLinkId: link.LinkId,
})
if err != nil {
return nil, err
}
// Update share link status to 2 (confirmed)
if err := l.svcCtx.ShareLinkModel.UpdateStatus(l.ctx, in.ShortCode, 2, ""); err != nil {
return nil, err
}
// Update plan status to 2 (producing) if currently 1 (pending)
plan, err := l.svcCtx.PlanModel.FindOneByPlanId(l.ctx, link.PlanId)
if err == nil && plan.Status == 1 {
if err := l.svcCtx.PlanModel.UpdateStatus(l.ctx, link.PlanId, 2); err != nil {
return nil, err
}
}
return &pb.Empty{}, nil
}

View File

@ -0,0 +1,126 @@
package logic
import (
"context"
"database/sql"
"strconv"
"time"
"muyu-apiserver/pkg/tenantctx"
"muyu-apiserver/pkg/uid"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type CreateInboundLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCreateInboundLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateInboundLogic {
return &CreateInboundLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *CreateInboundLogic) CreateInbound(in *pb.CreateInboundReq) (*pb.IdResp, error) {
tenantId := tenantctx.ExtractTenantId(l.ctx)
qty, _ := strconv.ParseFloat(in.Quantity, 64)
if qty <= 0 {
return nil, ErrInvalidQuantity
}
plan, err := l.svcCtx.PlanModel.FindOneByPlanId(l.ctx, in.PlanId)
if err != nil {
return nil, ErrPlanNotFound
}
// Tenant isolation: allow plan owner OR an associated factory tenant
if plan.TenantId != tenantId {
factories, fErr := l.svcCtx.PlanFactoryModel.FindByPlanId(l.ctx, in.PlanId)
if fErr != nil {
return nil, ErrPermissionDenied
}
authorized := false
for _, f := range factories {
if f.FactoryTenantId == tenantId {
authorized = true
break
}
}
if !authorized {
return nil, ErrPermissionDenied
}
}
if plan.Status != 2 { // must be producing
return nil, ErrPlanNotProducing
}
// Check if adding this quantity would exceed target
if plan.CompletedQuantity+qty > plan.TargetQuantity {
return nil, ErrExceedTarget
}
price, _ := strconv.ParseFloat(in.PricePerMeter, 64)
recordId := uid.Generate()
// Transactional: insert record + recalculate completed_quantity + check completion + write inventory log
err = l.svcCtx.Conn.TransactCtx(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 1. Insert inbound record
_, err := session.ExecCtx(ctx,
"INSERT INTO pro_inbound_record (record_id, tenant_id, plan_id, product_id, batch_no, quantity, rolls, price_per_meter, operator_id, remark) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
recordId, tenantId, in.PlanId, plan.ProductId, in.BatchNo, qty, in.Rolls, price, tenantId,
sql.NullString{String: in.Remark, Valid: in.Remark != ""})
if err != nil {
return err
}
// 2. Recalculate completed_quantity via SUM (not increment — prevents drift)
_, err = session.ExecCtx(ctx,
"UPDATE pro_production_plan SET completed_quantity = COALESCE((SELECT SUM(quantity) FROM pro_inbound_record WHERE plan_id = ?), 0), operator = ? WHERE plan_id = ?",
in.PlanId, tenantId, in.PlanId)
if err != nil {
return err
}
// 3. Check if plan is now complete
var completedQty float64
err = session.QueryRowCtx(ctx, &completedQty,
"SELECT completed_quantity FROM pro_production_plan WHERE plan_id = ?", in.PlanId)
if err != nil {
return err
}
if completedQty >= plan.TargetQuantity {
// Mark plan as completed
_, err = session.ExecCtx(ctx,
"UPDATE pro_production_plan SET status = 3 WHERE plan_id = ?", in.PlanId)
if err != nil {
return err
}
// Mark fabric_warehouse step as completed
_, err = session.ExecCtx(ctx,
"UPDATE pro_plan_process_step SET status = 2, completed_at = NOW(), operator_id = ? WHERE plan_id = ? AND step_type = 'fabric_warehouse'",
tenantId, in.PlanId)
if err != nil {
return err
}
}
// 4. Write inventory log (inside transaction via raw SQL)
logId := uid.Generate()
_, err = session.ExecCtx(ctx,
"INSERT INTO `inv_inventory_log` (`log_id`, `tenant_id`, `product_id`, `change_qty`, `balance_after`, `change_type`, `ref_type`, `ref_id`, `contact_id`, `log_date`, `operator`, `remark`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
logId, plan.TenantId, plan.ProductId, qty, float64(0), int64(2), "PRODUCTION", recordId, in.PlanId, time.Now(), tenantId, "生产入库 "+in.BatchNo)
return err
})
if err != nil {
return nil, err
}
return &pb.IdResp{Id: recordId}, nil
}

View File

@ -0,0 +1,122 @@
package logic
import (
"context"
"database/sql"
"fmt"
"strconv"
"time"
"muyu-apiserver/model"
"muyu-apiserver/pkg/tenantctx"
"muyu-apiserver/pkg/uid"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type CreateProductionPlanLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCreateProductionPlanLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateProductionPlanLogic {
return &CreateProductionPlanLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
var defaultSteps = []struct {
Type string
Order int64
}{
{"confirm", 1},
{"yarn_purchase", 2},
{"dyeing", 3},
{"machine_start", 4},
{"fabric_warehouse", 5},
}
func (l *CreateProductionPlanLogic) CreateProductionPlan(in *pb.CreateProductionPlanReq) (*pb.IdResp, error) {
tenantId := tenantctx.ExtractTenantId(l.ctx)
// Generate plan code PP-YYYYMMDD-XXXX
today := time.Now().Format("20060102")
count, err := l.svcCtx.PlanModel.CountTodayPlans(l.ctx, tenantId, time.Now().Format("2006-01-02"))
if err != nil {
return nil, err
}
planCode := fmt.Sprintf("PP-%s-%04d", today, count+1)
targetQty, _ := strconv.ParseFloat(in.TargetQuantity, 64)
yarnUsage, _ := strconv.ParseFloat(in.YarnUsagePerMeter, 64)
price, _ := strconv.ParseFloat(in.ProductionPrice, 64)
var startTime sql.NullTime
if in.StartTime != "" {
if t, err := time.Parse("2006-01-02", in.StartTime); err == nil {
startTime = sql.NullTime{Time: t, Valid: true}
}
}
planId := uid.Generate()
_, err = l.svcCtx.PlanModel.Insert(l.ctx, &model.ProProductionPlan{
PlanId: planId,
TenantId: tenantId,
PlanCode: planCode,
ProductId: in.ProductId,
ProductName: in.ProductName,
Color: in.Color,
FabricCode: in.FabricCode,
ColorCode: in.ColorCode,
TargetQuantity: targetQty,
YarnUsagePerMeter: yarnUsage,
ProductionPrice: price,
SupplierId: in.SupplierId,
Status: 0, // draft
Remark: sql.NullString{String: in.Remark, Valid: in.Remark != ""},
StartTime: startTime,
Creator: tenantId,
Operator: tenantId,
})
if err != nil {
return nil, err
}
// Create yarn ratios
if len(in.YarnRatios) > 0 {
ratios := make([]*model.ProPlanYarnRatio, 0, len(in.YarnRatios))
for _, yr := range in.YarnRatios {
ratio, _ := strconv.ParseFloat(yr.Ratio, 64)
amtPerM, _ := strconv.ParseFloat(yr.AmountPerMeter, 64)
ratios = append(ratios, &model.ProPlanYarnRatio{
RatioId: uid.Generate(),
PlanId: planId,
YarnName: yr.YarnName,
Ratio: ratio,
AmountPerMeter: amtPerM,
TotalAmount: amtPerM * targetQty,
})
}
if err := l.svcCtx.YarnRatioModel.BulkInsert(l.ctx, ratios); err != nil {
return nil, err
}
}
// Initialize 5 process steps
steps := make([]*model.ProPlanProcessStep, 0, len(defaultSteps))
for _, ds := range defaultSteps {
steps = append(steps, &model.ProPlanProcessStep{
StepId: uid.Generate(),
PlanId: planId,
StepType: ds.Type,
StepOrder: ds.Order,
Status: 0, // pending
})
}
if err := l.svcCtx.ProcessStepModel.BulkInsert(l.ctx, steps); err != nil {
return nil, err
}
return &pb.IdResp{Id: planId}, nil
}

View File

@ -0,0 +1,88 @@
package logic
import (
"context"
"crypto/rand"
"encoding/hex"
"time"
"muyu-apiserver/model"
"muyu-apiserver/pkg/tenantctx"
"muyu-apiserver/pkg/uid"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type CreateShareLinkLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCreateShareLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateShareLinkLogic {
return &CreateShareLinkLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *CreateShareLinkLogic) CreateShareLink(in *pb.CreateShareLinkReq) (*pb.ShareLinkInfo, error) {
tenantId := tenantctx.ExtractTenantId(l.ctx)
// Validate plan exists
_, err := l.svcCtx.PlanModel.FindOneByPlanId(l.ctx, in.PlanId)
if err != nil {
return nil, ErrPlanNotFound
}
// Generate 8-char short code using crypto/rand
b := make([]byte, 8)
if _, err := rand.Read(b); err != nil {
return nil, err
}
shortCode := hex.EncodeToString(b)[:8]
// Generate token
tb := make([]byte, 16)
if _, err := rand.Read(tb); err != nil {
return nil, err
}
token := hex.EncodeToString(tb)
// Default expiry: 30 minutes (override with ExpiresHours if provided)
expiry := 30 * time.Minute
if in.ExpiresHours > 0 {
expiry = time.Duration(in.ExpiresHours) * time.Hour
}
expiresAt := time.Now().Add(expiry)
linkId := uid.Generate()
link := &model.ProShareLink{
LinkId: linkId,
TenantId: tenantId,
PlanId: in.PlanId,
ShortCode: shortCode,
Token: token,
FactoryType: in.FactoryType,
HideSensitive: in.HideSensitive,
Status: 0, // created
ExpiresAt: expiresAt,
CreatedBy: tenantId,
}
if _, err := l.svcCtx.ShareLinkModel.Insert(l.ctx, link); err != nil {
return nil, err
}
return &pb.ShareLinkInfo{
LinkId: linkId,
TenantId: tenantId,
PlanId: in.PlanId,
ShortCode: shortCode,
Token: token,
FactoryType: in.FactoryType,
HideSensitive: in.HideSensitive,
Status: 0,
ExpiresAt: expiresAt.Format("2006-01-02 15:04:05"),
CreatedBy: tenantId,
}, nil
}

View File

@ -0,0 +1,22 @@
package logic
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
var (
ErrPlanNotFound = status.Error(codes.NotFound, "生产计划不存在")
ErrPlanNotDraft = status.Error(codes.FailedPrecondition, "计划不是草稿状态")
ErrPlanNotPending = status.Error(codes.FailedPrecondition, "计划不是待确认状态")
ErrPlanNotProducing = status.Error(codes.FailedPrecondition, "计划不在生产中")
ErrPlanCompleted = status.Error(codes.FailedPrecondition, "计划已完成")
ErrLinkNotFound = status.Error(codes.NotFound, "分享链接不存在")
ErrLinkExpired = status.Error(codes.FailedPrecondition, "分享链接已过期")
ErrLinkAlreadyUsed = status.Error(codes.FailedPrecondition, "分享链接已被使用")
ErrStepNotFound = status.Error(codes.NotFound, "流程节点不存在")
ErrStepAlreadyComplete = status.Error(codes.FailedPrecondition, "流程节点已完成")
ErrInvalidQuantity = status.Error(codes.InvalidArgument, "入库数量必须大于0")
ErrExceedTarget = status.Error(codes.FailedPrecondition, "入库总量将超过目标产量")
ErrPermissionDenied = status.Error(codes.PermissionDenied, "无权操作该生产计划")
)

View File

@ -0,0 +1,125 @@
package logic
import (
"context"
"fmt"
"muyu-apiserver/pkg/tenantctx"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type GetProductionPlanLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetProductionPlanLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProductionPlanLogic {
return &GetProductionPlanLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetProductionPlanLogic) GetProductionPlan(in *pb.GetProductionPlanReq) (*pb.ProductionPlanInfo, error) {
tenantId := tenantctx.ExtractTenantId(l.ctx)
plan, err := l.svcCtx.PlanModel.FindOneByPlanId(l.ctx, in.PlanId)
if err != nil {
return nil, ErrPlanNotFound
}
if plan.TenantId != tenantId {
return nil, ErrPermissionDenied
}
info := &pb.ProductionPlanInfo{
PlanId: plan.PlanId,
TenantId: plan.TenantId,
PlanCode: plan.PlanCode,
ProductId: plan.ProductId,
ProductName: plan.ProductName,
Color: plan.Color,
FabricCode: plan.FabricCode,
ColorCode: plan.ColorCode,
TargetQuantity: fmt.Sprintf("%.2f", plan.TargetQuantity),
CompletedQuantity: fmt.Sprintf("%.2f", plan.CompletedQuantity),
YarnUsagePerMeter: fmt.Sprintf("%.4f", plan.YarnUsagePerMeter),
ProductionPrice: fmt.Sprintf("%.2f", plan.ProductionPrice),
SupplierId: plan.SupplierId,
Status: plan.Status,
Remark: plan.Remark.String,
Creator: plan.Creator,
Operator: plan.Operator,
CreatedAt: plan.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: plan.UpdatedAt.Format("2006-01-02 15:04:05"),
}
if plan.StartTime.Valid {
info.StartTime = plan.StartTime.Time.Format("2006-01-02")
}
// Load yarn ratios
ratios, err := l.svcCtx.YarnRatioModel.FindByPlanId(l.ctx, in.PlanId)
if err == nil {
for _, r := range ratios {
info.YarnRatios = append(info.YarnRatios, &pb.YarnRatioInfo{
RatioId: r.RatioId,
PlanId: r.PlanId,
YarnName: r.YarnName,
Ratio: fmt.Sprintf("%.2f", r.Ratio),
AmountPerMeter: fmt.Sprintf("%.4f", r.AmountPerMeter),
TotalAmount: fmt.Sprintf("%.2f", r.TotalAmount),
CreatedAt: r.CreatedAt.Format("2006-01-02 15:04:05"),
})
}
}
// Load process steps
steps, err := l.svcCtx.ProcessStepModel.FindByPlanId(l.ctx, in.PlanId)
if err == nil {
for _, s := range steps {
stepInfo := &pb.ProcessStepInfo{
StepId: s.StepId,
PlanId: s.PlanId,
StepType: s.StepType,
StepOrder: s.StepOrder,
Status: s.Status,
OperatorId: s.OperatorId,
CreatedAt: s.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: s.UpdatedAt.Format("2006-01-02 15:04:05"),
}
if s.CompletedAt.Valid {
stepInfo.CompletedAt = s.CompletedAt.Time.Format("2006-01-02 15:04:05")
}
if s.Remark.Valid {
stepInfo.Remark = s.Remark.String
}
info.ProcessSteps = append(info.ProcessSteps, stepInfo)
}
}
// Load inbound records
records, _, err := l.svcCtx.InboundModel.FindByPlanId(l.ctx, in.PlanId, 1, 100)
if err == nil {
for _, r := range records {
rec := &pb.InboundRecordInfo{
RecordId: r.RecordId,
TenantId: r.TenantId,
PlanId: r.PlanId,
ProductId: r.ProductId,
BatchNo: r.BatchNo,
Quantity: fmt.Sprintf("%.2f", r.Quantity),
Rolls: r.Rolls,
PricePerMeter: fmt.Sprintf("%.2f", r.PricePerMeter),
OperatorId: r.OperatorId,
CreatedAt: r.CreatedAt.Format("2006-01-02 15:04:05"),
}
if r.Remark.Valid {
rec.Remark = r.Remark.String
}
info.InboundRecords = append(info.InboundRecords, rec)
}
}
return info, nil
}

View File

@ -0,0 +1,58 @@
package logic
import (
"context"
"time"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type GetShareLinkLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetShareLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetShareLinkLogic {
return &GetShareLinkLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetShareLinkLogic) GetShareLink(in *pb.GetShareLinkReq) (*pb.ShareLinkInfo, error) {
link, err := l.svcCtx.ShareLinkModel.FindByShortCode(l.ctx, in.ShortCode)
if err != nil {
return nil, ErrLinkNotFound
}
// Check if expired
if time.Now().After(link.ExpiresAt) {
return nil, ErrLinkExpired
}
info := &pb.ShareLinkInfo{
LinkId: link.LinkId,
TenantId: link.TenantId,
PlanId: link.PlanId,
ShortCode: link.ShortCode,
Token: link.Token,
FactoryType: link.FactoryType,
HideSensitive: link.HideSensitive,
Status: link.Status,
ClickCount: link.ClickCount,
RejectReason: link.RejectReason,
ExpiresAt: link.ExpiresAt.Format("2006-01-02 15:04:05"),
CreatedBy: link.CreatedBy,
CreatedAt: link.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: link.UpdatedAt.Format("2006-01-02 15:04:05"),
}
if link.ClickedAt.Valid {
info.ClickedAt = link.ClickedAt.Time.Format("2006-01-02 15:04:05")
}
if link.RespondedAt.Valid {
info.RespondedAt = link.RespondedAt.Time.Format("2006-01-02 15:04:05")
}
return info, nil
}

View File

@ -0,0 +1,111 @@
package logic
import (
"context"
"fmt"
"time"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type GetShareLinkPlanViewLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetShareLinkPlanViewLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetShareLinkPlanViewLogic {
return &GetShareLinkPlanViewLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetShareLinkPlanViewLogic) GetShareLinkPlanView(in *pb.GetShareLinkReq) (*pb.ShareLinkPlanView, error) {
link, err := l.svcCtx.ShareLinkModel.FindByShortCode(l.ctx, in.ShortCode)
if err != nil {
return nil, ErrLinkNotFound
}
// Check if expired
if time.Now().After(link.ExpiresAt) {
return nil, ErrLinkExpired
}
plan, err := l.svcCtx.PlanModel.FindOneByPlanId(l.ctx, link.PlanId)
if err != nil {
return nil, ErrPlanNotFound
}
planInfo := &pb.ProductionPlanInfo{
PlanId: plan.PlanId,
PlanCode: plan.PlanCode,
ProductId: plan.ProductId,
ProductName: plan.ProductName,
Color: plan.Color,
FabricCode: plan.FabricCode,
ColorCode: plan.ColorCode,
TargetQuantity: fmt.Sprintf("%.2f", plan.TargetQuantity),
CompletedQuantity: fmt.Sprintf("%.2f", plan.CompletedQuantity),
Status: plan.Status,
CreatedAt: plan.CreatedAt.Format("2006-01-02 15:04:05"),
}
if plan.StartTime.Valid {
planInfo.StartTime = plan.StartTime.Time.Format("2006-01-02")
}
// If hide_sensitive is set, mask price/supplier info
if link.HideSensitive == 0 {
planInfo.YarnUsagePerMeter = fmt.Sprintf("%.4f", plan.YarnUsagePerMeter)
planInfo.ProductionPrice = fmt.Sprintf("%.2f", plan.ProductionPrice)
planInfo.SupplierId = plan.SupplierId
}
// Load yarn ratios
ratios, err := l.svcCtx.YarnRatioModel.FindByPlanId(l.ctx, link.PlanId)
if err == nil {
for _, r := range ratios {
planInfo.YarnRatios = append(planInfo.YarnRatios, &pb.YarnRatioInfo{
RatioId: r.RatioId,
PlanId: r.PlanId,
YarnName: r.YarnName,
Ratio: fmt.Sprintf("%.2f", r.Ratio),
AmountPerMeter: fmt.Sprintf("%.4f", r.AmountPerMeter),
TotalAmount: fmt.Sprintf("%.2f", r.TotalAmount),
})
}
}
// Load process steps
steps, err := l.svcCtx.ProcessStepModel.FindByPlanId(l.ctx, link.PlanId)
if err == nil {
for _, s := range steps {
stepInfo := &pb.ProcessStepInfo{
StepId: s.StepId,
PlanId: s.PlanId,
StepType: s.StepType,
StepOrder: s.StepOrder,
Status: s.Status,
}
if s.CompletedAt.Valid {
stepInfo.CompletedAt = s.CompletedAt.Time.Format("2006-01-02 15:04:05")
}
planInfo.ProcessSteps = append(planInfo.ProcessSteps, stepInfo)
}
}
linkInfo := &pb.ShareLinkInfo{
LinkId: link.LinkId,
PlanId: link.PlanId,
ShortCode: link.ShortCode,
FactoryType: link.FactoryType,
HideSensitive: link.HideSensitive,
Status: link.Status,
ExpiresAt: link.ExpiresAt.Format("2006-01-02 15:04:05"),
}
return &pb.ShareLinkPlanView{
Plan: planInfo,
Link: linkInfo,
}, nil
}

View File

@ -0,0 +1,84 @@
package logic
import (
"context"
"fmt"
"muyu-apiserver/pkg/tenantctx"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type ListInboundLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewListInboundLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListInboundLogic {
return &ListInboundLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *ListInboundLogic) ListInbound(in *pb.ListInboundReq) (*pb.ListInboundResp, error) {
tenantId := tenantctx.ExtractTenantId(l.ctx)
// Verify requester has access to this plan (owner or associated factory)
plan, err := l.svcCtx.PlanModel.FindOneByPlanId(l.ctx, in.PlanId)
if err != nil {
return nil, ErrPlanNotFound
}
if plan.TenantId != tenantId {
factories, fErr := l.svcCtx.PlanFactoryModel.FindByPlanId(l.ctx, in.PlanId)
if fErr != nil {
return nil, ErrPermissionDenied
}
authorized := false
for _, f := range factories {
if f.FactoryTenantId == tenantId {
authorized = true
break
}
}
if !authorized {
return nil, ErrPermissionDenied
}
}
page := in.Page
if page <= 0 {
page = 1
}
pageSize := in.PageSize
if pageSize <= 0 {
pageSize = 20
}
records, total, err := l.svcCtx.InboundModel.FindByPlanId(l.ctx, in.PlanId, page, pageSize)
if err != nil {
return nil, err
}
list := make([]*pb.InboundRecordInfo, 0, len(records))
for _, r := range records {
list = append(list, &pb.InboundRecordInfo{
RecordId: r.RecordId,
TenantId: r.TenantId,
PlanId: r.PlanId,
ProductId: r.ProductId,
BatchNo: r.BatchNo,
Quantity: fmt.Sprintf("%.2f", r.Quantity),
Rolls: r.Rolls,
PricePerMeter: fmt.Sprintf("%.2f", r.PricePerMeter),
OperatorId: r.OperatorId,
Remark: r.Remark.String,
CreatedAt: r.CreatedAt.Format("2006-01-02 15:04:05"),
})
}
return &pb.ListInboundResp{
Total: total,
List: list,
}, nil
}

View File

@ -0,0 +1,74 @@
package logic
import (
"context"
"fmt"
"muyu-apiserver/pkg/tenantctx"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type ListProductionPlanLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewListProductionPlanLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListProductionPlanLogic {
return &ListProductionPlanLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *ListProductionPlanLogic) ListProductionPlan(in *pb.ListProductionPlanReq) (*pb.ListProductionPlanResp, error) {
tenantId := tenantctx.ExtractTenantId(l.ctx)
page := in.Page
if page <= 0 {
page = 1
}
pageSize := in.PageSize
if pageSize <= 0 {
pageSize = 20
}
plans, total, err := l.svcCtx.PlanModel.FindList(l.ctx, tenantId, page, pageSize, in.Status, in.ProductName, in.PlanCode)
if err != nil {
return nil, err
}
list := make([]*pb.ProductionPlanInfo, 0, len(plans))
for _, p := range plans {
info := &pb.ProductionPlanInfo{
PlanId: p.PlanId,
TenantId: p.TenantId,
PlanCode: p.PlanCode,
ProductId: p.ProductId,
ProductName: p.ProductName,
Color: p.Color,
FabricCode: p.FabricCode,
ColorCode: p.ColorCode,
TargetQuantity: fmt.Sprintf("%.2f", p.TargetQuantity),
CompletedQuantity: fmt.Sprintf("%.2f", p.CompletedQuantity),
YarnUsagePerMeter: fmt.Sprintf("%.4f", p.YarnUsagePerMeter),
ProductionPrice: fmt.Sprintf("%.2f", p.ProductionPrice),
SupplierId: p.SupplierId,
Status: p.Status,
Remark: p.Remark.String,
Creator: p.Creator,
Operator: p.Operator,
CreatedAt: p.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: p.UpdatedAt.Format("2006-01-02 15:04:05"),
}
if p.StartTime.Valid {
info.StartTime = p.StartTime.Time.Format("2006-01-02")
}
list = append(list, info)
}
return &pb.ListProductionPlanResp{
Total: total,
List: list,
}, nil
}

View File

@ -0,0 +1,45 @@
package logic
import (
"context"
"time"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type RejectShareLinkLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewRejectShareLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RejectShareLinkLogic {
return &RejectShareLinkLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *RejectShareLinkLogic) RejectShareLink(in *pb.RejectShareLinkReq) (*pb.Empty, error) {
link, err := l.svcCtx.ShareLinkModel.FindByShortCode(l.ctx, in.ShortCode)
if err != nil {
return nil, ErrLinkNotFound
}
// Check not expired
if time.Now().After(link.ExpiresAt) {
return nil, ErrLinkExpired
}
// Check status is 0 (created) or 1 (clicked)
if link.Status != 0 && link.Status != 1 {
return nil, ErrLinkAlreadyUsed
}
// Update status to 3 (rejected) with reason
if err := l.svcCtx.ShareLinkModel.UpdateStatus(l.ctx, in.ShortCode, 3, in.RejectReason); err != nil {
return nil, err
}
return &pb.Empty{}, nil
}

View File

@ -0,0 +1,83 @@
package server
import (
"context"
"muyu-apiserver/rpc/production/internal/logic"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
)
type ProductionServiceServer struct {
svcCtx *svc.ServiceContext
pb.UnimplementedProductionServiceServer
}
func NewProductionServiceServer(svcCtx *svc.ServiceContext) *ProductionServiceServer {
return &ProductionServiceServer{svcCtx: svcCtx}
}
func (s *ProductionServiceServer) CreateProductionPlan(ctx context.Context, in *pb.CreateProductionPlanReq) (*pb.IdResp, error) {
l := logic.NewCreateProductionPlanLogic(ctx, s.svcCtx)
return l.CreateProductionPlan(in)
}
func (s *ProductionServiceServer) ListProductionPlan(ctx context.Context, in *pb.ListProductionPlanReq) (*pb.ListProductionPlanResp, error) {
l := logic.NewListProductionPlanLogic(ctx, s.svcCtx)
return l.ListProductionPlan(in)
}
func (s *ProductionServiceServer) GetProductionPlan(ctx context.Context, in *pb.GetProductionPlanReq) (*pb.ProductionPlanInfo, error) {
l := logic.NewGetProductionPlanLogic(ctx, s.svcCtx)
return l.GetProductionPlan(in)
}
func (s *ProductionServiceServer) ConfirmPlan(ctx context.Context, in *pb.ConfirmPlanReq) (*pb.Empty, error) {
l := logic.NewConfirmPlanLogic(ctx, s.svcCtx)
return l.ConfirmPlan(in)
}
func (s *ProductionServiceServer) CreateShareLink(ctx context.Context, in *pb.CreateShareLinkReq) (*pb.ShareLinkInfo, error) {
l := logic.NewCreateShareLinkLogic(ctx, s.svcCtx)
return l.CreateShareLink(in)
}
func (s *ProductionServiceServer) GetShareLink(ctx context.Context, in *pb.GetShareLinkReq) (*pb.ShareLinkInfo, error) {
l := logic.NewGetShareLinkLogic(ctx, s.svcCtx)
return l.GetShareLink(in)
}
func (s *ProductionServiceServer) GetShareLinkPlanView(ctx context.Context, in *pb.GetShareLinkReq) (*pb.ShareLinkPlanView, error) {
l := logic.NewGetShareLinkPlanViewLogic(ctx, s.svcCtx)
return l.GetShareLinkPlanView(in)
}
func (s *ProductionServiceServer) ClickShareLink(ctx context.Context, in *pb.ClickShareLinkReq) (*pb.Empty, error) {
l := logic.NewClickShareLinkLogic(ctx, s.svcCtx)
return l.ClickShareLink(in)
}
func (s *ProductionServiceServer) ConfirmShareLink(ctx context.Context, in *pb.ConfirmShareLinkReq) (*pb.Empty, error) {
l := logic.NewConfirmShareLinkLogic(ctx, s.svcCtx)
return l.ConfirmShareLink(in)
}
func (s *ProductionServiceServer) RejectShareLink(ctx context.Context, in *pb.RejectShareLinkReq) (*pb.Empty, error) {
l := logic.NewRejectShareLinkLogic(ctx, s.svcCtx)
return l.RejectShareLink(in)
}
func (s *ProductionServiceServer) CompleteStep(ctx context.Context, in *pb.CompleteStepReq) (*pb.Empty, error) {
l := logic.NewCompleteStepLogic(ctx, s.svcCtx)
return l.CompleteStep(in)
}
func (s *ProductionServiceServer) CreateInbound(ctx context.Context, in *pb.CreateInboundReq) (*pb.IdResp, error) {
l := logic.NewCreateInboundLogic(ctx, s.svcCtx)
return l.CreateInbound(in)
}
func (s *ProductionServiceServer) ListInbound(ctx context.Context, in *pb.ListInboundReq) (*pb.ListInboundResp, error) {
l := logic.NewListInboundLogic(ctx, s.svcCtx)
return l.ListInbound(in)
}

View File

@ -0,0 +1,35 @@
package svc
import (
"muyu-apiserver/model"
"muyu-apiserver/rpc/production/internal/config"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type ServiceContext struct {
Config config.Config
Conn sqlx.SqlConn
PlanModel model.ProProductionPlanModel
YarnRatioModel model.ProPlanYarnRatioModel
ProcessStepModel model.ProPlanProcessStepModel
ShareLinkModel model.ProShareLinkModel
PlanFactoryModel model.ProPlanFactoryModel
InboundModel model.ProInboundRecordModel
InventoryLogModel model.InvInventoryLogModel
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := sqlx.NewMysql(c.DataSource)
return &ServiceContext{
Config: c,
Conn: conn,
PlanModel: model.NewProProductionPlanModel(conn, c.Cache),
YarnRatioModel: model.NewProPlanYarnRatioModel(conn),
ProcessStepModel: model.NewProPlanProcessStepModel(conn),
ShareLinkModel: model.NewProShareLinkModel(conn),
PlanFactoryModel: model.NewProPlanFactoryModel(conn),
InboundModel: model.NewProInboundRecordModel(conn),
InventoryLogModel: model.NewInvInventoryLogModel(conn),
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,577 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v7.35.1
// source: rpc/production/production.proto
package pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
ProductionService_CreateProductionPlan_FullMethodName = "/production.ProductionService/CreateProductionPlan"
ProductionService_ListProductionPlan_FullMethodName = "/production.ProductionService/ListProductionPlan"
ProductionService_GetProductionPlan_FullMethodName = "/production.ProductionService/GetProductionPlan"
ProductionService_ConfirmPlan_FullMethodName = "/production.ProductionService/ConfirmPlan"
ProductionService_CreateShareLink_FullMethodName = "/production.ProductionService/CreateShareLink"
ProductionService_GetShareLink_FullMethodName = "/production.ProductionService/GetShareLink"
ProductionService_GetShareLinkPlanView_FullMethodName = "/production.ProductionService/GetShareLinkPlanView"
ProductionService_ClickShareLink_FullMethodName = "/production.ProductionService/ClickShareLink"
ProductionService_ConfirmShareLink_FullMethodName = "/production.ProductionService/ConfirmShareLink"
ProductionService_RejectShareLink_FullMethodName = "/production.ProductionService/RejectShareLink"
ProductionService_CompleteStep_FullMethodName = "/production.ProductionService/CompleteStep"
ProductionService_CreateInbound_FullMethodName = "/production.ProductionService/CreateInbound"
ProductionService_ListInbound_FullMethodName = "/production.ProductionService/ListInbound"
)
// ProductionServiceClient is the client API for ProductionService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ProductionServiceClient interface {
CreateProductionPlan(ctx context.Context, in *CreateProductionPlanReq, opts ...grpc.CallOption) (*IdResp, error)
ListProductionPlan(ctx context.Context, in *ListProductionPlanReq, opts ...grpc.CallOption) (*ListProductionPlanResp, error)
GetProductionPlan(ctx context.Context, in *GetProductionPlanReq, opts ...grpc.CallOption) (*ProductionPlanInfo, error)
ConfirmPlan(ctx context.Context, in *ConfirmPlanReq, opts ...grpc.CallOption) (*Empty, error)
CreateShareLink(ctx context.Context, in *CreateShareLinkReq, opts ...grpc.CallOption) (*ShareLinkInfo, error)
GetShareLink(ctx context.Context, in *GetShareLinkReq, opts ...grpc.CallOption) (*ShareLinkInfo, error)
GetShareLinkPlanView(ctx context.Context, in *GetShareLinkReq, opts ...grpc.CallOption) (*ShareLinkPlanView, error)
ClickShareLink(ctx context.Context, in *ClickShareLinkReq, opts ...grpc.CallOption) (*Empty, error)
ConfirmShareLink(ctx context.Context, in *ConfirmShareLinkReq, opts ...grpc.CallOption) (*Empty, error)
RejectShareLink(ctx context.Context, in *RejectShareLinkReq, opts ...grpc.CallOption) (*Empty, error)
CompleteStep(ctx context.Context, in *CompleteStepReq, opts ...grpc.CallOption) (*Empty, error)
CreateInbound(ctx context.Context, in *CreateInboundReq, opts ...grpc.CallOption) (*IdResp, error)
ListInbound(ctx context.Context, in *ListInboundReq, opts ...grpc.CallOption) (*ListInboundResp, error)
}
type productionServiceClient struct {
cc grpc.ClientConnInterface
}
func NewProductionServiceClient(cc grpc.ClientConnInterface) ProductionServiceClient {
return &productionServiceClient{cc}
}
func (c *productionServiceClient) CreateProductionPlan(ctx context.Context, in *CreateProductionPlanReq, opts ...grpc.CallOption) (*IdResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(IdResp)
err := c.cc.Invoke(ctx, ProductionService_CreateProductionPlan_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productionServiceClient) ListProductionPlan(ctx context.Context, in *ListProductionPlanReq, opts ...grpc.CallOption) (*ListProductionPlanResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListProductionPlanResp)
err := c.cc.Invoke(ctx, ProductionService_ListProductionPlan_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productionServiceClient) GetProductionPlan(ctx context.Context, in *GetProductionPlanReq, opts ...grpc.CallOption) (*ProductionPlanInfo, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ProductionPlanInfo)
err := c.cc.Invoke(ctx, ProductionService_GetProductionPlan_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productionServiceClient) ConfirmPlan(ctx context.Context, in *ConfirmPlanReq, opts ...grpc.CallOption) (*Empty, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
err := c.cc.Invoke(ctx, ProductionService_ConfirmPlan_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productionServiceClient) CreateShareLink(ctx context.Context, in *CreateShareLinkReq, opts ...grpc.CallOption) (*ShareLinkInfo, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ShareLinkInfo)
err := c.cc.Invoke(ctx, ProductionService_CreateShareLink_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productionServiceClient) GetShareLink(ctx context.Context, in *GetShareLinkReq, opts ...grpc.CallOption) (*ShareLinkInfo, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ShareLinkInfo)
err := c.cc.Invoke(ctx, ProductionService_GetShareLink_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productionServiceClient) GetShareLinkPlanView(ctx context.Context, in *GetShareLinkReq, opts ...grpc.CallOption) (*ShareLinkPlanView, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ShareLinkPlanView)
err := c.cc.Invoke(ctx, ProductionService_GetShareLinkPlanView_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productionServiceClient) ClickShareLink(ctx context.Context, in *ClickShareLinkReq, opts ...grpc.CallOption) (*Empty, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
err := c.cc.Invoke(ctx, ProductionService_ClickShareLink_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productionServiceClient) ConfirmShareLink(ctx context.Context, in *ConfirmShareLinkReq, opts ...grpc.CallOption) (*Empty, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
err := c.cc.Invoke(ctx, ProductionService_ConfirmShareLink_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productionServiceClient) RejectShareLink(ctx context.Context, in *RejectShareLinkReq, opts ...grpc.CallOption) (*Empty, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
err := c.cc.Invoke(ctx, ProductionService_RejectShareLink_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productionServiceClient) CompleteStep(ctx context.Context, in *CompleteStepReq, opts ...grpc.CallOption) (*Empty, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
err := c.cc.Invoke(ctx, ProductionService_CompleteStep_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productionServiceClient) CreateInbound(ctx context.Context, in *CreateInboundReq, opts ...grpc.CallOption) (*IdResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(IdResp)
err := c.cc.Invoke(ctx, ProductionService_CreateInbound_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productionServiceClient) ListInbound(ctx context.Context, in *ListInboundReq, opts ...grpc.CallOption) (*ListInboundResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListInboundResp)
err := c.cc.Invoke(ctx, ProductionService_ListInbound_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// ProductionServiceServer is the server API for ProductionService service.
// All implementations must embed UnimplementedProductionServiceServer
// for forward compatibility.
type ProductionServiceServer interface {
CreateProductionPlan(context.Context, *CreateProductionPlanReq) (*IdResp, error)
ListProductionPlan(context.Context, *ListProductionPlanReq) (*ListProductionPlanResp, error)
GetProductionPlan(context.Context, *GetProductionPlanReq) (*ProductionPlanInfo, error)
ConfirmPlan(context.Context, *ConfirmPlanReq) (*Empty, error)
CreateShareLink(context.Context, *CreateShareLinkReq) (*ShareLinkInfo, error)
GetShareLink(context.Context, *GetShareLinkReq) (*ShareLinkInfo, error)
GetShareLinkPlanView(context.Context, *GetShareLinkReq) (*ShareLinkPlanView, error)
ClickShareLink(context.Context, *ClickShareLinkReq) (*Empty, error)
ConfirmShareLink(context.Context, *ConfirmShareLinkReq) (*Empty, error)
RejectShareLink(context.Context, *RejectShareLinkReq) (*Empty, error)
CompleteStep(context.Context, *CompleteStepReq) (*Empty, error)
CreateInbound(context.Context, *CreateInboundReq) (*IdResp, error)
ListInbound(context.Context, *ListInboundReq) (*ListInboundResp, error)
mustEmbedUnimplementedProductionServiceServer()
}
// UnimplementedProductionServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedProductionServiceServer struct{}
func (UnimplementedProductionServiceServer) CreateProductionPlan(context.Context, *CreateProductionPlanReq) (*IdResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateProductionPlan not implemented")
}
func (UnimplementedProductionServiceServer) ListProductionPlan(context.Context, *ListProductionPlanReq) (*ListProductionPlanResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListProductionPlan not implemented")
}
func (UnimplementedProductionServiceServer) GetProductionPlan(context.Context, *GetProductionPlanReq) (*ProductionPlanInfo, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetProductionPlan not implemented")
}
func (UnimplementedProductionServiceServer) ConfirmPlan(context.Context, *ConfirmPlanReq) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConfirmPlan not implemented")
}
func (UnimplementedProductionServiceServer) CreateShareLink(context.Context, *CreateShareLinkReq) (*ShareLinkInfo, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateShareLink not implemented")
}
func (UnimplementedProductionServiceServer) GetShareLink(context.Context, *GetShareLinkReq) (*ShareLinkInfo, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetShareLink not implemented")
}
func (UnimplementedProductionServiceServer) GetShareLinkPlanView(context.Context, *GetShareLinkReq) (*ShareLinkPlanView, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetShareLinkPlanView not implemented")
}
func (UnimplementedProductionServiceServer) ClickShareLink(context.Context, *ClickShareLinkReq) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method ClickShareLink not implemented")
}
func (UnimplementedProductionServiceServer) ConfirmShareLink(context.Context, *ConfirmShareLinkReq) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConfirmShareLink not implemented")
}
func (UnimplementedProductionServiceServer) RejectShareLink(context.Context, *RejectShareLinkReq) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method RejectShareLink not implemented")
}
func (UnimplementedProductionServiceServer) CompleteStep(context.Context, *CompleteStepReq) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method CompleteStep not implemented")
}
func (UnimplementedProductionServiceServer) CreateInbound(context.Context, *CreateInboundReq) (*IdResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateInbound not implemented")
}
func (UnimplementedProductionServiceServer) ListInbound(context.Context, *ListInboundReq) (*ListInboundResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListInbound not implemented")
}
func (UnimplementedProductionServiceServer) mustEmbedUnimplementedProductionServiceServer() {}
func (UnimplementedProductionServiceServer) testEmbeddedByValue() {}
// UnsafeProductionServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ProductionServiceServer will
// result in compilation errors.
type UnsafeProductionServiceServer interface {
mustEmbedUnimplementedProductionServiceServer()
}
func RegisterProductionServiceServer(s grpc.ServiceRegistrar, srv ProductionServiceServer) {
// If the following call pancis, it indicates UnimplementedProductionServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&ProductionService_ServiceDesc, srv)
}
func _ProductionService_CreateProductionPlan_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateProductionPlanReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).CreateProductionPlan(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_CreateProductionPlan_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).CreateProductionPlan(ctx, req.(*CreateProductionPlanReq))
}
return interceptor(ctx, in, info, handler)
}
func _ProductionService_ListProductionPlan_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListProductionPlanReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).ListProductionPlan(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_ListProductionPlan_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).ListProductionPlan(ctx, req.(*ListProductionPlanReq))
}
return interceptor(ctx, in, info, handler)
}
func _ProductionService_GetProductionPlan_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetProductionPlanReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).GetProductionPlan(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_GetProductionPlan_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).GetProductionPlan(ctx, req.(*GetProductionPlanReq))
}
return interceptor(ctx, in, info, handler)
}
func _ProductionService_ConfirmPlan_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ConfirmPlanReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).ConfirmPlan(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_ConfirmPlan_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).ConfirmPlan(ctx, req.(*ConfirmPlanReq))
}
return interceptor(ctx, in, info, handler)
}
func _ProductionService_CreateShareLink_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateShareLinkReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).CreateShareLink(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_CreateShareLink_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).CreateShareLink(ctx, req.(*CreateShareLinkReq))
}
return interceptor(ctx, in, info, handler)
}
func _ProductionService_GetShareLink_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetShareLinkReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).GetShareLink(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_GetShareLink_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).GetShareLink(ctx, req.(*GetShareLinkReq))
}
return interceptor(ctx, in, info, handler)
}
func _ProductionService_GetShareLinkPlanView_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetShareLinkReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).GetShareLinkPlanView(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_GetShareLinkPlanView_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).GetShareLinkPlanView(ctx, req.(*GetShareLinkReq))
}
return interceptor(ctx, in, info, handler)
}
func _ProductionService_ClickShareLink_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ClickShareLinkReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).ClickShareLink(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_ClickShareLink_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).ClickShareLink(ctx, req.(*ClickShareLinkReq))
}
return interceptor(ctx, in, info, handler)
}
func _ProductionService_ConfirmShareLink_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ConfirmShareLinkReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).ConfirmShareLink(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_ConfirmShareLink_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).ConfirmShareLink(ctx, req.(*ConfirmShareLinkReq))
}
return interceptor(ctx, in, info, handler)
}
func _ProductionService_RejectShareLink_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RejectShareLinkReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).RejectShareLink(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_RejectShareLink_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).RejectShareLink(ctx, req.(*RejectShareLinkReq))
}
return interceptor(ctx, in, info, handler)
}
func _ProductionService_CompleteStep_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CompleteStepReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).CompleteStep(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_CompleteStep_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).CompleteStep(ctx, req.(*CompleteStepReq))
}
return interceptor(ctx, in, info, handler)
}
func _ProductionService_CreateInbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateInboundReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).CreateInbound(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_CreateInbound_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).CreateInbound(ctx, req.(*CreateInboundReq))
}
return interceptor(ctx, in, info, handler)
}
func _ProductionService_ListInbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListInboundReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductionServiceServer).ListInbound(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProductionService_ListInbound_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductionServiceServer).ListInbound(ctx, req.(*ListInboundReq))
}
return interceptor(ctx, in, info, handler)
}
// ProductionService_ServiceDesc is the grpc.ServiceDesc for ProductionService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var ProductionService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "production.ProductionService",
HandlerType: (*ProductionServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "CreateProductionPlan",
Handler: _ProductionService_CreateProductionPlan_Handler,
},
{
MethodName: "ListProductionPlan",
Handler: _ProductionService_ListProductionPlan_Handler,
},
{
MethodName: "GetProductionPlan",
Handler: _ProductionService_GetProductionPlan_Handler,
},
{
MethodName: "ConfirmPlan",
Handler: _ProductionService_ConfirmPlan_Handler,
},
{
MethodName: "CreateShareLink",
Handler: _ProductionService_CreateShareLink_Handler,
},
{
MethodName: "GetShareLink",
Handler: _ProductionService_GetShareLink_Handler,
},
{
MethodName: "GetShareLinkPlanView",
Handler: _ProductionService_GetShareLinkPlanView_Handler,
},
{
MethodName: "ClickShareLink",
Handler: _ProductionService_ClickShareLink_Handler,
},
{
MethodName: "ConfirmShareLink",
Handler: _ProductionService_ConfirmShareLink_Handler,
},
{
MethodName: "RejectShareLink",
Handler: _ProductionService_RejectShareLink_Handler,
},
{
MethodName: "CompleteStep",
Handler: _ProductionService_CompleteStep_Handler,
},
{
MethodName: "CreateInbound",
Handler: _ProductionService_CreateInbound_Handler,
},
{
MethodName: "ListInbound",
Handler: _ProductionService_ListInbound_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "rpc/production/production.proto",
}

View File

@ -0,0 +1,43 @@
package main
import (
"flag"
"fmt"
"muyu-apiserver/pkg/metrics"
"muyu-apiserver/rpc/production/internal/config"
"muyu-apiserver/rpc/production/internal/server"
"muyu-apiserver/rpc/production/internal/svc"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/core/service"
"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
var configFile = flag.String("f", "etc/production.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
metrics.StartHTTP(c.PrometheusAddr)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
pb.RegisterProductionServiceServer(grpcServer, server.NewProductionServiceServer(ctx))
if c.Mode == service.DevMode || c.Mode == service.TestMode {
reflection.Register(grpcServer)
}
})
s.AddUnaryInterceptors(metrics.UnaryServerInterceptor())
defer s.Stop()
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
s.Start()
}

View File

@ -0,0 +1,242 @@
syntax = "proto3";
package production;
option go_package = "./pb";
// ==================== Common ====================
message IdResp {
string id = 1;
}
message Empty {}
// ==================== ProductionPlan ====================
message ProductionPlanInfo {
string plan_id = 1;
string tenant_id = 2;
string plan_code = 3;
string product_id = 4;
string product_name = 5;
string color = 6;
string fabric_code = 7;
string color_code = 8;
string target_quantity = 9;
string completed_quantity = 10;
string yarn_usage_per_meter = 11;
string production_price = 12;
string supplier_id = 13;
int64 status = 14;
string remark = 15;
string start_time = 16;
string creator = 17;
string operator = 18;
string created_at = 19;
string updated_at = 20;
repeated YarnRatioInfo yarn_ratios = 21;
repeated ProcessStepInfo process_steps = 22;
repeated InboundRecordInfo inbound_records = 23;
}
message CreateProductionPlanReq {
string tenant_id = 1;
string product_id = 2;
string product_name = 3;
string color = 4;
string fabric_code = 5;
string color_code = 6;
string target_quantity = 7;
string yarn_usage_per_meter = 8;
string production_price = 9;
string supplier_id = 10;
string remark = 11;
string start_time = 12;
string creator = 13;
repeated YarnRatioReq yarn_ratios = 14;
repeated ProcessStepReq process_steps = 15;
}
message ListProductionPlanReq {
string tenant_id = 1;
int64 page = 2;
int64 page_size = 3;
int64 status = 4;
string product_name = 5;
string plan_code = 6;
}
message ListProductionPlanResp {
int64 total = 1;
repeated ProductionPlanInfo list = 2;
}
message GetProductionPlanReq {
string plan_id = 1;
}
message ConfirmPlanReq {
string plan_id = 1;
string operator = 2;
}
// ==================== YarnRatio ====================
message YarnRatioInfo {
string ratio_id = 1;
string plan_id = 2;
string yarn_name = 3;
string ratio = 4;
string amount_per_meter = 5;
string total_amount = 6;
string created_at = 7;
}
message YarnRatioReq {
string yarn_name = 1;
string ratio = 2;
string amount_per_meter = 3;
string total_amount = 4;
}
// ==================== ProcessStep ====================
message ProcessStepInfo {
string step_id = 1;
string plan_id = 2;
string step_type = 3;
int64 step_order = 4;
int64 status = 5;
string operator_id = 6;
string completed_at = 7;
string remark = 8;
string created_at = 9;
string updated_at = 10;
}
message ProcessStepReq {
string step_type = 1;
int64 step_order = 2;
string remark = 3;
}
message CompleteStepReq {
string plan_id = 1;
string step_type = 2;
string operator_id = 3;
}
// ==================== ShareLink ====================
message ShareLinkInfo {
string link_id = 1;
string tenant_id = 2;
string plan_id = 3;
string short_code = 4;
string token = 5;
string factory_type = 6;
int64 hide_sensitive = 7;
int64 status = 8;
int64 click_count = 9;
string clicked_at = 10;
string responded_at = 11;
string reject_reason = 12;
string expires_at = 13;
string created_by = 14;
string created_at = 15;
string updated_at = 16;
}
message ShareLinkPlanView {
ProductionPlanInfo plan = 1;
ShareLinkInfo link = 2;
}
message CreateShareLinkReq {
string tenant_id = 1;
string plan_id = 2;
string factory_type = 3;
int64 hide_sensitive = 4;
int64 expires_hours = 5;
string created_by = 6;
}
message GetShareLinkReq {
string short_code = 1;
string token = 2;
}
message ClickShareLinkReq {
string short_code = 1;
}
message ConfirmShareLinkReq {
string short_code = 1;
string token = 2;
string factory_tenant_id = 3;
}
message RejectShareLinkReq {
string short_code = 1;
string token = 2;
string reject_reason = 3;
}
// ==================== Inbound ====================
message InboundRecordInfo {
string record_id = 1;
string tenant_id = 2;
string plan_id = 3;
string product_id = 4;
string batch_no = 5;
string quantity = 6;
int64 rolls = 7;
string price_per_meter = 8;
string operator_id = 9;
string remark = 10;
string created_at = 11;
}
message CreateInboundReq {
string tenant_id = 1;
string plan_id = 2;
string product_id = 3;
string batch_no = 4;
string quantity = 5;
int64 rolls = 6;
string price_per_meter = 7;
string operator_id = 8;
string remark = 9;
}
message ListInboundReq {
string tenant_id = 1;
string plan_id = 2;
int64 page = 3;
int64 page_size = 4;
}
message ListInboundResp {
int64 total = 1;
repeated InboundRecordInfo list = 2;
}
// ==================== Service ====================
service ProductionService {
rpc CreateProductionPlan(CreateProductionPlanReq) returns (IdResp);
rpc ListProductionPlan(ListProductionPlanReq) returns (ListProductionPlanResp);
rpc GetProductionPlan(GetProductionPlanReq) returns (ProductionPlanInfo);
rpc ConfirmPlan(ConfirmPlanReq) returns (Empty);
rpc CreateShareLink(CreateShareLinkReq) returns (ShareLinkInfo);
rpc GetShareLink(GetShareLinkReq) returns (ShareLinkInfo);
rpc GetShareLinkPlanView(GetShareLinkReq) returns (ShareLinkPlanView);
rpc ClickShareLink(ClickShareLinkReq) returns (Empty);
rpc ConfirmShareLink(ConfirmShareLinkReq) returns (Empty);
rpc RejectShareLink(RejectShareLinkReq) returns (Empty);
rpc CompleteStep(CompleteStepReq) returns (Empty);
rpc CreateInbound(CreateInboundReq) returns (IdResp);
rpc ListInbound(ListInboundReq) returns (ListInboundResp);
}

View File

@ -0,0 +1,113 @@
package productionservice
import (
"context"
"muyu-apiserver/rpc/production/pb"
"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc"
)
type (
CreateProductionPlanReq = pb.CreateProductionPlanReq
ListProductionPlanReq = pb.ListProductionPlanReq
ListProductionPlanResp = pb.ListProductionPlanResp
GetProductionPlanReq = pb.GetProductionPlanReq
ProductionPlanInfo = pb.ProductionPlanInfo
ConfirmPlanReq = pb.ConfirmPlanReq
CreateShareLinkReq = pb.CreateShareLinkReq
GetShareLinkReq = pb.GetShareLinkReq
ShareLinkInfo = pb.ShareLinkInfo
ShareLinkPlanView = pb.ShareLinkPlanView
ClickShareLinkReq = pb.ClickShareLinkReq
ConfirmShareLinkReq = pb.ConfirmShareLinkReq
RejectShareLinkReq = pb.RejectShareLinkReq
CompleteStepReq = pb.CompleteStepReq
CreateInboundReq = pb.CreateInboundReq
ListInboundReq = pb.ListInboundReq
ListInboundResp = pb.ListInboundResp
InboundRecordInfo = pb.InboundRecordInfo
YarnRatioInfo = pb.YarnRatioInfo
YarnRatioReq = pb.YarnRatioReq
ProcessStepInfo = pb.ProcessStepInfo
ProcessStepReq = pb.ProcessStepReq
IdResp = pb.IdResp
Empty = pb.Empty
ProductionService interface {
CreateProductionPlan(ctx context.Context, in *CreateProductionPlanReq, opts ...grpc.CallOption) (*IdResp, error)
ListProductionPlan(ctx context.Context, in *ListProductionPlanReq, opts ...grpc.CallOption) (*ListProductionPlanResp, error)
GetProductionPlan(ctx context.Context, in *GetProductionPlanReq, opts ...grpc.CallOption) (*ProductionPlanInfo, error)
ConfirmPlan(ctx context.Context, in *ConfirmPlanReq, opts ...grpc.CallOption) (*Empty, error)
CreateShareLink(ctx context.Context, in *CreateShareLinkReq, opts ...grpc.CallOption) (*ShareLinkInfo, error)
GetShareLink(ctx context.Context, in *GetShareLinkReq, opts ...grpc.CallOption) (*ShareLinkInfo, error)
GetShareLinkPlanView(ctx context.Context, in *GetShareLinkReq, opts ...grpc.CallOption) (*ShareLinkPlanView, error)
ClickShareLink(ctx context.Context, in *ClickShareLinkReq, opts ...grpc.CallOption) (*Empty, error)
ConfirmShareLink(ctx context.Context, in *ConfirmShareLinkReq, opts ...grpc.CallOption) (*Empty, error)
RejectShareLink(ctx context.Context, in *RejectShareLinkReq, opts ...grpc.CallOption) (*Empty, error)
CompleteStep(ctx context.Context, in *CompleteStepReq, opts ...grpc.CallOption) (*Empty, error)
CreateInbound(ctx context.Context, in *CreateInboundReq, opts ...grpc.CallOption) (*IdResp, error)
ListInbound(ctx context.Context, in *ListInboundReq, opts ...grpc.CallOption) (*ListInboundResp, error)
}
defaultProductionService struct {
cli zrpc.Client
}
)
func NewProductionService(cli zrpc.Client) ProductionService {
return &defaultProductionService{cli: cli}
}
func (m *defaultProductionService) CreateProductionPlan(ctx context.Context, in *CreateProductionPlanReq, opts ...grpc.CallOption) (*IdResp, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).CreateProductionPlan(ctx, in, opts...)
}
func (m *defaultProductionService) ListProductionPlan(ctx context.Context, in *ListProductionPlanReq, opts ...grpc.CallOption) (*ListProductionPlanResp, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).ListProductionPlan(ctx, in, opts...)
}
func (m *defaultProductionService) GetProductionPlan(ctx context.Context, in *GetProductionPlanReq, opts ...grpc.CallOption) (*ProductionPlanInfo, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).GetProductionPlan(ctx, in, opts...)
}
func (m *defaultProductionService) ConfirmPlan(ctx context.Context, in *ConfirmPlanReq, opts ...grpc.CallOption) (*Empty, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).ConfirmPlan(ctx, in, opts...)
}
func (m *defaultProductionService) CreateShareLink(ctx context.Context, in *CreateShareLinkReq, opts ...grpc.CallOption) (*ShareLinkInfo, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).CreateShareLink(ctx, in, opts...)
}
func (m *defaultProductionService) GetShareLink(ctx context.Context, in *GetShareLinkReq, opts ...grpc.CallOption) (*ShareLinkInfo, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).GetShareLink(ctx, in, opts...)
}
func (m *defaultProductionService) GetShareLinkPlanView(ctx context.Context, in *GetShareLinkReq, opts ...grpc.CallOption) (*ShareLinkPlanView, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).GetShareLinkPlanView(ctx, in, opts...)
}
func (m *defaultProductionService) ClickShareLink(ctx context.Context, in *ClickShareLinkReq, opts ...grpc.CallOption) (*Empty, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).ClickShareLink(ctx, in, opts...)
}
func (m *defaultProductionService) ConfirmShareLink(ctx context.Context, in *ConfirmShareLinkReq, opts ...grpc.CallOption) (*Empty, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).ConfirmShareLink(ctx, in, opts...)
}
func (m *defaultProductionService) RejectShareLink(ctx context.Context, in *RejectShareLinkReq, opts ...grpc.CallOption) (*Empty, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).RejectShareLink(ctx, in, opts...)
}
func (m *defaultProductionService) CompleteStep(ctx context.Context, in *CompleteStepReq, opts ...grpc.CallOption) (*Empty, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).CompleteStep(ctx, in, opts...)
}
func (m *defaultProductionService) CreateInbound(ctx context.Context, in *CreateInboundReq, opts ...grpc.CallOption) (*IdResp, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).CreateInbound(ctx, in, opts...)
}
func (m *defaultProductionService) ListInbound(ctx context.Context, in *ListInboundReq, opts ...grpc.CallOption) (*ListInboundResp, error) {
return pb.NewProductionServiceClient(m.cli.Conn()).ListInbound(ctx, in, opts...)
}