Compare commits

...

10 Commits

Author SHA1 Message Date
han0
c4c74cda65 fix: ci
Some checks failed
ci / build (push) Has been cancelled
2025-10-13 15:21:26 +08:00
han0
80272d2bf3 fix: 替换四六舍入为四五舍入 2025-06-05 18:57:35 +08:00
han0
1402b1a1e3 feat: 新增单独计算近半年均价的接口 2025-06-05 18:26:30 +08:00
han0
d752fe17c8 feat: 更新计算价格与位数同步 2025-05-26 17:44:09 +08:00
han0
463f9256fa fix: 修复材料半年均价显示位数异常的问题 2025-05-21 08:42:34 +08:00
han0
a10ab91a31 fix: 修复四舍五入计算问题 2025-05-19 18:12:03 +08:00
han0
7a9aa93a10 fix: 修复发布价材料重复的问题 2025-05-19 15:57:25 +08:00
han0
40c1593ff7 fix: 修复材料半年均价计算错误的问题 2025-05-07 12:48:55 +08:00
han0
6ada6371a3 fix: 修复计算机未按权重计算的问题 2025-05-07 08:35:52 +08:00
han0
bfb1315028 fix: 更新计算价的计算方式 2025-04-22 15:41:32 +08:00
8 changed files with 125 additions and 59 deletions

48
.gitea/workflows/ci.yaml Normal file
View File

@@ -0,0 +1,48 @@
name: ci
on:
push:
branches:
- test
env:
DEPLOY_PATH: /home/opt/app/material-api
DEPLOY_TEST_HOST_IP: 172.17.0.1
jobs:
build:
runs-on: ubuntu-latests
container:
image: python:3.7.16
steps:
- name: Checkout
run: |
git clone --depth 1 "http://test:89085111dd6939710a992d6404bd61a50e420685@8.138.239.222:20300/${GITHUB_REPOSITORY}.git" .
git checkout "${GITHUB_SHA}"
- name: Build
run: |
mkdir dist
PYTHONUSERBASE=$CI_PROJECT_DIR/home pip install --upgrade pip -i https://mirror.aliyun.com/pypi/simple --user
PYTHONUSERBASE=$CI_PROJECT_DIR/home pip install -r ./requirements.txt -i https://mirror.aliyun.com/pypi/simple --user
rsync -ha ./ ./dist --exclude=dist --exclude=.git --delete
- name: Deploy
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh -o StrictHostKeyChecking=no root@$DEPLOY_TEST_HOST_IP "mkdir -p $DEPLOY_PATH"
rsync -r ./dist/ ${DEPLOY_DEV_HOST_IP}:${DEPLOY_PATH} --delete
ssh ${SSH_OPTS} ${DEPLOY_DEV_HOST_IP} "cd ${DEPLOY_PATH} && source /etc/profile && docker-compose up -d --force-recreate"
ssh root@$DEPLOY_TEST_HOST_IP "rm -f $DEPLOY_PATH/*.jar"
scp ruoyi-admin/target/ruoyi-admin.jar root@$DEPLOY_TEST_HOST_IP:$DEPLOY_PATH/
ssh root@$DEPLOY_TEST_HOST_IP "mv $DEPLOY_PATH/ruoyi-admin.jar $DEPLOY_PATH/app.jar"
scp docker-compose.test.yml root@$DEPLOY_TEST_HOST_IP:$DEPLOY_PATH/
ssh root@$DEPLOY_TEST_HOST_IP "mv $DEPLOY_PATH/docker-compose.test.yml $DEPLOY_PATH/docker-compose.yml"
ssh root@$DEPLOY_TEST_HOST_IP "cd $DEPLOY_PATH && docker compose up -d --force-recreate"

View File

@@ -38,6 +38,7 @@ def refresh_task(task_id):
class CalculateQuery(BaseModel): class CalculateQuery(BaseModel):
year: int = Field(title='年份') year: int = Field(title='年份')
month: int = Field(title='月份') month: int = Field(title='月份')
only_avg: int = Field(title='是否只计算均值', default=0)
@data.route('/calculate', methods=['GET']) @data.route('/calculate', methods=['GET'])
@@ -51,6 +52,6 @@ def start_calculate(query: CalculateQuery):
@data.route('/collect', methods=['GET']) @data.route('/collect', methods=['GET'])
@siwa.doc(tags=[""], summary='触发计算任务', query=CalculateQuery) @siwa.doc(tags=[""], summary='触发计算任务', query=CalculateQuery)
def start_collect(query: CalculateQuery): def start_collect(query: CalculateQuery):
collect(year=query.year, month=query.month) collect(year=query.year, month=query.month, only_avg=query.only_avg)
return Response() return Response()

View File

@@ -1,4 +1,4 @@
from decimal import Decimal from decimal import Decimal, ROUND_HALF_UP
from commons.models.data_fujian import DataFujian from commons.models.data_fujian import DataFujian
from commons.models.data_network import DataNetwork from commons.models.data_network import DataNetwork
@@ -37,7 +37,8 @@ class Helper:
class RoundMethod: class RoundMethod:
@staticmethod @staticmethod
def normal(n, round_bit): def normal(n, round_bit):
result = round(n, round_bit) q_ext = Decimal('0.' + '0' * round_bit)
result = Decimal(n).quantize(q_ext, rounding=ROUND_HALF_UP)
return result if round_bit != 0 else int(result) return result if round_bit != 0 else int(result)
@staticmethod @staticmethod
@@ -133,6 +134,7 @@ class Calculator:
} }
def run(self): def run(self):
self._get_weight()
self.price_ftb, self.fluctuating_ftb = self._get_ftb_price() self.price_ftb, self.fluctuating_ftb = self._get_ftb_price()
self.price_ss, self.fluctuating_ss = self._get_ss_price() self.price_ss, self.fluctuating_ss = self._get_ss_price()
self.price_fhb, self.fluctuating_fhb = self._get_fhb_price() self.price_fhb, self.fluctuating_fhb = self._get_fhb_price()
@@ -190,17 +192,20 @@ class Calculator:
return getattr(self.previous_prices, 'price_recommend', 0) return getattr(self.previous_prices, 'price_recommend', 0)
def _get_calculate_price(self, round_dit=2): def _get_calculate_price(self, round_dit=2):
prices = (self.price_ftb, self.price_ss, self.price_fhb, self.price_network, self.price_survey, self.price_last_month) prices = [
total = sum(float(i) for i in prices) self.price_ftb * self.weight_ftb,
if total == 0: self.price_ss * self.weight_ss,
self.price_fhb * self.weight_fhb,
self.price_network * self.weight_network,
self.price_survey * self.weight_survey,
]
if sum(prices) == 0:
return 0 return 0
result = total / len([i for i in prices if i != 0]) result = sum(prices) / sum([self.weight_ftb, self.weight_ss, self.weight_fhb, self.weight_network, self.weight_survey])
result = round(result, round_dit) result = self.round_method(result, self.round_bit)
return result return result
def _get_recommend_price(self, round_by=10): def _get_recommend_price(self, round_by=10):
self._get_weight()
if not self.previous_prices: if not self.previous_prices:
return 0, 0 return 0, 0
@@ -226,7 +231,7 @@ class Calculator:
def _get_weight(self): def _get_weight(self):
# 如果有上月数据按上月权重计算 无则平均数 # 如果有上月数据按上月权重计算 无则平均数
if self._previous_prices: if self.previous_prices:
self.weight_ftb = self._previous_prices.weight_ftb self.weight_ftb = self._previous_prices.weight_ftb
self.weight_ss = self._previous_prices.weight_ss self.weight_ss = self._previous_prices.weight_ss
self.weight_fhb = self._previous_prices.weight_fhb self.weight_fhb = self._previous_prices.weight_fhb

View File

@@ -1,9 +1,10 @@
import datetime import datetime
import decimal
from decimal import Decimal
from sqlalchemy import func from sqlalchemy import func
from calculators import Helper from calculators import Helper
from commons.models.fujian_survey import FujianSurvey
from commons.models.price_publish import PricePublish from commons.models.price_publish import PricePublish
from commons.models.price_result import PriceResult from commons.models.price_result import PriceResult
from commons.models.material import Material from commons.models.material import Material
@@ -11,7 +12,8 @@ from commons.models.material import Material
class Collector: class Collector:
def __init__(self, year, month, force=True): def __init__(self, year, month, force=True, only_avg=0):
self.only_avg = only_avg
self.year = year self.year = year
self.month = month self.month = month
self.force = True # todo-2 已发布的价格不在覆盖计算 self.force = True # todo-2 已发布的价格不在覆盖计算
@@ -21,14 +23,17 @@ class Collector:
self.material_codes = [m.code for m in self.materials if m.code] self.material_codes = [m.code for m in self.materials if m.code]
# 缓存材料税率信息 # 缓存材料税率信息
self.tax_map = {m.code: m.tax for m in self.materials if m.code} self.tax_map = {m.code: m.tax for m in self.materials if m.code}
self.unit_map = {m.code: m.unit for m in self.materials if m.code}
self.name_map = {m.code: m.name for m in self.materials if m.code}
self.spec_map = {m.code: m.spec for m in self.materials if m.code}
self.round_bit_map = {m.code: m.round_bit for m in self.materials if m.code}
def get_avg(self): def get_avg(self):
query = PricePublish.get_query(material_id_in=self.material_codes) query = PricePublish.get_query(material_id_in=self.material_codes)
query = PricePublish.query_previous_month(query, start_date=datetime.date(self.year, self.month, 1), count=6) query = PricePublish.query_previous_month(query, start_date=datetime.date(self.year, self.month, 1), count=6)
query = query.filter(PricePublish.type == '1')
query = query.with_entities( query = query.with_entities(
PricePublish.material_id, PricePublish.material_id,
PricePublish.name,
PricePublish.spec,
func.avg(PricePublish.price), func.avg(PricePublish.price),
func.avg(PricePublish.price_fuzhou), func.avg(PricePublish.price_fuzhou),
func.avg(PricePublish.price_xiamen), func.avg(PricePublish.price_xiamen),
@@ -41,43 +46,39 @@ class Collector:
func.avg(PricePublish.price_ningde), func.avg(PricePublish.price_ningde),
func.avg(PricePublish.price_pintan), func.avg(PricePublish.price_pintan),
func.avg(PricePublish.price_zhangzhoukfq), func.avg(PricePublish.price_zhangzhoukfq),
PricePublish.tax,
PricePublish.unit,
) )
query = query.filter(PricePublish.price != 0) query = query.filter(PricePublish.price != 0)
query = query.group_by( query = query.group_by(
PricePublish.material_id, PricePublish.material_id,
PricePublish.name,
PricePublish.spec,
PricePublish.tax,
PricePublish.unit,
) )
data = query.all() data = query.all()
for item in data: for item in data:
material_id, name, spec, price, price_fuzhou, price_xiamen, price_putian, price_sanming, price_quanzhou, \ material_id, price, price_fuzhou, price_xiamen, price_putian, price_sanming, price_quanzhou, \
price_zhangzhou, price_nanpin, price_longyan, price_ningde, price_pintan, price_zhangzhoukfq, tax, unit, = item price_zhangzhou, price_nanpin, price_longyan, price_ningde, price_pintan, price_zhangzhoukfq = item
display_digit = self.digit_map.get(material_id, 1) display_digit = self.digit_map.get(material_id, 1)
round_bit = self.round_bit_map.get(material_id, 2)
q_ext = Decimal('0.' + '0' * round_bit)
PricePublish( PricePublish(
year=self.year, year=self.year,
month=self.month, month=self.month,
material_id=material_id, material_id=material_id,
name=name, name=self.name_map.get(material_id, ''),
spec=spec, spec=self.spec_map.get(material_id, ''),
price=round(price, 2), price=price.quantize(q_ext, decimal.ROUND_HALF_UP) if price else 0,
price_fuzhou=round(price_fuzhou or 0, 2), price_fuzhou=price_fuzhou.quantize(q_ext, decimal.ROUND_HALF_UP) if price_fuzhou else 0,
price_xiamen=round(price_xiamen or 0, 2), price_xiamen=price_xiamen.quantize(q_ext, decimal.ROUND_HALF_UP) if price_xiamen else 0,
price_putian=round(price_putian or 0, 2), price_putian=price_putian.quantize(q_ext, decimal.ROUND_HALF_UP) if price_putian else 0,
price_sanming=round(price_sanming or 0, 2), price_sanming=price_sanming.quantize(q_ext, decimal.ROUND_HALF_UP) if price_sanming else 0,
price_quanzhou=round(price_quanzhou or 0, 2), price_quanzhou=price_quanzhou.quantize(q_ext, decimal.ROUND_HALF_UP) if price_quanzhou else 0,
price_zhangzhou=round(price_zhangzhou or 0, 2), price_zhangzhou=price_zhangzhou.quantize(q_ext, decimal.ROUND_HALF_UP) if price_zhangzhou else 0,
price_nanpin=round(price_nanpin or 0, 2), price_nanpin=price_nanpin.quantize(q_ext, decimal.ROUND_HALF_UP) if price_nanpin else 0,
price_longyan=round(price_longyan or 0, 2), price_longyan=price_longyan.quantize(q_ext, decimal.ROUND_HALF_UP) if price_longyan else 0,
price_ningde=round(price_ningde or 0, 2), price_ningde=price_ningde.quantize(q_ext, decimal.ROUND_HALF_UP) if price_ningde else 0,
price_pintan=round(price_pintan or 0, 2), price_pintan=price_pintan.quantize(q_ext, decimal.ROUND_HALF_UP) if price_pintan else 0,
price_zhangzhoukfq=round(price_zhangzhoukfq or 0, 2), price_zhangzhoukfq=price_zhangzhoukfq.quantize(q_ext, decimal.ROUND_HALF_UP) if price_zhangzhoukfq else 0,
tax=self.tax_map.get(material_id, tax), # 从材料表获取税率 tax=self.tax_map.get(material_id, 0), # 从材料表获取税率
type=2, type=2,
unit=unit, unit=self.unit_map.get(material_id, ''),
display_digit=display_digit, display_digit=display_digit,
).upsert() ).upsert()
@@ -120,9 +121,11 @@ class Collector:
def run(self): def run(self):
# 当月价 # 当月价
# self.get_from_survey() if not self.only_avg:
self.get_from_result() PricePublish.clean(self.year, self.month, type=1) # 清空当月价
self.get_from_result()
# 近半年平均价 # 近半年平均价
PricePublish.clean(self.year, self.month, type=2) # 清空近半年平均价
self.get_avg() self.get_avg()

View File

@@ -6,6 +6,12 @@ class BaseModelMixin:
def upsert(self): def upsert(self):
result = self.find_by_key() result = self.find_by_key()
session = self._db.session session = self._db.session
# # 住建厅数据初始化
# if result:
# result.price_fujian = self.price_fujian
# result.fluctuating_fujian = self.fluctuating_fujian
# session.add(result)
# session.commit()
if result: if result:
session.delete(result) session.delete(result)
session.flush() session.flush()

View File

@@ -1,7 +1,8 @@
import datetime import datetime
from operator import and_
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from sqlalchemy import Column, Integer, String, Numeric from sqlalchemy import Column, Integer, String, Numeric, or_
from commons.models.mixin.base import BaseModelMixin from commons.models.mixin.base import BaseModelMixin
from commons.models.mixin.operation_track import OperationTrackMixin from commons.models.mixin.operation_track import OperationTrackMixin
@@ -40,8 +41,11 @@ class PricePublish(db.Model, Model, OperationTrackMixin, BaseModelMixin):
) )
@classmethod @classmethod
def clean(cls, year, month): def clean(cls, year, month, type=None):
cls.query.filter(cls.year == year, cls.month == month).delete() query = cls.query.filter(cls.year == year, cls.month == month)
if type:
query.filter(cls.type == type)
query.delete()
@classmethod @classmethod
def get_by_key(cls, name, year, month, type): def get_by_key(cls, name, year, month, type):
@@ -71,10 +75,10 @@ class PricePublish(db.Model, Model, OperationTrackMixin, BaseModelMixin):
@classmethod @classmethod
def query_previous_month(cls, query, start_date: datetime.date, count=6): def query_previous_month(cls, query, start_date: datetime.date, count=6):
condition = False condition = False
for i in range(count): for i in range(0, count):
date = start_date - relativedelta(months=i) date = start_date - relativedelta(months=i)
condition = condition or (cls.year == date.year and cls.month == date.month) condition = or_(condition, and_(cls.year == str(date.year), cls.month == str(date.month)))
query.filter(condition) query = query.filter(condition)
return query return query
@classmethod @classmethod

View File

@@ -18,7 +18,7 @@ def calculate(year=2023, month=8):
calculator.material_id = material.id calculator.material_id = material.id
calculator.unit = material.unit calculator.unit = material.unit
calculator.spec = material.spec calculator.spec = material.spec
# 设置小数位数,如果数据库中未设置则使用默认值0 # 设置小数位数,如果数据库中未设置则使用默认值0
calculator.round_bit = material.round_bit if material.round_bit is not None else 0 calculator.round_bit = material.round_bit if material.round_bit is not None else 0
@@ -39,11 +39,11 @@ if __name__ == '__main__':
from core.factory import ClientApp from core.factory import ClientApp
with ClientApp().app_context(): with ClientApp().app_context():
calculate(2024, 4) calculate(2025, 5)
# for i in range(2, 12): # for i in range(2, 12):
# calculate(2022, i+1) # calculate(2022, i+1)
# for i in range(0, 12): # for i in range(1, 5+1):
# calculate(2023, i+1) # calculate(2025, i)
# for i in range(6-1, 10): # for i in range(6-1, 10):
# calculate(2024, i+1) # calculate(2024, i+1)

View File

@@ -2,12 +2,11 @@ from collectors import Collector
from commons.models.price_publish import PricePublish from commons.models.price_publish import PricePublish
def collect(year=2023, month=11): def collect(year=2023, month=11, only_avg=0):
""" """
整理发布价格 整理发布价格
""" """
PricePublish.clean(year, month) # 清空当月数据 collector = Collector(year, month, only_avg=only_avg)
collector = Collector(year, month)
collector.run() collector.run()
@@ -15,11 +14,11 @@ if __name__ == '__main__':
from core.factory import ClientApp from core.factory import ClientApp
with ClientApp().app_context(): with ClientApp().app_context():
collect(2023, 3) collect(2025, 5)
# for i in range(2, 12): # for i in range(4):
# collect(2022, i + 1) # collect(2025, i + 1)
# for i in range(0, 12): # for i in range(9,12):
# collect(2023, i + 1) # collect(2023, i + 1)
# for i in range(5, 10): # for i in range(12):
# collect(2024, i + 1) # collect(2024, i + 1)